hl2_src-leak-2017/src/utils/classcheck/processmodule.cpp

1866 lines
39 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <assert.h>
#include <time.h>
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include "classcheck_util.h"
#include "codeprocessor.h"
/*
================
UTIL_FloatTime
================
*/
double UTIL_FloatTime (void)
{
// more precise, less portable
clock_t current;
static clock_t base;
static bool first = true;
current = clock();
if ( first )
{
first = false;
base = current;
}
return (double)(current - base)/(double)CLOCKS_PER_SEC;
}
CClass *CCodeProcessor::FindClass( const char *name ) const
{
CClass *cl = m_pClassList;
while ( cl )
{
if ( !stricmp( cl->m_szName, name ) )
return cl;
cl = cl->m_pNext;
}
return NULL;
}
void ClearMissingTypes();
void CCodeProcessor::Clear( void )
{
ClearMissingTypes();
CClass *cl = m_pClassList, *next;
while ( cl )
{
next = cl->m_pNext;
delete cl;
cl = next;
}
m_pClassList = NULL;
}
int CCodeProcessor::Count( void ) const
{
int c = 0;
CClass *cl = m_pClassList;
while ( cl )
{
c++;
cl = cl->m_pNext;
}
return c;
}
int FnClassSortCompare( const void *elem1, const void *elem2 )
{
CClass *c1 = *(CClass **)elem1;
CClass *c2 = *(CClass **)elem2;
return ( stricmp( c1->m_szName, c2->m_szName ) );
}
void CCodeProcessor::SortClassList( void )
{
int n = Count();
if ( n <= 1 )
return;
CClass **ppList = new CClass *[ n ];
if ( ppList )
{
CClass *cl;
int i;
for ( i = 0, cl = m_pClassList; i < n; i++, cl = cl->m_pNext )
{
ppList[ i ] = cl;
}
qsort( ppList, n, sizeof( CClass * ), FnClassSortCompare );
for ( i = 0; i < n - 1; i++ )
{
ppList[ i ]->m_pNext = ppList[ i + 1 ];
}
ppList[ i ]->m_pNext = NULL;
m_pClassList = ppList[ 0 ];
}
delete[] ppList;
}
void CCodeProcessor::ResolveBaseClasses( const char *baseentityclass )
{
SortClassList();
CClass *cl = m_pClassList;
while ( cl )
{
if ( cl->m_szBaseClass[0] )
{
cl->m_pBaseClass = FindClass( cl->m_szBaseClass );
if ( !cl->m_pBaseClass )
{
//vprint( 0, "couldn't find base class %s for %s\n", cl->m_szBaseClass, cl->m_szName );
}
}
cl = cl->m_pNext;
}
cl = m_pClassList;
while ( cl )
{
cl->CheckChildOfBaseEntity( baseentityclass );
cl = cl->m_pNext;
}
}
void CCodeProcessor::PrintMissingTDFields( void ) const
{
int classcount;
int fieldcount;
int c;
CClass *cl;
if ( GetPrintTDs() )
{
classcount = 0;
fieldcount = 0;
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity || cl->m_bHasSaveRestoreData )
{
if ( cl->CheckForMissingTypeDescriptionFields( c ) )
{
classcount++;
fieldcount += c;
}
}
cl = cl->m_pNext;
}
if ( fieldcount )
{
vprint( 0, "\nSummary: %i fields missing from %i classes\n", fieldcount, classcount );
}
else
{
if ( !classcount )
{
vprint( 0, "\nSummary: no saverestore info present\n");
}
else
{
vprint( 0, "\nSummary: no errors for %i classes\n", classcount );
}
}
vprint( 0, "\n" );
}
if ( GetPrintPredTDs() )
{
//Now check prediction stuff
classcount = 0;
fieldcount = 0;
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity || cl->m_bHasPredictionData )
{
if ( cl->CheckForMissingPredictionFields( c, false ) )
{
classcount++;
fieldcount += c;
}
}
cl = cl->m_pNext;
}
if ( fieldcount )
{
vprint( 0, "\nSummary: %i prediction fields missing from %i classes\n", fieldcount, classcount );
}
else
{
if ( !classcount )
{
vprint( 0, "\nSummary: no prediction info present\n");
}
else
{
vprint( 0, "\nSummary: no errors for %i predictable classes\n", classcount );
}
}
vprint( 0, "\n" );
}
if ( GetPrintCreateMissingTDs() )
{
//Now check prediction stuff
classcount = 0;
fieldcount = 0;
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity || cl->m_bHasSaveRestoreData )
{
if ( cl->CheckForMissingTypeDescriptionFields( c, true ) )
{
classcount++;
fieldcount += c;
}
}
cl = cl->m_pNext;
}
if ( fieldcount )
{
vprint( 0, "\nSummary: %i saverestore fields missing from %i classes\n", fieldcount, classcount );
}
else
{
if ( !classcount )
{
vprint( 0, "\nSummary: no saverestore info present\n");
}
else
{
vprint( 0, "\nSummary: no errors for %i classes\n", classcount );
}
}
vprint( 0, "\n" );
}
if ( GetPrintCreateMissingPredTDs() )
{
//Now check prediction stuff
classcount = 0;
fieldcount = 0;
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity || cl->m_bHasPredictionData )
{
if ( cl->CheckForMissingPredictionFields( c, true ) )
{
classcount++;
fieldcount += c;
}
}
cl = cl->m_pNext;
}
if ( fieldcount )
{
vprint( 0, "\nSummary: %i prediction fields missing from %i classes\n", fieldcount, classcount );
}
else
{
if ( !classcount )
{
vprint( 0, "\nSummary: no prediction info present\n");
}
else
{
vprint( 0, "\nSummary: no errors for %i predictable classes\n", classcount );
}
}
vprint( 0, "\n" );
}
// Now check for things that are in the prediction TD but not marked correctly as being part of the sendtable
{
//Now check prediction stuff
classcount = 0;
fieldcount = 0;
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity || cl->m_bHasPredictionData )
{
if ( cl->CheckForPredictionFieldsInRecvTableNotMarkedAsSuchCorrectly( c ) )
{
classcount++;
fieldcount += c;
}
}
cl = cl->m_pNext;
}
vprint( 0, "\n" );
}
// Print stuff derived from CBaseEntity that doesn't have save/restore data
vprint( 0, "\nMissing DATADESC tables:\n\n" );
cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity && !cl->m_bHasSaveRestoreData && cl->m_nVarCount )
{
vprint( 0, "\t%s\n", cl->m_szName );
}
cl = cl->m_pNext;
}
vprint( 0, "\n" );
}
void CCodeProcessor::ReportHungarianNotationErrors()
{
if ( !GetCheckHungarian() )
return;
vprint( 0, "\tChecking for hungarian notation issues\n" );
CClass *cl = m_pClassList;
int classcount = 0;
int warningcount = 0;
while ( cl )
{
int c = 0;
cl->CheckForHungarianErrors( c );
classcount++;
warningcount += c;
cl = cl->m_pNext;
}
vprint( 0, "\tFound %i notation errors across %i classes\n", classcount, warningcount );
}
void CCodeProcessor::PrintClassList( void ) const
{
if ( GetPrintHierarchy() )
{
vprint( 0, "\nClass Summary\n\n" );
}
CClass *cl = m_pClassList;
while ( cl )
{
if ( cl->m_bDerivedFromCBaseEntity )
{
bool missing = false;
char missingwarning[ 128 ];
missingwarning[0]=0;
if ( cl->m_szTypedefBaseClass[0] )
{
if ( stricmp( cl->m_szBaseClass, cl->m_szTypedefBaseClass ) )
{
vprint( 0, "class %s has incorrect typedef %s BaseClass\n", cl->m_szName, cl->m_szTypedefBaseClass );
}
}
else if ( cl->m_szBaseClass[ 0 ] )
{
missing = true;
sprintf( missingwarning, ", missing typedef %s BaseClass", cl->m_szBaseClass );
}
if ( GetPrintHierarchy() || missing )
{
vprint( 0, "class %s%s\n", cl->m_szName, missing ? missingwarning : "" );
}
int level = 1;
CClass *base = cl->m_pBaseClass;
while ( base )
{
if ( GetPrintHierarchy() )
{
vprint( level++, "public %s\n", base->m_szName );
}
base = base->m_pBaseClass;
}
int i;
if ( GetPrintHierarchy() && GetPrintMembers() )
{
if ( cl->m_nMemberCount )
{
vprint( 1, "\nMember functions:\n\n" );
}
for ( i = 0; i < cl->m_nMemberCount; i++ )
{
CClassMemberFunction *member = cl->m_Members[ i ];
if ( member->m_szType[0] )
{
vprint( 1, "%s %s();\n", member->m_szType, member->m_szName );
}
else
{
char *p = member->m_szName;
if ( *p == '~' )
p++;
if ( stricmp( p, cl->m_szName ) )
{
vprint( 0, "class %s has member function %s with no return type!!!\n",
cl->m_szName, member->m_szName );
}
vprint( 1, "%s();\n", member->m_szName );
}
}
if ( cl->m_nVarCount )
{
vprint( 1, "\nMember Variables\n\n" );
}
for ( i = 0; i < cl->m_nVarCount; i++ )
{
CClassVariable *var = cl->m_Variables[ i ];
if ( var->m_bIsArray )
{
if ( var->m_szArraySize[0]==0 )
{
vprint( 1, "%s %s[];\n", var->m_szType, var->m_szName );
}
else
{
vprint( 1, "%s %s[ %s ];\n", var->m_szType, var->m_szName, var->m_szArraySize );
}
}
else
{
vprint( 1, "%s %s;\n", var->m_szType, var->m_szName );
}
}
if ( cl->m_nTDCount )
{
vprint( 1, "\nSave/Restore TYPEDESCRIPTION\n\n" );
}
for ( i = 0; i < cl->m_nTDCount; i++ )
{
CTypeDescriptionField *td = cl->m_TDFields[ i ];
if ( td->m_bCommentedOut )
{
vprint( 1, "// " );
}
else
{
vprint( 1, "" );
}
vprint( 0, "%s( %s, %s, %s, ... )\n", td->m_szDefineType, cl->m_szName, td->m_szVariableName, td->m_szType );
}
if ( !cl->m_bHasSaveRestoreData )
{
// vprint( 1, "\nSave/Restore TYPEDESCRIPTION not specified for class\n\n" );
}
if ( cl->m_nPredTDCount )
{
vprint( 1, "\nPrediction TYPEDESCRIPTION\n\n" );
}
for ( i = 0; i < cl->m_nPredTDCount; i++ )
{
CTypeDescriptionField *td = cl->m_PredTDFields[ i ];
if ( td->m_bCommentedOut )
{
vprint( 1, "// " );
}
else
{
vprint( 1, "" );
}
vprint( 0, "%s( %s, %s, %s, ... )\n", td->m_szDefineType, cl->m_szName, td->m_szVariableName, td->m_szType );
}
if ( !cl->m_bHasPredictionData )
{
// vprint( 1, "\nPrediction TYPEDESCRIPTION not specified for class\n\n" );
}
}
if ( GetPrintHierarchy() )
{
vprint( 0, "\n" );
}
}
cl = cl->m_pNext;
}
}
CClass *CCodeProcessor::AddClass( const char *classname )
{
CClass *cl = FindClass( classname );
if ( !cl )
{
cl = new CClass( classname );
m_nClassesParsed++;
cl->m_pNext = m_pClassList;
m_pClassList = cl;
}
return cl;
}
char *CCodeProcessor::ParseTypeDescription( char *current, bool fIsMacroized )
{
// Next token is classname then :: then variablename then braces then = then {
char classname[ 256 ];
char variablename[ 256 ];
if ( !fIsMacroized )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
return current;
strcpy( classname, com_token );
if ( classname[0]=='*' )
return current;
current = CC_ParseToken( current );
if (stricmp( com_token, ":" ) )
{
return current;
}
current = CC_ParseToken( current );
Assert( !stricmp( com_token, ":" ) );
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
return current;
strcpy( variablename, com_token );
}
else
{
current = CC_ParseToken( current );
if (stricmp( com_token, "(" ) )
{
return current;
}
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
return current;
strcpy( classname, com_token );
if ( classname[0]=='*' )
return current;
current = CC_ParseToken( current );
if (stricmp( com_token, ")" ) )
{
return current;
}
// It's macro-ized
strcpy( variablename, "m_DataDesc" );
}
if ( !fIsMacroized )
{
char ch;
current = CC_RawParseChar( current, "{", &ch );
Assert( ch == '{' );
if ( strlen( com_token ) <= 0 )
return current;
}
com_ignoreinlinecomment = true;
bool insidecomment = false;
// Now parse typedescription line by line
while ( 1 )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
break;
// Go to next line
if ( !stricmp( com_token, "," ) )
continue;
// end
if ( !fIsMacroized )
{
if ( !stricmp( com_token, "}" ) )
break;
}
else
{
if ( !stricmp( com_token, "END_DATADESC" ) ||
!stricmp( com_token, "END_BYTESWAP_DATADESC" ) )
break;
}
// skip #ifdef's inside of typedescs
if ( com_token[0]=='#' )
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
if ( !stricmp( com_token, "/" ) )
{
current = CC_ParseToken( current );
if ( !stricmp( com_token, "/" ) )
{
// There are two styles supported. One is to have the member definition present but commented out:
// DEFINE_FIELD( m_member, FIELD_INTEGER ),
// the other is to have a comment where the first token of the comment is a member name:
// m_member
current = CC_ParseToken( current );
if ( !strnicmp( com_token, "DEFINE_", 7 ) )
{
CC_UngetToken();
insidecomment = true;
}
else
{
char commentedvarname[ 256 ];
strcpy( commentedvarname, com_token );
CClass *cl = FindClass( classname );
if ( cl )
{
if ( !cl->FindTD( commentedvarname ) )
{
cl->AddTD( commentedvarname, "", "", true );
}
// Mark that it has a data table
cl->m_bHasSaveRestoreData = true;
}
current = CC_ParseUntilEndOfLine( current );
}
continue;
}
}
com_ignoreinlinecomment = false;
// Parse a typedescription line
char definetype[ 256 ];
strcpy( definetype, com_token );
current = CC_ParseToken( current );
if ( stricmp( com_token, "(" ) )
break;
char varname[ 256 ];
current = CC_ParseToken( current );
strcpy( varname, com_token );
char vartype[ 256 ];
vartype[0]=0;
if ( !stricmp( definetype, "DEFINE_FUNCTION" ) ||
!stricmp( definetype, "DEFINE_THINKFUNC" ) ||
!stricmp( definetype, "DEFINE_ENTITYFUNC" ) ||
!stricmp( definetype, "DEFINE_USEFUNC" ) ||
!stricmp( definetype, "DEFINE_OUTPUT" ) ||
!stricmp( definetype, "DEFINE_INPUTFUNC" ) )
{
strcpy( vartype, "funcptr" );
}
else if ( !stricmp(definetype, "DEFINE_FIELD") ||
!stricmp(definetype, "DEFINE_INDEX") ||
!stricmp(definetype, "DEFINE_KEYFIELD") ||
!stricmp(definetype, "DEFINE_KEYFIELD_NOT_SAVED") ||
!stricmp(definetype, "DEFINE_UTLVECTOR") ||
!stricmp(definetype, "DEFINE_GLOBAL_FIELD") ||
!stricmp(definetype, "DEFINE_GLOBAL_KEYFIELD") ||
!stricmp(definetype, "DEFINE_CUSTOM_FIELD") ||
!stricmp(definetype, "DEFINE_INPUT") ||
!stricmp(definetype, "DEFINE_AUTO_ARRAY") ||
!stricmp(definetype, "DEFINE_AUTO_ARRAY_KEYFIELD") ||
!stricmp(definetype, "DEFINE_AUTO_ARRAY2D") ||
!stricmp(definetype, "DEFINE_ARRAY") )
{
// skip comma
current = CC_ParseToken( current );
if (!strcmp( com_token, "[" ))
{
// Read array...
current = CC_ParseToken( current );
strcat( varname, "[" );
strcat( varname, com_token );
current = CC_ParseToken( current );
// eat everything until the next "]"
while (strcmp( com_token, "]") != 0)
{
strcat( varname, com_token );
current = CC_ParseToken( current );
}
if ( strcmp( com_token, "]" ))
{
current = current;
}
strcat( varname, "]" );
// skip comma
current = CC_ParseToken( current );
}
current = CC_ParseToken( current );
strcpy( vartype, com_token );
}
// Jump to end of definition
int nParenCount = 1;
do
{
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
break;
if ( !stricmp( com_token, "(" ) )
{
++nParenCount;
}
else if ( !stricmp( com_token, ")" ) )
{
if ( --nParenCount == 0 )
{
break;
}
}
} while ( 1 );
// vprint( 2, "%s%s::%s %s %s %s\n",
// insidecomment ? "// " : "",
// classname, variablename,
// definetype, varname, vartype );
CClass *cl = FindClass( classname );
if ( cl )
{
if ( strcmp( vartype, "funcptr" ) && cl->FindTD( varname ) )
{
vprint( 0, "class %s::%s already has typedescription entry for field %s\n", classname, variablename, varname );
}
else
{
cl->AddTD( varname, vartype, definetype, insidecomment );
}
// Mark that it has a data table
cl->m_bHasSaveRestoreData = true;
}
insidecomment = false;
com_ignoreinlinecomment = true;
}
com_ignoreinlinecomment = false;
return current;
}
char *CCodeProcessor::ParseReceiveTable( char *current )
{
// Next token is open paren, then classname close paren, then {
char classname[ 256 ];
current = CC_ParseToken( current );
if (stricmp( com_token, "(" ) )
{
return current;
}
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
return current;
strcpy( classname, com_token );
if ( classname[0]=='*' )
return current;
if ( !strcmp( classname, "className" ) )
return current;
if ( !strcmp( classname, "clientClassName" ) )
return current;
CClass *cl = FindClass( classname );
if ( cl )
{
cl->m_bHasRecvTableData = true;
}
CClass *leafClass = cl;
// parse until end of line
current = CC_ParseUntilEndOfLine( current );
// Now parse recvtable entries line by line
while ( 1 )
{
cl = leafClass;
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
break;
// Go to next line
if ( !stricmp( com_token, "," ) )
continue;
// end
if ( !stricmp( com_token, "END_RECV_TABLE" ) )
break;
// skip #ifdef's inside of recv tables
if ( com_token[0]=='#' )
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
// Parse recproxy line
char recvproptype[ 256 ];
strcpy( recvproptype, com_token );
if ( strnicmp( recvproptype, "RecvProp", strlen( "RecvProp" ) ) )
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
if ( !strcmp( recvproptype, "RecvPropArray" ) )
{
current = CC_ParseToken( current );
if ( stricmp( com_token, "(" ) )
break;
current = CC_ParseToken( current );
if ( strnicmp( recvproptype, "RecvProp", strlen( "RecvProp" ) ) )
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
}
current = CC_ParseToken( current );
if ( stricmp( com_token, "(" ) )
break;
// Read macro or fieldname
current = CC_ParseToken( current );
char varname[ 256 ];
if ( !strnicmp( com_token, "RECVINFO", strlen( "RECVINFO" ) ) )
{
current = CC_ParseToken( current );
if ( stricmp( com_token, "(" ) )
break;
current = CC_ParseToken( current );
}
else
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
strcpy( varname, com_token );
current = CC_ParseUntilEndOfLine( current );
if ( cl )
{
// Look up the var
CClassVariable *classVar = cl->FindVar( varname, true );
if ( classVar )
{
classVar->m_bInRecvTable = true;
}
else
{
char cropped[ 256 ];
char root[ 256 ];
strcpy( cropped, varname );
while ( 1 )
{
// See if varname is an embedded var
char *spot = strstr( cropped, "." );
if ( spot )
{
strcpy( root, cropped );
root[ spot - cropped ] = 0;
strcpy( cropped, spot + 1 );
classVar = cl->FindVar( root, true );
}
else
{
classVar = cl->FindVar( cropped, true );
break;
}
if ( classVar )
break;
}
if ( !classVar )
{
vprint( 0, "class %s::%s missing, but referenced by RecvTable!!!\n", classname, varname );
}
else
{
classVar->m_bInRecvTable = true;
}
}
}
else
{
vprint( 0, "class %s::%s found in RecvTable, but no such class is known!!!\n", classname, varname );
}
}
return current;
}
char *CCodeProcessor::ParsePredictionTypeDescription( char *current )
{
// Next token is open paren, then classname close paren, then {
char classname[ 256 ];
char variablename[ 256 ];
current = CC_ParseToken( current );
if (stricmp( com_token, "(" ) )
{
return current;
}
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
return current;
strcpy( classname, com_token );
if ( classname[0]=='*' )
return current;
CClass *cl = FindClass( classname );
if ( cl )
{
cl->m_bHasPredictionData = true;
}
current = CC_ParseToken( current );
if (stricmp( com_token, ")" ) )
{
return current;
}
// It's macro-ized
strcpy( variablename, "m_PredDesc" );
com_ignoreinlinecomment = true;
bool insidecomment = false;
// Now parse typedescription line by line
while ( 1 )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
break;
// Go to next line
if ( !stricmp( com_token, "," ) )
continue;
// end
if ( !stricmp( com_token, "END_PREDICTION_DATA" ) )
break;
// skip #ifdef's inside of typedescs
if ( com_token[0]=='#' )
{
current = CC_ParseUntilEndOfLine( current );
continue;
}
if ( !stricmp( com_token, "/" ) )
{
current = CC_ParseToken( current );
if ( !stricmp( com_token, "/" ) )
{
current = CC_ParseToken( current );
if ( !strnicmp( com_token, "DEFINE_", 7 ) )
{
CC_UngetToken();
insidecomment = true;
}
else
{
current = CC_ParseUntilEndOfLine( current );
}
continue;
}
}
com_ignoreinlinecomment = false;
// Parse a typedescription line
char definetype[ 256 ];
strcpy( definetype, com_token );
current = CC_ParseToken( current );
if ( stricmp( com_token, "(" ) )
break;
char varname[ 256 ];
current = CC_ParseToken( current );
strcpy( varname, com_token );
char vartype[ 256 ];
vartype[0]=0;
if ( stricmp( definetype, "DEFINE_FUNCTION" ) )
{
// skip comma
current = CC_ParseToken( current );
current = CC_ParseToken( current );
strcpy( vartype, com_token );
}
else
{
strcpy( vartype, "funcptr" );
}
bool inrecvtable = false;
// Jump to end of definition
int nParenCount = 1;
do
{
current = CC_ParseToken( current );
if ( strlen( com_token ) <= 0 )
break;
if ( !stricmp( com_token, "(" ) )
{
++nParenCount;
}
else if ( !stricmp( com_token, ")" ) )
{
if ( --nParenCount == 0 )
{
break;
}
}
if ( !stricmp( com_token, "FTYPEDESC_INSENDTABLE" ) )
{
inrecvtable = true;
}
} while ( 1 );
/*
vprint( 2, "%s%s::%s %s %s %s\n",
insidecomment ? "// " : "",
classname, variablename,
definetype, varname, vartype );
*/
if ( cl )
{
if ( cl->FindPredTD( varname ) )
{
vprint( 0, "class %s::%s already has prediction typedescription entry for field %s\n", classname, variablename, varname );
}
else
{
cl->AddPredTD( varname, vartype, definetype, insidecomment, inrecvtable );
}
}
insidecomment = false;
com_ignoreinlinecomment = true;
}
com_ignoreinlinecomment = false;
return current;
}
void CCodeProcessor::AddHeader( int depth, const char *filename, const char *rootmodule )
{
// if ( depth < 1 )
// return;
if ( depth != 1 )
return;
// Check header list
int idx = m_Headers.Find( filename );
if ( idx != m_Headers.InvalidIndex() )
{
vprint( 0, "%s included twice in module %s\n", filename, rootmodule );
return;
}
CODE_MODULE module;
module.skipped = false;
m_Headers.Insert( filename, module );
}
bool CCodeProcessor::CheckShouldSkip( bool forcequiet, int depth, char const *filename, int& numheaders, int& skippedfiles)
{
int idx = m_Modules.Find( filename );
if ( idx == m_Modules.InvalidIndex() )
return false;
CODE_MODULE *module = &m_Modules[ idx ];
if ( forcequiet )
{
m_nHeadersProcessed++;
numheaders++;
if ( module->skipped )
{
skippedfiles++;
}
}
AddHeader( depth, filename, m_szCurrentCPP );
return true;
}
bool CCodeProcessor::LoadFile( char **buffer, char *filename, char const *module, bool forcequiet,
int depth, int& filelength, int& numheaders, int& skippedfiles,
char const *srcroot, char const *root, char const *baseroot )
{
for ( int i = 0; i < m_IncludePath.Count(); ++i )
{
// Load the base module
sprintf( filename, "%s\\%s", m_IncludePath[i], module );
strlwr( filename );
if ( CheckShouldSkip( forcequiet, depth, filename, numheaders, skippedfiles ) )
{
return false;
}
*buffer = (char *)COM_LoadFile( filename, &filelength );
if ( *buffer )
return true;
}
return false;
}
static bool SkipFile( char const *module )
{
if ( !stricmp( module, "predictable_entity.h" ) )
return true;
if ( !stricmp( module, "baseentity_shared.h" ) )
return true;
if ( !stricmp( module, "baseplayer_shared.h" ) )
return true;
if ( !stricmp( module, "tf_tacticalmap.cpp" ) )
return true;
if ( !stricmp( module, "techtree.cpp" ) )
return true;
if ( !stricmp( module, "techtree_parse.cpp" ) )
return true;
return false;
}
void CCodeProcessor::ProcessModule( bool forcequiet, int depth, int& maxdepth, int& numheaders, int& skippedfiles,
const char *srcroot, const char *baseroot, const char *root, const char *module )
{
char filename[ 256 ];
if ( depth > maxdepth )
{
maxdepth = depth;
}
int filelength;
char *buffer = NULL;
// Always skip these particular modules/headers
if ( SkipFile( module ) )
{
CODE_MODULE module;
module.skipped = true;
m_Modules.Insert( filename, module );
skippedfiles++;
return;
}
if ( !LoadFile( &buffer, filename, module, forcequiet, depth, filelength, numheaders, skippedfiles,
srcroot, root, baseroot ) )
{
CODE_MODULE module;
module.skipped = true;
m_Modules.Insert( filename, module );
skippedfiles++;
return;
}
Assert( buffer );
m_nBytesProcessed += filelength;
CODE_MODULE m;
m.skipped = false;
m_Modules.Insert( filename, m );
if ( !forcequiet )
{
strcpy( m_szCurrentCPP, filename );
}
AddHeader( depth, filename, m_szCurrentCPP );
bool onclient = !strnicmp( m_szBaseEntityClass, "C_", 2 ) ? true : false;
// Parse tokens looking for #include directives or class starts
char *current = buffer;
current = CC_ParseToken( current );
while ( 1 )
{
// No more tokens
if ( !current )
break;
if ( !stricmp( com_token, "#include" ) )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 &&
com_token[ 0 ] != '<' )
{
//vprint( "#include %s\n", com_token );
m_nHeadersProcessed++;
numheaders++;
ProcessModule( true, depth + 1, maxdepth, numheaders, skippedfiles, srcroot, baseroot, root, com_token );
}
}
else if ( !stricmp( com_token, "class" ) ||
!stricmp( com_token, "struct" ) )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 )
{
//vprint( depth, "class %s\n", com_token );
CClass *cl = AddClass( com_token );
// Now see if there's a base class
current = CC_ParseToken( current );
if ( !stricmp( com_token, ":" ) )
{
// Parse out public and then classname an
current = CC_ParseToken( current );
if ( !stricmp( com_token, "public" ) )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 )
{
cl->SetBaseClass( com_token );
do
{
current = CC_ParseToken( current );
} while ( strlen( com_token ) && stricmp( com_token, "{" ) );
if ( !stricmp( com_token, "{" ) )
{
current = cl->ParseClassDeclaration( current );
}
}
}
}
else if ( !stricmp( com_token, "{" ) )
{
current = cl->ParseClassDeclaration( current );
}
}
}
else if ( !strnicmp( com_token, "PREDICTABLE_CLASS", strlen( "PREDICTABLE_CLASS" ) ) )
{
char prefix[ 32 ];
prefix[ 0 ] = 0;
int type = 0;
int bases = 1;
int usebase = 0;
if ( !stricmp( com_token, "PREDICTABLE_CLASS_ALIASED" ) )
{
type = 2;
bases = 2;
if ( onclient )
{
strcpy( prefix, "C_" );
}
else
{
strcpy( prefix, "C" );
usebase = 1;
}
}
else if ( !stricmp( com_token, "PREDICTABLE_CLASS_SHARED" ) )
{
type = 1;
bases = 1;
}
else if ( !stricmp( com_token, "PREDICTABLE_CLASS" ) )
{
type = 0;
bases = 1;
if ( onclient )
{
strcpy( prefix, "C_" );
}
else
{
strcpy( prefix, "C" );
}
}
else if ( !stricmp( com_token, "PREDICTABLE_CLASS_ALIASED_PREFIXED" ) )
{
// Nothing
}
else
{
vprint( 0, "PREDICTABLE_CLASS of unknown type!!! %s\n", com_token );
}
// parse the (
current = CC_ParseToken( current );
if ( !strcmp( com_token, "(" ) )
{
// Now the classname
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 )
{
//vprint( depth, "class %s\n", com_token );
CClass *cl = AddClass( com_token );
// Now see if there's a base class
current = CC_ParseToken( current );
if ( !stricmp( com_token, "," ) )
{
// Parse out public and then classname an
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 )
{
char basename[ 256 ];
sprintf( basename, "%s%s", prefix, com_token );
bool valid = true;
if ( bases == 2 )
{
valid = false;
current = CC_ParseToken( current );
if ( !stricmp( com_token, "," ) )
{
current = CC_ParseToken( current );
if ( strlen( com_token ) > 0 )
{
valid = true;
if ( usebase == 1 )
{
sprintf( basename, "%s%s", prefix, com_token );
}
}
}
}
if ( valid )
{
cl->SetBaseClass( basename );
strcpy( cl->m_szTypedefBaseClass, basename );
}
do
{
current = CC_ParseToken( current );
} while ( strlen( com_token ) && stricmp( com_token, ")" ) );
if ( !stricmp( com_token, ")" ) )
{
current = cl->ParseClassDeclaration( current );
}
}
}
else if ( !stricmp( com_token, ")" ) )
{
current = cl->ParseClassDeclaration( current );
}
}
}
}
else if ( !strcmp( com_token, "TYPEDESCRIPTION" ) ||
!strcmp( com_token, "typedescription_t" ) )
{
current = ParseTypeDescription( current, false );
}
else if ( !strcmp( com_token, "BEGIN_DATADESC" ) ||
!strcmp( com_token, "BEGIN_DATADESC_NO_BASE" ) ||
!strcmp( com_token, "BEGIN_SIMPLE_DATADESC" ) ||
!strcmp( com_token, "BEGIN_BYTESWAP_DATADESC" ) )
{
current = ParseTypeDescription( current, true );
}
else if ( !strcmp( com_token, "BEGIN_PREDICTION_DATA" ) ||
!strcmp( com_token, "BEGIN_EMBEDDED_PREDDESC" ) )
{
current = ParsePredictionTypeDescription( current );
}
else if ( !strcmp( com_token, "BEGIN_RECV_TABLE" ) ||
!strcmp( com_token, "BEGIN_RECV_TABLE_NOBASE" ) ||
!strcmp( com_token, "IMPLEMENT_CLIENTCLASS_DT" ) ||
!strcmp( com_token, "IMPLEMENT_CLIENTCLASS_DT_NOBASE" ) )
{
current = ParseReceiveTable( current );
}
else if ( !strcmp( com_token, "IMPLEMENT_PREDICTABLE_NODATA" ) )
{
current = CC_ParseToken( current );
if ( !strcmp( com_token, "(" ) )
{
current = CC_ParseToken( current );
CClass *cl = FindClass( com_token );
if ( cl )
{
if ( cl->m_bHasPredictionData )
{
if ( !forcequiet )
{
vprint( 0, "Class %s declared predictable and implemented with IMPLEMENT_PREDICTABLE_NODATA in typedescription\n",
cl->m_szName );
}
cl->m_bHasPredictionData = false;
}
}
current = CC_ParseToken( current );
}
}
current = CC_ParseToken( current );
}
COM_FreeFile( (unsigned char *)buffer );
if ( !forcequiet && !GetQuiet() )
{
vprint( 0, " %s: headers (%i game / %i total)", (char *)&filename[ m_nOffset ], numheaders - skippedfiles, numheaders );
if ( maxdepth > 1 )
{
vprint( 0, ", depth %i", maxdepth );
}
vprint( 0, "\n" );
}
m_nLinesOfCode += linesprocessed;
linesprocessed = 0;
}
void CCodeProcessor::ProcessModules( const char *srcroot, const char *root, const char *rootmodule )
{
m_nFilesProcessed++;
// Reset header list per module
m_Headers.RemoveAll();
int numheaders = 0;
int maxdepth = 0;
int skippedfiles = 0;
ProcessModule( false, 0, maxdepth, numheaders, skippedfiles, srcroot, root, root, rootmodule );
}
void ReportMissingTypes();
void CCodeProcessor::PrintResults( const char *baseentityclass )
{
vprint( 0, "\nChecking for errors and totaling...\n\n" );
ResolveBaseClasses( baseentityclass );
PrintClassList();
PrintMissingTDFields();
ReportMissingTypes();
ReportHungarianNotationErrors();
vprint( 0, "%i total classes parsed from %i files ( %i headers parsed )\n",
m_nClassesParsed,
m_nFilesProcessed,
m_nHeadersProcessed );
vprint( 0, "%.3f K lines of code processed\n",
(double)m_nLinesOfCode / 1024.0 );
double elapsed = ( m_flEnd - m_flStart );
if ( elapsed > 0.0 )
{
vprint( 0, "%.2f K processed in %.3f seconds, throughput %.2f KB/sec\n\n",
(double)m_nBytesProcessed / 1024.0, elapsed, (double)m_nBytesProcessed / ( 1024.0 * elapsed ) );
}
Clear();
}
CCodeProcessor::CCodeProcessor( void )
{
m_pClassList = NULL;
m_Modules.RemoveAll();
m_bQuiet = false;
m_bPrintHierarchy = false;
m_bPrintMembers = true;
m_bPrintTypedescriptionErrors = true;
m_bPrintPredictionDescErrors = true;
m_bCreateMissingTDs = false;
m_bLogToFile = false;
m_bCheckHungarian = false;
m_nFilesProcessed = 0;
m_nHeadersProcessed = 0;
m_nClassesParsed = 0;
m_nOffset = 0;
m_nBytesProcessed = 0;
m_nLinesOfCode = 0;
m_flStart = 0.0;
m_flEnd = 0.0;
m_szCurrentCPP[ 0 ] = 0;
m_szBaseEntityClass[ 0 ] = 0;
}
CCodeProcessor::~CCodeProcessor( void )
{
}
void CCodeProcessor::ConstructModuleList_R( int level, const char *baseentityclass,
const char *gamespecific, const char *root, const char *srcroot )
{
char directory[ 256 ];
char filename[ 256 ];
WIN32_FIND_DATA wfd;
HANDLE ff;
sprintf( directory, "%s\\*.*", root );
if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
return;
do
{
if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
{
if ( wfd.cFileName[ 0 ] == '.' )
continue;
// Once we descend down a branch, don't keep looking for hl2/tf2 in name, just recurse through all children
if ( level == 0 && !strstr( wfd.cFileName, gamespecific ) )
continue;
// Recurse down directory
sprintf( filename, "%s\\%s", root, wfd.cFileName );
ConstructModuleList_R( level+1, baseentityclass, gamespecific, filename, srcroot );
}
else
{
if ( strstr( wfd.cFileName, ".cpp" ) )
{
ProcessModules( srcroot, root, wfd.cFileName );
}
}
} while ( FindNextFile( ff, &wfd ) );
}
void CCodeProcessor::CleanupIncludePath()
{
for ( int i = m_IncludePath.Count(); --i >= 0; )
{
delete [] m_IncludePath[i];
}
m_IncludePath.RemoveAll();
}
void CCodeProcessor::AddIncludePath( const char *pPath )
{
int i = m_IncludePath.AddToTail();
int nLen = strlen(pPath) + 1;
m_IncludePath[i] = new char[nLen];
memcpy( m_IncludePath[i], pPath, nLen );
}
void CCodeProcessor::SetupIncludePath( const char *sourcetreebase, const char *subdir, const char *gamespecific )
{
CleanupIncludePath();
char path[MAX_PATH];
sprintf( path, "%s\\%s", sourcetreebase, subdir );
strlwr( path );
AddIncludePath( path );
char modsubdir[128];
if ( !stricmp(subdir, "dlls") )
{
sprintf(modsubdir,"%s\\%s_dll", subdir, gamespecific );
}
else if ( !stricmp(subdir, "cl_dll") )
{
sprintf(modsubdir,"%s\\%s_hud", subdir, gamespecific );
}
else
{
sprintf(modsubdir,"%s\\%s", subdir, gamespecific );
}
sprintf( path, "%s\\%s", sourcetreebase, modsubdir );
strlwr( path );
AddIncludePath( path );
// Game shared
sprintf( path, "%s\\game_shared", sourcetreebase );
strlwr( path );
AddIncludePath( path );
sprintf( path, "%s\\game_shared\\%s", sourcetreebase, gamespecific );
strlwr( path );
AddIncludePath( path );
sprintf( path, "%s\\public", sourcetreebase );
strlwr( path );
AddIncludePath( path );
}
void CCodeProcessor::Process( const char *baseentityclass, const char *gamespecific, const char *sourcetreebase, const char *subdir )
{
SetupIncludePath( sourcetreebase, subdir, gamespecific );
strcpy( m_szBaseEntityClass, baseentityclass );
m_nBytesProcessed = 0;
m_nFilesProcessed = 0;
m_nHeadersProcessed = 0;
m_nClassesParsed = 0;
m_nLinesOfCode = 0;
linesprocessed = 0;
m_Modules.RemoveAll();
m_Headers.RemoveAll();
m_flStart = UTIL_FloatTime();
char rootdirectory[ 256 ];
sprintf( rootdirectory, "%s\\%s", sourcetreebase, subdir );
vprint( 0, "--- Processing %s\n\n", rootdirectory );
m_nOffset = strlen( rootdirectory ) + 1;
ConstructModuleList_R( 0, baseentityclass, gamespecific, rootdirectory, sourcetreebase );
sprintf( rootdirectory, "%s\\%s", sourcetreebase, "game_shared" );
vprint( 0, "--- Processing %s\n\n", rootdirectory );
m_nOffset = strlen( rootdirectory ) + 1;
ConstructModuleList_R( 0, baseentityclass, gamespecific, rootdirectory, sourcetreebase );
m_flEnd = UTIL_FloatTime();
PrintResults( baseentityclass );
}
void CCodeProcessor::Process( const char *baseentityclass, const char *gamespecific,
const char *sourcetreebase, const char *subdir, const char *pFileName )
{
SetupIncludePath( sourcetreebase, subdir, gamespecific );
strcpy( m_szBaseEntityClass, baseentityclass );
m_nBytesProcessed = 0;
m_nFilesProcessed = 0;
m_nHeadersProcessed = 0;
m_nClassesParsed = 0;
m_nLinesOfCode = 0;
linesprocessed = 0;
m_Modules.RemoveAll();
m_Headers.RemoveAll();
m_flStart = UTIL_FloatTime();
char rootdirectory[ 256 ];
sprintf( rootdirectory, "%s\\%s", sourcetreebase, subdir );
vprint( 0, "--- Processing %s\n\n", rootdirectory );
m_nOffset = strlen( rootdirectory ) + 1;
ProcessModules( sourcetreebase, rootdirectory, pFileName );
m_flEnd = UTIL_FloatTime();
PrintResults( baseentityclass );
}
void CCodeProcessor::SetQuiet( bool quiet )
{
m_bQuiet = quiet;
}
bool CCodeProcessor::GetQuiet( void ) const
{
return m_bQuiet;
}
void CCodeProcessor::SetPrintHierarchy( bool print )
{
m_bPrintHierarchy = print;
}
bool CCodeProcessor::GetPrintHierarchy( void ) const
{
return m_bPrintHierarchy;
}
void CCodeProcessor::SetPrintMembers( bool print )
{
m_bPrintMembers = print;
}
bool CCodeProcessor::GetPrintMembers( void ) const
{
return m_bPrintMembers;
}
void CCodeProcessor::SetPrintTDs( bool print )
{
m_bPrintTypedescriptionErrors = print;
}
bool CCodeProcessor::GetPrintTDs( void ) const
{
return m_bPrintTypedescriptionErrors;
}
void CCodeProcessor::SetLogFile( bool log )
{
m_bLogToFile = log;
}
bool CCodeProcessor::GetLogFile( void ) const
{
return m_bLogToFile;
}
void CCodeProcessor::SetPrintPredTDs( bool print )
{
m_bPrintPredictionDescErrors = print;
}
bool CCodeProcessor::GetPrintPredTDs( void ) const
{
return m_bPrintPredictionDescErrors;
}
void CCodeProcessor::SetPrintCreateMissingTDs( bool print )
{
m_bCreateMissingTDs = print;
}
bool CCodeProcessor::GetPrintCreateMissingTDs( void ) const
{
return m_bCreateMissingTDs;
}
void CCodeProcessor::SetPrintCreateMissingPredTDs( bool print )
{
m_bCreateMissingPredTDs = print;
}
bool CCodeProcessor::GetPrintCreateMissingPredTDs( void ) const
{
return m_bCreateMissingPredTDs;
}
void CCodeProcessor::SetCheckHungarian( bool check )
{
m_bCheckHungarian = check;
}
bool CCodeProcessor::GetCheckHungarian() const
{
return m_bCheckHungarian;
}
static CCodeProcessor g_Processor;
ICodeProcessor *processor = ( ICodeProcessor * )&g_Processor;