You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1866 lines
39 KiB
1866 lines
39 KiB
//========= 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;
|