diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/classcheck/processmodule.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'utils/classcheck/processmodule.cpp')
| -rw-r--r-- | utils/classcheck/processmodule.cpp | 1866 |
1 files changed, 1866 insertions, 0 deletions
diff --git a/utils/classcheck/processmodule.cpp b/utils/classcheck/processmodule.cpp new file mode 100644 index 0000000..fd5c2b1 --- /dev/null +++ b/utils/classcheck/processmodule.cpp @@ -0,0 +1,1866 @@ +//========= 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;
\ No newline at end of file |