//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include #include #include "stdafx.h" #include #include #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;