//-----------------------------------------------------------------------------
// 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;
}
//-----------------------------------------------------------------------------
// 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;
}
예제 #3
0
bool GetModelNameFromSourceFile( char const *filename, char *modelname, int maxlen )
{
	modelname[0]=0;

	int filelength;
	char *buffer = (char *)COM_LoadFile( filename, &filelength );
	if ( !buffer )
	{
		vprint( 0, "Couldn't load %s\n", filename );
		return false;
	}

	bool valid = false;

	// Parse tokens
	char *current = buffer;
	while ( current )
	{
		current = CC_ParseToken( current );
		if ( strlen( com_token ) <= 0 )
			break;

		if ( stricmp( com_token, "$modelname" ) )
			continue;

		current = CC_ParseToken( current );

		strcpy( modelname, com_token );
		_strlwr( modelname );

		Q_FixSlashes( modelname );

		Q_DefaultExtension( modelname, ".mdl", maxlen );

		valid = true;
		break;
	}

	COM_FreeFile( (unsigned char *)buffer );

	if ( !valid )
	{
		vprint( 0, ".qc file %s missing $modelname directive!!!\n", filename );
	}
	return valid;
}
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;

		// skip classname
		current = CC_ParseToken( current );
		if( stricmp( com_token, classname ) )
		{
			vprint( 0, "PREDICTION TYPEDESCRIPTION for class %s uses offset from class %s\n",
				classname, com_token );
		}
		// skip comma
		current = CC_ParseToken( current );

		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;
}
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::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" ) )
				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( CMyClass, 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;

		// skip classname
		current = CC_ParseToken( current );
		if( stricmp( com_token, classname ) )
		{
			vprint( 0, "TYPEDESCRIPTION for class %s uses offset from class %s\n",
				classname, com_token );
		}
		// skip comma
		current = CC_ParseToken( current );
		if ( !stricmp( com_token, ":" ) )
		{
			// Scoped class name here...
			current = CC_ParseToken( current );
			assert( !stricmp( com_token, ":" ) );
			current = CC_ParseToken( current );

			// skip comma
			current = CC_ParseToken( current );
		}

		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_KEYFIELD") || 
			!stricmp(definetype, "DEFINE_KEYFIELD_NOT_SAVED") || 
			!stricmp(definetype, "DEFINE_UTLVECTOR") || 
			!stricmp(definetype, "DEFINE_GLOBAL_FIELD") || 
			!stricmp(definetype, "DEFINE_GLOBAL_KEYFIELD") || 
			!stricmp(definetype, "DEFINE_INPUT") ||
			!stricmp(definetype, "DEFINE_AUTO_ARRAY") ||
			!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 );
				assert (!strcmp( com_token, "]" ));
 				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;
}
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" ) )
		{
			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::ProcessModule( bool forcequiet, int depth, int& maxdepth, int& numheaders, int& skippedfiles, const char *baseroot, const char *root, const char *module )
{
	char filename[ 256 ];

	bool checkroot = false;

	if ( depth > maxdepth )
	{
		maxdepth = depth;
	}

	// Load the base module
	sprintf( filename, "%s\\%s", root, module );
	strlwr( filename );

	bool firstheader = true;
retry:

	// Check module list
	for ( int i = 0; i < m_nModuleCount; i++ )
	{
		if ( !stricmp( m_Modules[ i ].name, filename ) )
		{
			if ( forcequiet )
			{
				m_nHeadersProcessed++;
				numheaders++;

				if ( m_Modules[ i ].skipped )
				{
					skippedfiles++;
				}
			}

			AddHeader( depth, filename, m_szCurrentCPP );

			return;
		}
	}

	int filelength;
	char *buffer = (char *)COM_LoadFile( filename, &filelength );
	if ( !buffer )
	{
		if ( !checkroot )
		{
			checkroot = true;
			// Load the base module
			sprintf( filename, "%s\\%s", baseroot, module );
			goto retry;
		}
		m_Modules[ m_nModuleCount ].skipped = true;
		strcpy( m_Modules[ m_nModuleCount++ ].name, filename );
		
		skippedfiles++;
		return;
	}

	m_nBytesProcessed += filelength;

	m_Modules[ m_nModuleCount ].skipped = false;
	strcpy( m_Modules[ m_nModuleCount++ ].name, filename );

	bool readonly = false;
	bool madechanges = false;
	CreateBackup( filename, readonly );

	if ( !forcequiet )
	{
		strcpy( m_szCurrentCPP, filename );
		
		vprint( 0, "- %s\n", (char *)&filename[ m_nOffset ] );
	}

	// Parse tokens looking for #include directives or class starts
	char *current = buffer;
	char *startofline;

	current = CC_ParseToken( current );
	while ( current )
	{
		// No more tokens
		if ( strlen( com_token ) <= 0 )
			break;

		if ( !stricmp( com_token, "#include" ) )
		{
			startofline = current - strlen( "#include" );

			current = CC_ParseToken( current );

			if ( strlen( com_token ) > 0)
			{
				vprint( 1, "#include %s", com_token );
				m_nHeadersProcessed++;
				numheaders++;				

				AddHeader( depth, filename, m_szCurrentCPP );

				bool dobuild = true;
				if ( firstheader )
				{
					if ( !stricmp( com_token, "cbase.h" ) )
					{
						dobuild = false;
					}

					if ( !TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) )
					{
						// build is broken, stop
						assert( 0 );
					}
				}

				firstheader = false;

				if ( dobuild )
				{
					// Try removing the header and compiling
					char saveinfo[2];
					memcpy( saveinfo, startofline, 2 );
					startofline[ 0 ] = '/';
					startofline[ 1 ] = '/';

					if ( TryBuild( baseroot, filename, (unsigned char *)buffer, filelength ) )
					{
						vprint( 0, ", unnecessary\n" );
						madechanges = true;
					}
					else
					{
						// Restore line
						memcpy( startofline, saveinfo, 2 );
						vprint( 0, "\n" );
					}
				}
				else
				{
					vprint( 0, "\n" );
				}
			}
		}

		current = CC_ParseToken( current );
	}

	// Save out last set of changes
	{
		FILE *fp;
		fp = fopen( filename, "wb" );
		if ( fp )
		{
			fwrite( buffer, filelength, 1, fp );
			fclose( fp );
		}
	}

	COM_FreeFile( (unsigned char *)buffer );

	if ( !madechanges )
	{
		RestoreBackup( filename, readonly );
	}

	if ( !forcequiet && !GetQuiet() )
	{
		vprint( 0, " %s: headers (%i)", (char *)&filename[ m_nOffset ], numheaders );
		if ( maxdepth > 1 )
		{
			vprint( 0, ", depth %i", maxdepth );
		}
		vprint( 0, "\n" );
	}

	m_nLinesOfCode += linesprocessed;
	linesprocessed = 0;
}