summaryrefslogtreecommitdiff
path: root/utils/classcheck
diff options
context:
space:
mode:
Diffstat (limited to 'utils/classcheck')
-rw-r--r--utils/classcheck/class.cpp1581
-rw-r--r--utils/classcheck/class.h196
-rw-r--r--utils/classcheck/classcheck.cpp216
-rw-r--r--utils/classcheck/classcheck.vpc39
-rw-r--r--utils/classcheck/classcheck_util.cpp369
-rw-r--r--utils/classcheck/classcheck_util.h29
-rw-r--r--utils/classcheck/codeprocessor.h129
-rw-r--r--utils/classcheck/icodeprocessor.h59
-rw-r--r--utils/classcheck/processmodule.cpp1866
-rw-r--r--utils/classcheck/stdafx.cpp15
-rw-r--r--utils/classcheck/stdafx.h26
11 files changed, 4525 insertions, 0 deletions
diff --git a/utils/classcheck/class.cpp b/utils/classcheck/class.cpp
new file mode 100644
index 0000000..369aa74
--- /dev/null
+++ b/utils/classcheck/class.cpp
@@ -0,0 +1,1581 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <assert.h>
+#include "stdafx.h"
+#include <stdio.h>
+#include <windows.h>
+#include "classcheck_util.h"
+#include "class.h"
+#include "icodeprocessor.h"
+
+CClass::CClass( const char *name )
+{
+ m_nVarCount = 0;
+ m_nMemberCount = 0;
+ m_nTDCount = 0;
+ m_nPredTDCount = 0;
+
+ strcpy( m_szName, name );
+ m_szBaseClass[0]=0;
+ m_pBaseClass = NULL;
+ m_szTypedefBaseClass[0]=0;
+
+ m_bDerivedFromCBaseEntity = false;
+ m_bHasSaveRestoreData = false;
+ m_bHasPredictionData = false;
+ m_bConstructPredictableCalled = false;
+ m_bHasRecvTableData = false;
+
+ m_nClassDataSize = 0;
+}
+
+CClass::~CClass( void )
+{
+ int i;
+
+ for ( i = 0; i < m_nVarCount; i++ )
+ {
+ delete m_Variables[ i ];
+ }
+ m_nVarCount = 0;
+
+ for ( i = 0; i < m_nMemberCount; i++ )
+ {
+ delete m_Members[ i ];
+ }
+ m_nMemberCount = 0;
+ for ( i = 0; i < m_nTDCount; i++ )
+ {
+ delete m_TDFields[ i ];
+ }
+ m_nTDCount = 0;
+ for ( i = 0; i < m_nPredTDCount; i++ )
+ {
+ delete m_PredTDFields[ i ];
+ }
+ m_nPredTDCount = 0;
+}
+
+CTypeDescriptionField *CClass::FindTD( const char *name )
+{
+ for ( int i = 0; i < m_nTDCount; i++ )
+ {
+ if ( !strcmp( m_TDFields[ i ]->m_szVariableName, name ) )
+ return m_TDFields[ i ];
+ }
+ return NULL;
+}
+
+CTypeDescriptionField *CClass::FindPredTD( const char *name )
+{
+ for ( int i = 0; i < m_nPredTDCount; i++ )
+ {
+ if ( !strcmp( m_PredTDFields[ i ]->m_szVariableName, name ) )
+ return m_PredTDFields[ i ];
+ }
+ return NULL;
+}
+
+CClassVariable *CClass::FindVar( const char *name, bool checkbaseclasses /*= false*/ )
+{
+ CClass *cl = this;
+ while ( cl )
+ {
+ for ( int i = 0; i < cl->m_nVarCount; i++ )
+ {
+ if ( !strcmp( cl->m_Variables[ i ]->m_szName, name ) )
+ return cl->m_Variables[ i ];
+ }
+
+ if ( !checkbaseclasses )
+ break;
+
+ if ( !cl->m_pBaseClass )
+ {
+ cl->m_pBaseClass = processor->FindClass( cl->m_szBaseClass );
+ }
+
+ cl = cl->m_pBaseClass;
+ if ( !cl )
+ break;
+ }
+ return NULL;
+}
+
+CClassMemberFunction *CClass::FindMember( const char *name )
+{
+ for ( int i = 0; i < m_nMemberCount; i++ )
+ {
+ if ( !strcmp( m_Members[ i ]->m_szName, name ) )
+ return m_Members[ i ];
+ }
+ return NULL;
+}
+
+CTypeDescriptionField *CClass::AddTD( const char *name, const char *type, const char *definetype, bool incomments )
+{
+ CTypeDescriptionField *td = FindTD( name );
+ if ( !td )
+ {
+ td = new CTypeDescriptionField();
+ strcpy( td->m_szVariableName, name );
+ strcpy( td->m_szType, type );
+ strcpy( td->m_szDefineType, definetype );
+ td->m_bCommentedOut = incomments;
+
+ m_TDFields[ m_nTDCount++ ] = td;
+ if ( m_nTDCount >= MAX_TDFIELDS )
+ {
+ vprint( 0, "too many typedescription fields\n" );
+ exit( 1 );
+ }
+ }
+ return td;
+}
+
+CTypeDescriptionField *CClass::AddPredTD( const char *name, const char *type, const char *definetype, bool incomments, bool inrecvtable )
+{
+ CTypeDescriptionField *td = FindPredTD( name );
+ if ( !td )
+ {
+ td = new CTypeDescriptionField();
+ strcpy( td->m_szVariableName, name );
+ strcpy( td->m_szType, type );
+ strcpy( td->m_szDefineType, definetype );
+ td->m_bCommentedOut = incomments;
+ td->m_bRepresentedInRecvTable = inrecvtable;
+
+ m_PredTDFields[ m_nPredTDCount++ ] = td;
+ if ( m_nPredTDCount >= MAX_TDFIELDS )
+ {
+ vprint( 0, "too many prediction typedescription fields\n" );
+ exit( 1 );
+ }
+ }
+ return td;
+}
+
+CClassVariable *CClass::AddVar( const char *name )
+{
+ CClassVariable *var = FindVar( name );
+ if ( !var )
+ {
+ var = new CClassVariable();
+ strcpy( var->m_szName, name );
+
+ m_Variables[ m_nVarCount++ ] = var;
+ if ( m_nVarCount >= MAX_VARIABLES )
+ {
+ vprint( 0, "too many variables\n" );
+ exit( 1 );
+ }
+ }
+ return var;
+}
+
+CClassMemberFunction *CClass::AddMember( const char *name )
+{
+ CClassMemberFunction *member = FindMember( name );
+ if ( !member )
+ {
+ member = new CClassMemberFunction();
+ strcpy( member->m_szName, name );
+
+ m_Members[ m_nMemberCount++ ] = member;
+ if ( m_nMemberCount >= MAX_MEMBERS )
+ {
+ vprint( 0, "too many members\n" );
+ exit( 1 );
+ }
+ }
+ return member;
+}
+
+void CClass::SetBaseClass( const char *name )
+{
+ if ( !m_szBaseClass[ 0 ] )
+ {
+ strcpy( m_szBaseClass, name );
+ }
+ else if ( stricmp( m_szBaseClass, name ) )
+ {
+ vprint( 0, "Base class differs for %s %s vs %s\n", m_szName, m_szBaseClass, name );
+ }
+}
+
+void CClass::CheckChildOfBaseEntity( const char *baseentityclass )
+{
+ m_bDerivedFromCBaseEntity = false;
+
+ if ( !stricmp( m_szName, baseentityclass ) )
+ {
+ m_bDerivedFromCBaseEntity = true;
+ return;
+ }
+
+ CClass *base = m_pBaseClass;
+ while ( base )
+ {
+ // Early out?
+ if ( base->m_bDerivedFromCBaseEntity )
+ {
+ m_bDerivedFromCBaseEntity = true;
+ return;
+ }
+
+ // Check name
+ if ( !stricmp( base->m_szName, baseentityclass ) )
+ {
+ m_bDerivedFromCBaseEntity = true;
+ return;
+ }
+
+ // Keep going up hierarchy
+ base = base->m_pBaseClass;
+ }
+}
+
+static bool IsType( char *input, char *test )
+{
+ char *pMatch = strstr( input, test );
+ if ( !pMatch )
+ return false;
+
+ size_t nLen = strlen(test);
+ if ( ( pMatch[nLen] != 0 ) && (pMatch[nLen] != ' ') )
+ return false;
+
+ if ( input != pMatch && (*(pMatch-1) != ' ') )
+ return false;
+
+ return true;
+}
+
+static char const *TranslateSimpleType( CClassVariable *var )
+{
+ static char out[ 256 ];
+ out[ 0 ] = 0;
+
+ char *input = var->m_szType;
+
+ // Don't know how to handle templatized things yet
+ if ( strstr( input, "<" ) )
+ {
+ return out;
+ }
+
+ if ( IsType( input, "bool" ) )
+ {
+ return "FIELD_BOOLEAN";
+ }
+ else if ( IsType( input, "short" ) )
+ {
+ return "FIELD_SHORT";
+ }
+ else if ( IsType( input, "int" ) )
+ {
+ return "FIELD_INTEGER";
+ }
+ else if ( IsType( input, "byte" ) )
+ {
+ return "FIELD_CHARACTER";
+ }
+ else if ( IsType( input, "float" ) )
+ {
+ return "FIELD_FLOAT";
+ }
+ else if ( IsType( input, "EHANDLE" ) || IsType( input, "CHandle" ) )
+ {
+ return "FIELD_EHANDLE";
+ }
+ else if ( IsType( input, "color32" ) )
+ {
+ return "FIELD_COLOR32";
+ }
+ else if ( IsType( input, "Vector" ) || IsType( input, "QAngle" ) )
+ {
+ return "FIELD_VECTOR";
+ }
+ else if ( IsType( input, "Quaternion" ) )
+ {
+ return "FIELD_QUATERNION";
+ }
+ else if ( IsType( input, "VMatrix" ) )
+ {
+ return "FIELD_VMATRIX";
+ }
+ else if ( IsType( input, "string_t" ) )
+ {
+ return "FIELD_STRING";
+ }
+ else if ( IsType( input, "char" ) )
+ {
+ return "FIELD_CHARACTER";
+ }
+
+ return out;
+}
+
+void CClass::ReportTypeMismatches( CClassVariable *var, CTypeDescriptionField *td )
+{
+ char const *t = TranslateSimpleType( var );
+ if ( !t[0] )
+ return;
+
+ // Special cases
+ if ( td->m_bCommentedOut )
+ return;
+
+ if ( !strcmp( td->m_szType, "FIELD_TIME" ) )
+ {
+ if ( !strcmp( t, "FIELD_FLOAT" ) )
+ return;
+ }
+
+ if ( !strcmp( td->m_szType, "FIELD_TICK" ) )
+ {
+ if ( !strcmp( t, "FIELD_INTEGER" ) )
+ return;
+ }
+
+ if ( !strcmp( td->m_szType, "FIELD_MODELNAME" ) || !strcmp( td->m_szType, "FIELD_SOUNDNAME" ) )
+ {
+ if ( !strcmp( t, "FIELD_STRING" ) )
+ return;
+ }
+
+ if ( !strcmp( td->m_szType, "FIELD_MODELINDEX" ) || !strcmp( td->m_szType, "FIELD_MATERIALINDEX" ) )
+ {
+ if ( !strcmp( t, "FIELD_INTEGER" ) )
+ return;
+ }
+
+ if ( !strcmp( td->m_szType, "FIELD_POSITION_VECTOR" ) )
+ {
+ if ( !strcmp( t, "FIELD_VECTOR" ) )
+ return;
+ }
+
+ if ( !strcmp( td->m_szType, "FIELD_VMATRIX_WORLDSPACE" ) )
+ {
+ if ( !strcmp( t, "FIELD_VMATRIX" ) )
+ return;
+ }
+
+ if ( strcmp( t, td->m_szType ) )
+ {
+ vprint( 0, "class %s has an incorrect FIELD_ type for variable '%s (%s, %s)'\n",
+ m_szName, var->m_szName, var->m_szType, td->m_szType );
+ }
+}
+
+
+bool CClass::CheckForMissingTypeDescriptionFields( int& missingcount, bool createtds )
+{
+ bool bret = false;
+ missingcount = 0;
+ // Didn't specify a TYPEDESCRIPTION at all
+ if ( !m_bHasSaveRestoreData )
+ return bret;
+
+ for ( int i = 0; i < m_nVarCount; i++ )
+ {
+ CClassVariable *var = m_Variables[ i ];
+
+ bool isstatic = false;
+ char *p = var->m_szType;
+ while ( 1 )
+ {
+ p = CC_ParseToken( p );
+ if ( strlen( com_token ) <= 0 )
+ break;
+ if ( !stricmp( com_token, "static" ) )
+ {
+ isstatic = true;
+ break;
+ }
+ }
+
+ // Statics aren't encoded
+ if ( isstatic )
+ continue;
+
+ char *goodname = var->m_szName;
+
+ // Skip * pointer modifier
+ while ( *goodname && *goodname == '*' )
+ {
+ goodname++;
+ }
+
+ CTypeDescriptionField *td = FindTD( goodname );
+ if ( td )
+ {
+ ReportTypeMismatches( var, td );
+ continue;
+ }
+
+ bret = true;
+ missingcount++;
+
+ if ( !createtds )
+ {
+ vprint( 0, "class %s missing typedescription_t field for variable '%s %s'\n",
+ m_szName, var->m_szType, var->m_szName );
+ continue;
+ }
+
+ char const *t = TranslateSimpleType( var );
+
+ vprint( 0, "//\tClass %s:\n", m_szName );
+ if ( var->m_bIsArray &&
+ (
+ stricmp( var->m_szType, "char" ) ||
+ stricmp( t, "FIELD_STRING" )
+ ) )
+ {
+ if ( *t )
+ {
+ vprint( 0, "\tDEFINE_ARRAY( %s, %s, %s ),\n", goodname, t, var->m_szArraySize );
+ }
+ else
+ {
+ vprint( 0, "\t// DEFINE_ARRAY( %s, %s, %s ),\n", goodname, var->m_szType, var->m_szArraySize );
+ }
+ }
+ else
+ {
+ if ( *t )
+ {
+ vprint( 0, "\tDEFINE_FIELD( %s, %s ),\n", goodname, t );
+ }
+ else
+ {
+ vprint( 0, "\t// DEFINE_FIELD( %s, %s ),\n", goodname, var->m_szType );
+ }
+ }
+ }
+
+ return bret;
+}
+
+bool CClass::CheckForPredictionFieldsInRecvTableNotMarkedAsSuchCorrectly( int &missingcount )
+{
+ bool bret = false;
+ missingcount = 0;
+ // Didn't specify a TYPEDESCRIPTION at all
+ if ( !m_bHasPredictionData )
+ return bret;
+
+ if ( !m_bHasRecvTableData )
+ return bret;
+
+ for ( int i = 0; i < m_nVarCount; i++ )
+ {
+ CClassVariable *var = m_Variables[ i ];
+ bool inreceivetable = var->m_bInRecvTable;
+
+ bool isstatic = false;
+ char *p = var->m_szType;
+ while ( 1 )
+ {
+ p = CC_ParseToken( p );
+ if ( strlen( com_token ) <= 0 )
+ break;
+ if ( !stricmp( com_token, "static" ) )
+ {
+ isstatic = true;
+ break;
+ }
+ }
+
+ // Statics aren't encoded
+ if ( isstatic )
+ continue;
+
+ char *goodname = var->m_szName;
+
+ // Skip * pointer modifier
+ while ( *goodname && *goodname == '*' )
+ goodname++;
+
+ CTypeDescriptionField *td = FindPredTD( goodname );
+ // Missing variables are caught in a different routine
+ td = FindPredTD( goodname );
+ if ( !td )
+ continue;
+
+ // These are implicitly ok
+ if ( !strcmp( td->m_szDefineType, "DEFINE_PRED_TYPEDESCRIPTION" ) )
+ {
+ CClass *cl2 = processor->FindClass( td->m_szType );
+ if ( cl2 )
+ {
+ bret = cl2->CheckForPredictionFieldsInRecvTableNotMarkedAsSuchCorrectly( missingcount );
+ }
+ continue;
+ }
+
+ // Looks good (either in or out!)
+ // Check for appripriate flags
+ if ( inreceivetable == td->m_bRepresentedInRecvTable )
+ continue;
+
+ bret = true;
+ missingcount++;
+
+ if ( inreceivetable && !td->m_bRepresentedInRecvTable )
+ {
+ vprint( 0, "%s::%s: Missing FTYPEDESC_INSENDTABLE flag in prediction typedescription\n", m_szName, var->m_szName );
+ }
+ else
+ {
+ vprint( 0, "%s::%s: Field marked as FTYPEDESC_INSENDTABLE in prediction typedescription missing from RecvTable\n", m_szName, var->m_szName );
+ }
+ }
+
+ return bret;
+}
+
+bool CClass::CheckForMissingPredictionFields( int& missingcount, bool createtds )
+{
+ bool bret = false;
+ missingcount = 0;
+ // Didn't specify a TYPEDESCRIPTION at all
+ if ( !m_bHasPredictionData )
+ return bret;
+
+ for ( int i = 0; i < m_nVarCount; i++ )
+ {
+ CClassVariable *var = m_Variables[ i ];
+
+ // private and protected variables can't be referenced in data tables right now
+ //if ( var->m_Type != CClassVariable::TPUBLIC )
+ // continue;
+
+ bool isstatic = false;
+ char *p = var->m_szType;
+ while ( 1 )
+ {
+ p = CC_ParseToken( p );
+ if ( strlen( com_token ) <= 0 )
+ break;
+ if ( !stricmp( com_token, "static" ) )
+ {
+ isstatic = true;
+ break;
+ }
+ }
+
+ // Statics aren't encoded
+ if ( !isstatic )
+ {
+ char *goodname = var->m_szName;
+
+ // Skip * pointer modifier
+ while ( *goodname && *goodname == '*' )
+ goodname++;
+
+ CTypeDescriptionField *td = FindPredTD( goodname );
+ td = FindPredTD( goodname );
+ if ( !td )
+ {
+ bret = true;
+ missingcount++;
+
+ if ( !createtds )
+ {
+ vprint( 0, "class %s missing prediction typedescription_t field for variable '%s %s'\n",
+ m_szName, var->m_szType, var->m_szName );
+ }
+ else
+ {
+ char const *t = TranslateSimpleType( var );
+
+ vprint( 0, "//\tClass %s:\n", m_szName );
+ if ( var->m_bIsArray &&
+ (
+ stricmp( var->m_szType, "char" ) ||
+ stricmp( t, "FIELD_STRING" )
+ ) )
+ {
+ if ( *t )
+ {
+ vprint( 0, "\tDEFINE_ARRAY( %s, %s, %s ),\n", goodname, t, var->m_szArraySize );
+ }
+ else
+ {
+ vprint( 0, "\t// DEFINE_ARRAY( %s, %s, %s ),\n", goodname, var->m_szType, var->m_szArraySize );
+ }
+ }
+ else
+ {
+ if ( *t )
+ {
+ vprint( 0, "\tDEFINE_FIELD( %s, %s ),\n", goodname, t );
+ }
+ else
+ {
+ vprint( 0, "\t// DEFINE_FIELD( %s, %s ),\n", goodname, var->m_szType );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bret;
+}
+
+void AppendType( const char *token, char *type )
+{
+ strcat( type, token );
+ strcat( type, " " );
+}
+
+class MissingType
+{
+public:
+ CClass *owning_class;
+ CClassVariable *var;
+};
+
+#include "utldict.h"
+CUtlDict< MissingType, unsigned short > missing_types;
+
+bool IsMissingType( CClass *cl, CClassVariable *var )
+{
+ unsigned short lookup;
+
+ lookup = missing_types.Find( var->m_szType );
+ if ( lookup != missing_types.InvalidIndex() )
+ return true;
+
+ MissingType t;
+ t.owning_class = cl;
+ t.var = var;
+ missing_types.Insert( var->m_szType, t );
+ return true;
+}
+
+void ReportMissingTypes( void )
+{
+ int c = missing_types.Count();
+ for ( int i= 0; i < c; i++ )
+ {
+ MissingType *t = &missing_types[ i ];
+ if ( !t )
+ continue;
+
+ if ( !t->owning_class )
+ continue;
+
+ if ( !t->owning_class->m_bDerivedFromCBaseEntity )
+ continue;
+
+ if ( !t->var )
+ continue;
+
+ vprint( 0, "Can't compute size of %s %s %s\n",
+ t->owning_class->m_szName, t->var->m_szType, t->var->m_szName );
+ }
+}
+
+void ClearMissingTypes()
+{
+ missing_types.Purge();
+}
+
+static int GetTypeSize( CClass *cl, CClassVariable *var )
+{
+ int out = 0;
+
+ char *input = var->m_szType;
+
+ // Don't know how to handle templatized things yet
+ if ( strstr( input, "<" ) )
+ {
+ IsMissingType( cl, var );
+ return out;
+ }
+
+ if ( strstr( var->m_szName, "*" ) )
+ {
+ return sizeof( void * );
+ }
+
+ if ( strstr( input, "bool" ) )
+ {
+ return sizeof( bool );
+ }
+ else if ( strstr( input, "int64" ) )
+ {
+ return sizeof( __int64 );
+ }
+ else if ( strstr( input, "short" ) )
+ {
+ return sizeof( short );
+ }
+ else if ( strstr( input, "unsigned short" ) )
+ {
+ return sizeof( unsigned short );
+ }
+ else if ( strstr( input, "int" ) )
+ {
+ return sizeof( int );
+ }
+ else if ( strstr( input, "float" ) )
+ {
+ return sizeof( float );
+ }
+ else if ( strstr( input, "vec_t" ) )
+ {
+ return sizeof( float );
+ }
+ else if ( strstr( input, "Vector" ) || strstr( input, "QAngle" ) )
+ {
+ return 3 * sizeof( float );
+ }
+ else if ( strstr( input, "vec3_t" ) )
+ {
+ return 3 * sizeof( float );
+ }
+ else if ( strstr( input, "char" ) )
+ {
+ return sizeof( char );
+ }
+ else if ( strstr( input, "unsigned char" ) )
+ {
+ return sizeof( unsigned char );
+ }
+ else if ( strstr( input, "BYTE" ) )
+ {
+ return sizeof( char );
+ }
+ else if ( strstr( input, "byte" ) )
+ {
+ return sizeof( char );
+ }
+ else if ( !strcmp( input, "unsigned" ) )
+ {
+ return sizeof(unsigned int);
+ }
+ else if ( strstr( input, "long" ) )
+ {
+ return sizeof( int );
+ }
+ else if ( strstr( input, "color32" ) )
+ {
+ return sizeof( int );
+ }
+ // It's a pointer
+ else if ( strstr( input, "*" ) )
+ {
+ return sizeof( void * );
+ }
+ // Static data doesn't count
+ else if ( strstr( input, "static" ) )
+ {
+ return 0;
+ }
+
+
+ // Okay, see if it's a classname
+ CClass *base = processor->FindClass( input );
+ if ( base )
+ {
+ return base->m_nClassDataSize;
+ }
+
+ IsMissingType( cl, var );
+
+ return out;
+}
+
+
+void CClass::AddVariable( int protection, char *type, char *name, bool array, char *arraysize )
+{
+ CClassVariable *var = AddVar( name );
+ if ( !var )
+ return;
+
+ strcpy( var->m_szType, type );
+ var->m_Type = (CClassVariable::VARTYPE)protection;
+ var->m_TypeSize = GetTypeSize( this, var );
+
+ m_nClassDataSize += var->m_TypeSize;
+
+ if ( array )
+ {
+ var->m_bIsArray = true;
+ strcpy( var->m_szArraySize, arraysize );
+ }
+ else
+ {
+ var->m_bIsArray = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses information to determine the base class of this class
+//-----------------------------------------------------------------------------
+bool CClass::ParseBaseClass( char *&input )
+{
+ if ( !strcmp( com_token, "DECLARE_CLASS" )
+ || !strcmp( com_token, "DECLARE_CLASS_GAMEROOT" )
+ || !strcmp( com_token, "DECLARE_CLASS_NOFRIEND" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+ input = CC_ParseToken( input );
+
+ do
+ {
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+
+ m_szTypedefBaseClass[0] = 0;
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( m_szTypedefBaseClass, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+ return true;
+ }
+ else if ( !strcmp( com_token, "DECLARE_CLASS_NOBASE" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+ input = CC_DiscardUntilMatchingCharIncludingNesting( input, "()" );
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses networkvars
+//-----------------------------------------------------------------------------
+bool CClass::ParseNetworkVar( char *&input, int protection )
+{
+ MemberVarParse_t var;
+
+ if ( !strcmp( com_token, "CNetworkVar" ) ||
+ !strcmp( com_token, "CNetworkVarForDerived" ) ||
+ !strcmp( com_token, "CNetworkVarEmbedded" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pType, com_token );
+ strcat( var.m_pType, " " );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, var.m_pType, var.m_pName, false );
+ return true;
+ }
+
+ if ( !strcmp( com_token, "CNetworkHandle" ) || !strcmp( com_token, "CNetworkHandleForDerived" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ strcpy( var.m_pType, "CHandle<" );
+ do
+ {
+ strcat( var.m_pType, com_token );
+ strcat( var.m_pType, " " );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+ strcat( var.m_pType, ">" );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, "EHANDLE", var.m_pName, false );
+ return true;
+ }
+
+ if ( !strcmp( com_token, "CNetworkVector" ) ||
+ !strcmp( com_token, "CNetworkVectorForDerived" ) ||
+ !strcmp( com_token, "CNetworkQAngle" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, "Vector", var.m_pName, false );
+ return true;
+ }
+
+ if ( !strcmp( com_token, "CNetworkColor32" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, "color32", var.m_pName, false );
+ return true;
+ }
+
+ if ( !strcmp( com_token, "CNetworkString" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pArraySize, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, "char *", var.m_pName, true, var.m_pArraySize );
+ return true;
+ }
+
+ if ( !strcmp( com_token, "CNetworkArray" ) || !strcmp( com_token, "CNetworkArrayForDerived" ) )
+ {
+ input = CC_ParseToken( input );
+ Assert( !strcmp( com_token, "(") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pType, com_token );
+ strcat( var.m_pType, " " );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pName, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ",") );
+
+ input = CC_ParseToken( input );
+ do
+ {
+ strcat( var.m_pArraySize, com_token );
+ input = CC_ParseToken( input );
+ } while( strcmp( com_token, ")") );
+
+ AddVariable( protection, var.m_pType, var.m_pName, true, var.m_pArraySize );
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses a class member definition
+//-----------------------------------------------------------------------------
+bool CClass::ParseClassMember( char *&input, int protection )
+{
+ MemberVarParse_t var;
+
+ bool isfunction = false;
+ bool wascomma = false;
+ bool skipvar = false;
+
+ if ( ParseNetworkVar( input, protection ) )
+ return true;
+
+ strcpy( var.m_pName, com_token );
+ if ( !stricmp( var.m_pName, "SHARED_CLASSNAME" ) )
+ {
+ input = CC_ParseToken( input );
+ if ( !stricmp( com_token, "(" ) )
+ {
+ char inside[ 256 ];
+ char *saveinput = input;
+ input = CC_DiscardUntilMatchingCharIncludingNesting( input, "()" );
+
+ int len = input - saveinput;
+ strncpy( inside, saveinput, len );
+ inside[ len ] =0;
+
+ strcat( var.m_pName, "(" );
+ strcat( var.m_pName, inside );
+ }
+ }
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( !stricmp( com_token, "(" ) )
+ {
+ char *saveinput = input;
+
+ isfunction = true;
+
+ input = CC_DiscardUntilMatchingCharIncludingNesting( input, "()" );
+
+ // see if the function is being declared in line here
+ input = CC_ParseToken( input );
+
+ if ( !stricmp( com_token, "const" ) )
+ {
+ // Swallow const if we see it
+ input = CC_ParseToken( input );
+ }
+
+ if ( !stricmp( com_token, "{" ) )
+ {
+ input = CC_DiscardUntilMatchingCharIncludingNesting( input, "{}" );
+ }
+ // pure virtual function?
+ else if ( !stricmp( com_token, "=" ) )
+ {
+ char ch;
+ input = CC_RawParseChar( input, ";", &ch );
+ }
+ // this was a pointer to a base function
+ else if ( !stricmp( com_token, "(" ) )
+ {
+ char *end = input - 2;
+ input = saveinput;
+
+ char pfn[ 256 ];
+ int len = end - saveinput;
+ strncpy( pfn, input, len );
+ pfn[ len ] = 0;
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( com_token[0] == '*' )
+ {
+ break;
+ }
+ } while ( 1 );
+
+ if ( com_token[0] == '*' )
+ {
+ // com_token is the variable name
+ sprintf( var.m_pType, "%s (%s)", var.m_pName, pfn );
+ strcpy( var.m_pName, com_token );
+ input = end + 1;
+ }
+
+ if ( *input == '(' )
+ input++;
+ input = CC_DiscardUntilMatchingCharIncludingNesting( input, "()" );
+
+ isfunction = false;
+ }
+
+ break;
+ }
+ else if ( !stricmp( com_token, "[" ) )
+ {
+ // It's an array
+ var.m_bArray = true;
+ char ch;
+ char *oldinput = input;
+ do
+ {
+ input = CC_RawParseChar( input, "]", &ch );
+ if ( *input && ( *input == '[' ) )
+ {
+ input++;
+ continue;
+ }
+
+ break;
+ } while ( 1 );
+ int len = input-oldinput - 1;
+ if ( len > 0 )
+ {
+ strncpy( var.m_pArraySize, oldinput, len );
+ }
+ var.m_pArraySize[ len ] = 0;
+ break;
+ }
+ else if ( !stricmp( com_token, ";" ) )
+ {
+ break;
+ }
+ else if ( !stricmp( com_token, ":" ) && !isfunction )
+ {
+ // Eliminate the length specification
+ input = CC_ParseToken( input );
+ continue;
+ }
+ else if ( !stricmp( com_token, "," ) )
+ {
+ wascomma = true;
+ break;
+ }
+ // It's a templatized var
+ else if (( com_token[ strlen( com_token ) - 1 ] == '<' ) && strcmp(var.m_pName, "operator") )
+ {
+ do
+ {
+ AppendType( var.m_pName, var.m_pType );
+ strcpy( var.m_pName, com_token );
+
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+ }
+ while ( strcmp( com_token, ">" ) );
+
+ AppendType( var.m_pName, var.m_pType );
+ strcpy( var.m_pName, com_token );
+ }
+ else
+ {
+ if ( !stricmp( var.m_pName, "typedef" ) ||
+ !stricmp( var.m_pName, "enum" ) ||
+ !stricmp( var.m_pName, "friend" ) )
+ {
+ skipvar = true;
+ }
+ AppendType( var.m_pName, var.m_pType );
+ strcpy( var.m_pName, com_token );
+ continue;
+ }
+
+ } while ( 1 );
+
+ if ( strlen( var.m_pType ) >= 1 )
+ {
+ var.m_pType[ strlen( var.m_pType ) - 1 ] = 0;
+ }
+
+ if ( var.m_pType[0]==0 &&
+ ( !strcmp( var.m_pName, "CUSTOM_SCHEDULES" ) ||
+ !strcmp( var.m_pName, "DEFINE_CUSTOM_SCHEDULE_PROVIDER" ) ||
+ !strcmp( var.m_pName, "DEFINE_CUSTOM_AI" ) ||
+ !strcmp( var.m_pName, "DECLARE_DATADESC" ) ||
+ !strcmp( var.m_pName, "DECLARE_EMBEDDED_DATADESC" ) ||
+ !strcmp( var.m_pName, "DECLARE_SERVERCLASS" ) ||
+ !strcmp( var.m_pName, "DECLARE_CLIENTCLASS" ) ||
+ !strcmp( var.m_pName, "DECLARE_ENTITY_PANEL" ) ||
+ !strcmp( var.m_pName, "DECLARE_MINIMAP_PANEL" ) ||
+ !strcmp( var.m_pName, "MANUALMODE_GETSET_PROP" ) ) )
+ {
+ return true;
+ }
+
+ if ( var.m_pType[0]==0 &&
+ ( !strcmp( var.m_pName, "DECLARE_PREDICTABLE" ) ||
+ !strcmp( var.m_pName, "DECLARE_EMBEDDED_PREDDESC" ) ) )
+ {
+ m_bHasPredictionData = true;
+ return true;
+ }
+
+ /*
+ if ( var.m_pName[0] == '*' )
+ {
+ strcat( type, " *" );
+
+ char newname[ 256 ];
+ strcpy( newname, &var.m_pName[1] );
+ strcpy( var.m_pName, newname );
+ }
+ */
+
+ if ( isfunction )
+ {
+ CClassMemberFunction *member = AddMember( var.m_pName );
+ if ( member )
+ {
+ strcpy( member->m_szType, var.m_pType );
+ member->m_Type = (CClassMemberFunction::MEMBERTYPE)protection;
+ }
+ }
+ else
+ {
+ // It's a variable
+ do
+ {
+ if ( !skipvar )
+ {
+ AddVariable( protection, var.m_pType, var.m_pName, var.m_bArray, var.m_pArraySize );
+ }
+ else if ( !stricmp( var.m_pName, "BaseClass" ) )
+ {
+ if ( !m_szTypedefBaseClass[0] )
+ {
+ char *p = var.m_pType;
+ p = CC_ParseToken( p );
+ p = CC_ParseToken( p );
+ strcpy( m_szTypedefBaseClass, com_token );
+ }
+ }
+
+ if ( !wascomma )
+ break;
+
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ // Remove length specifiers
+ if ( !stricmp( com_token, ":" ) )
+ {
+ input = CC_ParseToken( input );
+ input = CC_ParseToken( input );
+ }
+
+ if ( !stricmp( com_token, "," ) )
+ {
+ input = CC_ParseToken( input );
+ }
+
+ if ( !stricmp( com_token, ";" ) )
+ break;
+
+ strcpy( var.m_pName, com_token );
+
+ } while ( 1 );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses a nested class definition
+//-----------------------------------------------------------------------------
+bool CClass::ParseNestedClass( char *&input )
+{
+ if ( stricmp( com_token, "struct" ) && stricmp( com_token, "class" ) )
+ return false;
+
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) > 0 )
+ {
+ //vprint( depth, "class %s\n", com_token );
+ char decorated[ 256 ];
+ sprintf( decorated, "%s::%s", m_szName, com_token );
+
+ CClass *cl = processor->AddClass( decorated );
+
+ // Now see if there's a base class
+ input = CC_ParseToken( input );
+ if ( !stricmp( com_token, ":" ) )
+ {
+ // Parse out public and then classname an
+ input = CC_ParseToken( input );
+ if ( !stricmp( com_token, "public" ) )
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) > 0 )
+ {
+ cl->SetBaseClass( com_token );
+
+ do
+ {
+ input = CC_ParseToken( input );
+ } while ( strlen( com_token ) && stricmp( com_token, "{" ) );
+
+ if ( !stricmp( com_token, "{" ) )
+ {
+ input = cl->ParseClassDeclaration( input );
+ }
+ }
+ }
+ }
+ else if ( !stricmp( com_token, "{" ) )
+ {
+ input = cl->ParseClassDeclaration( input );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses public/protected/private
+//-----------------------------------------------------------------------------
+bool CClass::ParseProtection( char *&input, int &protection )
+{
+ if ( !stricmp( com_token, "public" ) )
+ {
+ protection = 0;
+ input = CC_ParseToken( input );
+ Assert( !stricmp( com_token, ":" ) );
+ return true;
+ }
+ else if ( !stricmp( com_token, "protected" ) )
+ {
+ protection = 1;
+ input = CC_ParseToken( input );
+ Assert( !stricmp( com_token, ":" ) );
+ return true;
+ }
+ else if ( !stricmp( com_token, "private" ) )
+ {
+ protection = 2;
+ input = CC_ParseToken( input );
+ Assert( !stricmp( com_token, ":" ) );
+ return true;
+ }
+
+ return false;
+}
+
+
+// parse until } found
+// public:, private:, protected: set protection mode, private is initial default
+// if token is not one of those, then parse and concatenate all tokens up to the first
+// ; or (
+//
+
+char *CClass::ParseClassDeclaration( char *input )
+{
+ int nestcount = 1;
+
+ // public = 0, protected = 1, private = 2;
+ int protection = 2;
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( com_token[ 1 ] == 0 )
+ {
+ if ( com_token[ 0 ] == '{' )
+ {
+ nestcount++;
+ }
+ else if ( com_token[ 0 ] == '}' )
+ {
+ nestcount--;
+ }
+ }
+
+ if ( ParseProtection( input, protection ) )
+ continue;
+
+ if ( !stricmp( com_token, ";" ) )
+ continue;
+
+ if ( com_token[0] == '#' )
+ {
+ // swallow rest of line
+ input = CC_ParseUntilEndOfLine( input );
+ continue;
+ }
+
+ if ( ParseNestedClass( input ) )
+ continue;
+
+ if ( nestcount == 1 )
+ {
+ // See if we found a line that describes the base class
+ if ( ParseBaseClass( input ) )
+ continue;
+
+ ParseClassMember( input, protection );
+ }
+
+ } while ( nestcount != 0 && ( strlen( com_token ) >= 0 ) );
+
+ return input;
+}
+
+static bool ShouldHungarianCheck( char const *name )
+{
+ if ( !Q_strncmp( name, "m_", 2 ) ||
+ !Q_strncmp( name, "g_", 2 ) ||
+ !Q_strncmp( name, "s_", 2 ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+enum Required
+{
+ NEVER = 0,
+ ALWAYS
+};
+
+struct Impermissible
+{
+ char const *prefix;
+ char const *mustinclude;
+ int required; // if true, then must match to be permitted
+};
+
+static Impermissible g_Permissibles[] =
+{
+ { "fl", "float", ALWAYS },
+ { "b", "bool", ALWAYS },
+ { "n", "int", ALWAYS },
+ { "isz", "string_t", ALWAYS },
+ { "i", "float", NEVER },
+ { "i", "bool", NEVER },
+ { "i", "short", NEVER },
+ { "i", "long", NEVER },
+ { "ui", "int", ALWAYS },
+ { "sz", "char", ALWAYS },
+ { "ch", "char", ALWAYS },
+ { "uch", "float", NEVER },
+ { "uch", "int", NEVER },
+ { "uch", "short", NEVER },
+ { "uch", "long", NEVER },
+ { "s", "short", ALWAYS },
+ { "us", "short", ALWAYS },
+ { "l", "long", ALWAYS },
+ { "ul", "long", ALWAYS },
+// { "f", "int", NEVER },
+// { "f", "short", NEVER },
+// { "f", "int", NEVER },
+ { "a", "UtlVector", ALWAYS },
+ { "h", "handle", ALWAYS },
+ { "p", "*", ALWAYS },
+};
+
+void CClass::CheckForHungarianErrors( int& warnings )
+{
+ int testcount = sizeof( g_Permissibles ) / sizeof( g_Permissibles[ 0 ] );
+
+ for ( int i = 0; i < m_nVarCount; i++ )
+ {
+ CClassVariable *var = m_Variables[ i ];
+
+ // Only check m_, s_, and g_ variables for now
+ if ( !ShouldHungarianCheck( var->m_szName ) )
+ {
+ continue;
+ }
+
+ bool isstatic = false;
+ char *p = var->m_szType;
+ while ( 1 )
+ {
+ p = CC_ParseToken( p );
+ if ( strlen( com_token ) <= 0 )
+ break;
+ if ( !stricmp( com_token, "static" ) )
+ {
+ isstatic = true;
+ break;
+ }
+ }
+
+ // Check for errors
+ for ( int j = 0; j < testcount; ++j )
+ {
+ Impermissible *tst = &g_Permissibles[ j ];
+
+ bool match = !Q_strncmp( var->m_szName + 2, tst->prefix, Q_strlen( tst->prefix ) ) ? true : false;
+ if ( !match )
+ continue;
+
+ // The first character after the prefix must be upper case or we skip...
+ int nextchar = 2 + Q_strlen( tst->prefix );
+
+ if ( !isupper( var->m_szName[ nextchar ] ) )
+ continue;
+
+ bool typeFound = Q_stristr( var->m_szType, tst->mustinclude ) ? true : false;
+
+ switch ( tst->required )
+ {
+ default:
+ case ALWAYS:
+ {
+ if ( !typeFound )
+ {
+ vprint( 1, "%s might have wrong type %s\n", var->m_szName, var->m_szType );
+ ++warnings;
+ }
+ else
+ {
+ return;
+ }
+ }
+ break;
+ case NEVER:
+ {
+ if ( typeFound )
+ {
+ vprint( 1, "%s might have wrong type %s\n", var->m_szName, var->m_szType );
+ ++warnings;
+ }
+ else
+ {
+ return;
+ }
+ }
+ break;
+ }
+ }
+
+
+ if ( !Q_strncmp( var->m_szName, "m_f", 3 ) &&
+ Q_strncmp( var->m_szName, "m_fl", 4 ) && isupper( var->m_szName[3] ) )
+ {
+ // If it's a "flag" and not a "float" type, it better be a bool or an int
+ if ( !Q_stristr( var->m_szType, "bool" ) &&
+ !Q_strstr( var->m_szType, "int" ) )
+ {
+ vprint( 1, "%s might have wrong type %s\n", var->m_szName, var->m_szType );
+ ++warnings;
+ return;
+ }
+ }
+ }
+
+}
diff --git a/utils/classcheck/class.h b/utils/classcheck/class.h
new file mode 100644
index 0000000..ff1935c
--- /dev/null
+++ b/utils/classcheck/class.h
@@ -0,0 +1,196 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( CLASS_H )
+#define CLASS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+class CTypeDescriptionField
+{
+public:
+ CTypeDescriptionField()
+ {
+ m_szVariableName[ 0 ] = 0;
+ m_szType[ 0 ] = 0;
+ m_szDefineType[ 0 ] = 0;
+ m_bCommentedOut = false;
+ m_bRepresentedInRecvTable = false;
+ }
+
+ char m_szVariableName[ 128 ];
+ char m_szType[ 128 ];
+ char m_szDefineType[ 128 ];
+ bool m_bCommentedOut;
+ bool m_bRepresentedInRecvTable;
+};
+
+class CClassVariable
+{
+public:
+ CClassVariable()
+ {
+ m_szName[ 0 ] = 0;
+ m_szType[ 0 ] = 0;
+
+ m_Type = TPUBLIC;
+
+ m_bKnownType = false;
+ m_nTypeSize = 0;
+
+ m_bIsArray = false;
+ m_szArraySize[ 0 ] = 0;
+
+ m_bInRecvTable = false;
+
+ m_TypeSize = 0;
+ }
+
+ typedef enum
+ {
+ TPUBLIC = 0,
+ TPROTECTED,
+ TPRIVATE
+ } VARTYPE;
+
+ char m_szName[ 128 ];
+ char m_szType[ 128 ];
+
+ VARTYPE m_Type;
+
+ bool m_bKnownType;
+ int m_nTypeSize;
+
+ bool m_bIsArray;
+ char m_szArraySize[ 128 ];
+
+ bool m_bInRecvTable;
+
+ int m_TypeSize;
+};
+
+class CClassMemberFunction
+{
+public:
+ typedef enum
+ {
+ TPUBLIC = 0,
+ TPROTECTED,
+ TPRIVATE
+ } MEMBERTYPE;
+
+ char m_szName[ 128 ];
+ // Return type
+ char m_szType[ 128 ];
+
+ MEMBERTYPE m_Type;
+
+};
+
+class CClassTypedef
+{
+public:
+ char m_szTypeName[ 128 ];
+ char m_szAlias[ 128 ];
+
+// bool m_bIsTypedefForBaseClass;
+};
+
+class CClass
+{
+public:
+ enum
+ {
+ MAX_VARIABLES = 1024,
+ MAX_MEMBERS = 1024,
+ MAX_TDFIELDS = 1024,
+ };
+
+ CClass( const char *name );
+ ~CClass( void );
+
+ char *ParseClassDeclaration( char *input );
+
+
+ void SetBaseClass( const char *name );
+ void CheckChildOfBaseEntity( const char *baseentityclass );
+ bool CheckForMissingTypeDescriptionFields( int& missingcount, bool createtds = false );
+ bool CheckForMissingPredictionFields( int& missingcount, bool createtds = false );
+ bool CheckForPredictionFieldsInRecvTableNotMarkedAsSuchCorrectly( int& missingcount );
+ void AddVariable( int protection, char *type, char *name, bool array, char *arraysize = 0 );
+
+ // Parsing helper methods
+ bool ParseProtection( char *&input, int &protection );
+ bool ParseNestedClass( char *&input );
+ bool ParseBaseClass( char *&input );
+ bool ParseClassMember( char *&input, int protection );
+ bool ParseNetworkVar( char *&input, int protection );
+ void ReportTypeMismatches( CClassVariable *var, CTypeDescriptionField *td );
+
+ void CheckForHungarianErrors( int& warnings );
+
+ char m_szName[ 128 ];
+ char m_szBaseClass[ 128 ];
+ char m_szTypedefBaseClass[ 128 ];
+
+ CClassVariable *FindVar( const char *name, bool checkbaseclasses = false );
+ CClassVariable *AddVar( const char *name );
+ int m_nVarCount;
+ CClassVariable *m_Variables[ MAX_VARIABLES ];
+
+ CClassMemberFunction *FindMember( const char *name );
+ CClassMemberFunction *AddMember( const char *name );
+ int m_nMemberCount;
+ CClassMemberFunction *m_Members[ MAX_MEMBERS ];
+
+ CTypeDescriptionField *FindTD( const char *name );
+ CTypeDescriptionField *AddTD( const char *name, const char *type, const char *definetype, bool incomments );
+ int m_nTDCount;
+ CTypeDescriptionField *m_TDFields[ MAX_TDFIELDS ];
+
+ CTypeDescriptionField *FindPredTD( const char *name );
+ CTypeDescriptionField *AddPredTD( const char *name, const char *type, const char *definetype, bool incomments, bool inrecvtable );
+ int m_nPredTDCount;
+ CTypeDescriptionField *m_PredTDFields[ MAX_TDFIELDS ];
+
+
+ CClass *m_pBaseClass;
+
+ CClass *m_pNext;
+
+ bool m_bDerivedFromCBaseEntity;
+ bool m_bHasSaveRestoreData;
+ bool m_bHasPredictionData;
+ bool m_bHasRecvTableData;
+ bool m_bConstructPredictableCalled;
+
+ int m_nClassDataSize;
+
+private:
+ struct MemberVarParse_t
+ {
+ char m_pType[256];
+ char m_pTypeModifier[256];
+ char m_pName[256];
+ char m_pArraySize[ 128 ];
+ bool m_bArray;
+
+ MemberVarParse_t() { Reset(); }
+
+ void Reset()
+ {
+ m_pType[0] = 0;
+ m_pTypeModifier[0] = 0;
+ m_pName[0] = 0;
+ m_pArraySize[0] = 0;
+ m_bArray = false;
+ }
+ };
+};
+
+#endif // CLASS_H \ No newline at end of file
diff --git a/utils/classcheck/classcheck.cpp b/utils/classcheck/classcheck.cpp
new file mode 100644
index 0000000..4326065
--- /dev/null
+++ b/utils/classcheck/classcheck.cpp
@@ -0,0 +1,216 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "stdafx.h"
+#include <stdio.h>
+#include <windows.h>
+#include "classcheck_util.h"
+#include "icodeprocessor.h"
+#include "tier1/strtools.h"
+#include "tier0/dbg.h"
+
+
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ printf( "%s", pMsg );
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ return SPEW_CONTINUE;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void printusage( void )
+{
+ vprint( 0, "usage: classcheck -q -h -m -t <root source directory> <game: hl2 | tf2>\n\
+ \t-q = quiet\n\
+ \t-h = print class hierarchy\n\
+ \t-m = don't print member functions/variables of class\n\
+ \t-t = don't print type description errors\n\
+ \t-p = don't print prediction description errors\n\
+ \t-c = create missing save descriptions\n\
+ \t-x = create missing prediction descriptions\n\
+ \t-i = specify specific input file to parse\n\
+ \t-b = similar to -i, allows specifying files outside client\\server directories\n\
+ \t-j = check for Crazy Jay Stelly's mismatched Hungarian notation errors\n\
+ \t-l = log to file log.txt\n" );
+
+ // Exit app
+ exit( 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *sourcetreebase -
+// *subdir -
+// *baseentityclass -
+//-----------------------------------------------------------------------------
+void ProcessDirectory( const char *game, const char *sourcetreebase, const char *subdir, const char *baseentityclass )
+{
+ char rootdirectory[ 256 ];
+ sprintf( rootdirectory, "%s\\%s", sourcetreebase, subdir );
+
+ // check for existence
+ if ( COM_DirectoryExists( rootdirectory ) )
+ {
+ processor->Process( baseentityclass, game, sourcetreebase, subdir );
+ }
+ else
+ {
+ vprint( 0, "Couldn't find directory %s, check path %s\n", rootdirectory, sourcetreebase );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *sourcetreebase -
+// *subdir -
+// *baseentityclass -
+//-----------------------------------------------------------------------------
+void ProcessFile( const char *game, const char *sourcetreebase, const char *subdir, const char *baseentityclass, const char *pFileName )
+{
+ char rootdirectory[ 256 ];
+ sprintf( rootdirectory, "%s\\%s", sourcetreebase, subdir );
+
+ // check for existence
+ if ( COM_DirectoryExists( rootdirectory ) )
+ {
+ processor->Process( baseentityclass, game, sourcetreebase, subdir, pFileName );
+ }
+ else
+ {
+ vprint( 0, "Couldn't find directory %s, check path %s\n", rootdirectory, sourcetreebase );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckLogFile( void )
+{
+ if ( processor->GetLogFile() )
+ {
+ _unlink( "log.txt" );
+ vprint( 0, " Outputting to log.txt\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : argc -
+// argv[] -
+// Output : int
+//-----------------------------------------------------------------------------
+int main( int argc, char* argv[] )
+{
+ SpewOutputFunc( SpewFunc );
+
+ vprint( 0, "Valve Software - classcheck.exe (%s)\n", __DATE__ );
+ vprint( 0, "--- Game Code Static Analysis ---\n" );
+ char *pSpecificFile = NULL;
+ bool bOutsideGamedir = false;
+
+ int i = 1;
+ for ( i ; i<argc ; i++)
+ {
+ if ( argv[ i ][ 0 ] == '-' )
+ {
+ switch( argv[ i ][ 1 ] )
+ {
+ case 'q':
+ processor->SetQuiet( true );
+ break;
+ case 'h':
+ processor->SetPrintHierarchy( true );
+ break;
+ case 'm':
+ processor->SetPrintMembers( false );
+ break;
+ case 't':
+ processor->SetPrintTDs( false );
+ break;
+ case 'p':
+ processor->SetPrintPredTDs( false );
+ break;
+ case 'c':
+ processor->SetPrintCreateMissingTDs( true );
+ break;
+ case 'x':
+ processor->SetPrintCreateMissingPredTDs( true );
+ break;
+ case 'i':
+ if (i < argc-1)
+ {
+ pSpecificFile = argv[i+1];
+ ++i;
+ }
+ break;
+ case 'b':
+ if (i < argc-1)
+ {
+ pSpecificFile = argv[i+1];
+ bOutsideGamedir = true;
+ ++i;
+ }
+ break;
+ case 'l':
+ processor->SetLogFile( true );
+ break;
+ case 'j':
+ processor->SetCheckHungarian( true );
+ break;
+ default:
+ printusage();
+ break;
+ }
+ }
+ }
+
+ if ( argc < 3 || ( i != argc ) )
+ {
+ printusage();
+ }
+
+ CheckLogFile();
+
+ vprint( 0, " Looking for obvious screwups and boneheaded mistakes...\n" );
+
+ char sourcetreebase[ 256 ];
+ strcpy( sourcetreebase, argv[i-2] );
+
+ Q_StripTrailingSlash( sourcetreebase );
+
+ if ( !pSpecificFile )
+ {
+ ProcessDirectory( argv[ i-1 ], sourcetreebase, "server", "CBaseEntity" );
+ ProcessDirectory( argv[ i-1 ], sourcetreebase, "client", "C_BaseEntity" );
+ }
+ else
+ {
+ if ( bOutsideGamedir )
+ {
+ ProcessFile( argv[ i-1 ], sourcetreebase, "", "", pSpecificFile );
+ }
+ else
+ {
+ ProcessFile( argv[ i-1 ], sourcetreebase, "server", "CBaseEntity", pSpecificFile );
+ ProcessFile( argv[ i-1 ], sourcetreebase, "client", "C_BaseEntity", pSpecificFile );
+ }
+ }
+
+ return 0;
+}
diff --git a/utils/classcheck/classcheck.vpc b/utils/classcheck/classcheck.vpc
new file mode 100644
index 0000000..9019f56
--- /dev/null
+++ b/utils/classcheck/classcheck.vpc
@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------------
+// CLASSCHECK.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,..\common"
+ }
+}
+
+$Project "Classcheck"
+{
+ $Folder "Source Files"
+ {
+ $File "class.cpp"
+ $File "classcheck.cpp"
+ $File "classcheck_util.cpp"
+ $File "processmodule.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "class.h"
+ $File "classcheck_util.h"
+ $File "codeprocessor.h"
+ $File "icodeprocessor.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ }
+}
diff --git a/utils/classcheck/classcheck_util.cpp b/utils/classcheck/classcheck_util.cpp
new file mode 100644
index 0000000..e8f4838
--- /dev/null
+++ b/utils/classcheck/classcheck_util.cpp
@@ -0,0 +1,369 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "stdafx.h"
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+#include "classcheck_util.h"
+#include "icodeprocessor.h"
+#include "tier0/dbg.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : depth -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void vprint( int depth, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+
+ if ( processor->GetLogFile() )
+ {
+ fp = fopen( "log.txt", "ab" );
+ }
+
+ while ( depth-- > 0 )
+ {
+ printf( " " );
+ OutputDebugString( " " );
+ if ( fp )
+ {
+ fprintf( fp, " " );
+ }
+ }
+
+ ::printf( "%s", string );
+ OutputDebugString( string );
+
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+
+bool com_ignorecolons = false; // YWB: Ignore colons as token separators in COM_Parse
+bool com_ignoreinlinecomment = false;
+static bool s_com_token_unget = false;
+char com_token[1024];
+int linesprocessed = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CC_UngetToken( void )
+{
+ s_com_token_unget = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ch -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CC_IsBreakChar( char ch )
+{
+ bool brk = false;
+ switch ( ch )
+ {
+ case '{':
+ case '}':
+ case ')':
+ case '(':
+ case '[':
+ case ']':
+ case '\'':
+ case '/':
+ case ',':
+ case ';':
+ case '<':
+ case '>':
+ brk = true;
+ break;
+
+ case ':':
+ if ( !com_ignorecolons )
+ brk = true;
+ break;
+ default:
+ break;
+ }
+ return brk;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *data -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_ParseToken(char *data)
+{
+ int c;
+ int len;
+
+ if ( s_com_token_unget )
+ {
+ s_com_token_unget = false;
+ return data;
+ }
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ return NULL; // end of file;
+ if ( c== '\n' )
+ {
+ linesprocessed++;
+ }
+ data++;
+ }
+
+// skip // comments
+ if ( !com_ignoreinlinecomment )
+ {
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+ }
+
+ if ( c == '/' && data[1] == '*' )
+ {
+ while (data[0] && data[1] && !( data[0] == '*' && data[1] == '/' ) )
+ {
+ if ( *data == '\n' )
+ {
+ linesprocessed++;
+ }
+ data++;
+ }
+
+ if ( data[0] == '*' && data[1] == '/' )
+ {
+ data+=2;
+ }
+ goto skipwhite;
+ }
+
+// handle quoted strings specially
+ bool isLstring = data[0] == 'L' && (data[1] == '\"' );
+ if ( isLstring )
+ {
+ com_token[len++] = (char)c;
+ return data+1;
+ }
+
+ if ( c == '\"' )
+ {
+ data++;
+ bool bEscapeSequence = false;
+ while (1)
+ {
+ Assert( len < 1024 );
+ if ( len >= 1024 )
+ {
+ com_token[ len -1 ] = 0;
+ return data;
+ }
+
+ c = *data++;
+ if ( (c=='\"' && !bEscapeSequence) || !c && len < sizeof( com_token ) - 1 )
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ bEscapeSequence = ( c == '\\' );
+ com_token[len] = (char)c;
+ len++;
+ }
+ }
+
+// parse single characters
+ if ( CC_IsBreakChar( (char)c ) )
+ {
+ Assert( len < 1024 );
+
+ com_token[len] = (char)c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ Assert( len < 1024 );
+
+ com_token[len] = (char)c;
+ data++;
+ len++;
+ c = *data;
+
+ if ( CC_IsBreakChar( (char)c ) )
+ break;
+ } while (c>32 && len < sizeof( com_token ) - 1);
+
+ com_token[len] = 0;
+ return data;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// *len -
+// Output : unsigned char
+//-----------------------------------------------------------------------------
+unsigned char *COM_LoadFile( const char *name, int *len)
+{
+ FILE *fp;
+ fp = fopen( name, "rb" );
+ if ( !fp )
+ {
+ *len = 0;
+ return NULL;
+ }
+
+ fseek( fp, 0, SEEK_END );
+ *len = ftell( fp );
+ fseek( fp, 0, SEEK_SET );
+
+ unsigned char *buffer = new unsigned char[ *len + 1 ];
+ fread( buffer, *len, 1, fp );
+ fclose( fp );
+ buffer[ *len ] = 0;
+
+ return buffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *buffer -
+//-----------------------------------------------------------------------------
+void COM_FreeFile( unsigned char *buffer )
+{
+ delete[] buffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *dir -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool COM_DirectoryExists( const char *dir )
+{
+ if ( !_access( dir, 0 ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_ParseUntilEndOfLine( char *input )
+{
+ while (*input && *input != '\n')
+ input++;
+
+ return input;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// *ch -
+// *breakchar -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_RawParseChar( char *input, const char *ch, char *breakchar )
+{
+ bool done = false;
+ int listlen = strlen( ch );
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( strlen( com_token ) == 1 )
+ {
+ for ( int i = 0; i < listlen; i++ )
+ {
+ if ( com_token[ 0 ] == ch[ i ] )
+ {
+ *breakchar = ch [ i ];
+ done = true;
+ break;
+ }
+ }
+ }
+ } while ( !done );
+
+ return input;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// *pairing -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_DiscardUntilMatchingCharIncludingNesting( char *input, const char *pairing )
+{
+ int nestcount = 1;
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( strlen( com_token ) == 1 )
+ {
+ if ( com_token[ 0 ] == pairing[ 0 ] )
+ {
+ nestcount++;
+ }
+ else if ( com_token[ 0 ] == pairing[ 1 ] )
+ {
+ nestcount--;
+ }
+ }
+ } while ( nestcount != 0 );
+
+ return input;
+} \ No newline at end of file
diff --git a/utils/classcheck/classcheck_util.h b/utils/classcheck/classcheck_util.h
new file mode 100644
index 0000000..cc95225
--- /dev/null
+++ b/utils/classcheck/classcheck_util.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CLASSCHECK_UTIL_H
+#define CLASSCHECK_UTIL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void vprint( int depth, const char *fmt, ... );
+void CC_UngetToken( void );
+char *CC_ParseToken(char *data);
+char *CC_DiscardUntilMatchingCharIncludingNesting( char *input, const char *pairing );
+char *CC_RawParseChar( char *input, const char *ch, char *breakchar );
+char *CC_ParseUntilEndOfLine( char *input );
+
+extern char com_token[1024];
+extern bool com_ignoreinlinecomment;
+extern int linesprocessed;
+
+unsigned char *COM_LoadFile( const char *name, int *len);
+void COM_FreeFile( unsigned char *buffer );
+bool COM_DirectoryExists( const char *dir );
+
+#endif // CLASSCHECK_UTIL_H
diff --git a/utils/classcheck/codeprocessor.h b/utils/classcheck/codeprocessor.h
new file mode 100644
index 0000000..d50b8d2
--- /dev/null
+++ b/utils/classcheck/codeprocessor.h
@@ -0,0 +1,129 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( CODEPROCESSOR_H )
+#define CODEPROCESSOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "class.h"
+#include "icodeprocessor.h"
+
+#include "utldict.h"
+
+class CCodeProcessor : public ICodeProcessor
+{
+public:
+ CCodeProcessor( void );
+ ~CCodeProcessor( void );
+
+ void Process( const char *baseentityclass, const char *gamespecific, const char *sourcetreebase, const char *subdir );
+ void Process( const char *baseentityclass, const char *gamespecific, const char *sourcetreebase, const char *subdir, const char *pFileName );
+ CClass *AddClass( const char *classname );
+
+ void SetQuiet( bool quiet );
+ bool GetQuiet( void ) const;
+
+ void SetPrintHierarchy( bool print );
+ bool GetPrintHierarchy( void ) const;
+
+ void SetPrintMembers( bool print );
+ bool GetPrintMembers( void ) const;
+
+ void SetPrintTDs( bool print );
+ bool GetPrintTDs( void ) const;
+
+ void SetPrintPredTDs( bool print );
+ bool GetPrintPredTDs( void ) const;
+
+ void SetPrintCreateMissingTDs( bool print );
+ bool GetPrintCreateMissingTDs( void ) const;
+
+ void SetPrintCreateMissingPredTDs( bool print );
+ bool GetPrintCreateMissingPredTDs( void ) const;
+
+ void SetLogFile( bool log );
+ bool GetLogFile( void ) const;
+
+ void SetCheckHungarian( bool check );
+ bool GetCheckHungarian() const;
+
+ CClass *FindClass( const char *name ) const;
+
+ void ResolveBaseClasses( const char *baseentityclass );
+ void Clear( void );
+ void PrintClassList( void ) const;
+ void PrintMissingTDFields( void ) const;
+ void ReportHungarianNotationErrors();
+
+ int Count( void ) const;
+
+ void SortClassList( void );
+
+private:
+ char *ParseTypeDescription( char *current, bool fIsMacroized );
+ char *ParsePredictionTypeDescription( char *current );
+ char *ParseReceiveTable( char *current );
+
+ void ProcessModule( bool forcequiet, int depth, int& maxdepth, int& numheaders, int& skippedfiles,
+ const char *srcroot, const char *baseroot, const char *root, const char *module );
+ void ProcessModules( const char *srcroot, const char *root, const char *rootmodule );
+ void PrintResults( const char *baseentityclass );
+ void ConstructModuleList_R( int level, const char *baseentityclass, const char *gamespecific, const char *root, char const *srcroot );
+
+ void AddHeader( int depth, const char *filename, const char *rootmodule );
+
+ bool CheckShouldSkip( bool forcequiet, int depth, char const *filename, int& numheaders, int& skippedfiles);
+ bool 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 );
+
+ // include file path
+ void CleanupIncludePath();
+ void AddIncludePath( const char *pPath );
+ void SetupIncludePath( const char *sourcetreebase, const char *subdir, const char *gamespecific );
+
+ CClass *m_pClassList;
+
+ typedef struct
+ {
+ bool skipped;
+ } CODE_MODULE;
+
+ CUtlDict< CODE_MODULE, int > m_Modules;
+ CUtlDict< CODE_MODULE, int > m_Headers;
+
+ CUtlVector< char * > m_IncludePath;
+
+ bool m_bQuiet;
+ bool m_bPrintHierarchy;
+ bool m_bPrintMembers;
+ bool m_bPrintTypedescriptionErrors;
+ bool m_bPrintPredictionDescErrors;
+ bool m_bCreateMissingTDs;
+ bool m_bCreateMissingPredTDs;
+ bool m_bLogToFile;
+ bool m_bCheckHungarian;
+
+ int m_nFilesProcessed;
+ int m_nHeadersProcessed;
+ int m_nClassesParsed;
+ int m_nOffset;
+ int m_nBytesProcessed;
+ int m_nLinesOfCode;
+
+ double m_flStart;
+ double m_flEnd;
+
+ char m_szCurrentCPP[ 128 ];
+ char m_szBaseEntityClass[ 256 ];
+
+};
+
+extern ICodeProcessor *processor;
+
+#endif // CODEPROCESSOR_H \ No newline at end of file
diff --git a/utils/classcheck/icodeprocessor.h b/utils/classcheck/icodeprocessor.h
new file mode 100644
index 0000000..b110c1a
--- /dev/null
+++ b/utils/classcheck/icodeprocessor.h
@@ -0,0 +1,59 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( ICODEPROCESSOR_H )
+#define ICODEPROCESSOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#pragma warning( disable : 4127 )
+
+class CClass;
+
+class ICodeProcessor
+{
+public:
+ // Process all files in a directory
+ virtual void Process( const char *baseentityclass, const char *gamespecific, const char *sourcetreebase, const char *subdir ) = 0;
+
+ // Process a single file
+ virtual void Process( const char *baseentityclass, const char *gamespecific, const char *sourcetreebase, const char *subdir, const char *pFileName ) = 0;
+
+ virtual CClass *AddClass( const char *classname ) = 0;
+ virtual CClass *FindClass( const char *name ) const = 0;
+
+ virtual void SetQuiet( bool quiet ) = 0;
+ virtual bool GetQuiet( void ) const = 0;
+
+ virtual void SetPrintHierarchy( bool print ) = 0;
+ virtual bool GetPrintHierarchy( void ) const = 0;
+
+ virtual void SetPrintMembers( bool print ) = 0;
+ virtual bool GetPrintMembers( void ) const = 0;
+
+ virtual void SetPrintTDs( bool print ) = 0;
+ virtual bool GetPrintTDs( void ) const = 0;
+
+ virtual void SetPrintPredTDs( bool print ) = 0;
+ virtual bool GetPrintPredTDs( void ) const = 0;
+
+ virtual void SetPrintCreateMissingTDs( bool print ) = 0;
+ virtual bool GetPrintCreateMissingTDs( void ) const = 0;
+
+ virtual void SetPrintCreateMissingPredTDs( bool print ) = 0;
+ virtual bool GetPrintCreateMissingPredTDs( void ) const = 0;
+
+ virtual void SetLogFile( bool log ) = 0;
+ virtual bool GetLogFile( void ) const = 0;
+
+ virtual void SetCheckHungarian( bool check ) = 0;
+ virtual bool GetCheckHungarian() const = 0;
+};
+
+extern ICodeProcessor *processor;
+
+#endif // ICODEPROCESSOR_H \ No newline at end of file
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
diff --git a/utils/classcheck/stdafx.cpp b/utils/classcheck/stdafx.cpp
new file mode 100644
index 0000000..1fdf9da
--- /dev/null
+++ b/utils/classcheck/stdafx.cpp
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// classcheck.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/utils/classcheck/stdafx.h b/utils/classcheck/stdafx.h
new file mode 100644
index 0000000..e0ae61a
--- /dev/null
+++ b/utils/classcheck/stdafx.h
@@ -0,0 +1,26 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_)
+#define AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_)