source-engine/utils/entcount/entcount.cpp

453 lines
8.3 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: searches through all bsp files in the current directory parsing out entity details
//
//=============================================================================//
#include <stdio.h>
#include <string.h>
#include <io.h>
#include <malloc.h>
#define max(x,y) ( ((x) > (y)) ? (x) : (y) )
void SetSearchWord( const char *searchWord );
char *FindSearchWord( char *buffer, char *bufend );
char *ParseToken( char *data, char *newToken );
void ClearTable( void );
void ClearUsageCountTable( void );
void AddToTable( const char *name );
void PrintOutTable( void );
void ParseFGD( char *buffer, char *bufend, const char *searchKey );
const char *g_UsageString = "usage: entcount [-fgd <fgdfile>] [-nofgd] [-permap] [-onlyent <entname>] [-files <searchmask>]\n";
int main( int argc, char *argv[] )
{
if ( argc < 2 )
{
printf( g_UsageString );
return 0;
}
bool printPerMap = false;
const char *filterEnt = NULL;
const char *fgdFile = NULL;
const char *fileMask = "*.bsp";
// parse the arguments
for ( int count = 1; count < argc; count++ )
{
if ( !stricmp( argv[count], "-permap" ) )
{
printPerMap = true;
}
else if ( !stricmp( argv[count], "-onlyent" ) )
{
count++;
if ( count < argc )
{
filterEnt = argv[count];
}
}
else if ( !stricmp( argv[count], "-fgd" ) )
{
count++;
if ( count < argc )
{
fgdFile = argv[count];
}
}
else if ( !stricmp( argv[count], "-files" ) )
{
count++;
if ( count < argc )
{
fileMask = argv[count];
}
}
else if ( !stricmp( argv[count], "-nofgd" ) )
{
}
else
{
printf( "error: unknown parameter \"%s\"\n", argv[count] );
printf( g_UsageString );
return 1;
}
}
// clear the entity accumulator table
ClearTable();
// open and parse the FGD, unless the -nofgd flag is specified
if ( fgdFile && !filterEnt && !printPerMap )
{
FILE *f = fopen( fgdFile, "rb" );
if ( !f )
{
printf( "error: could not open file %s\n", fgdFile );
return 2;
}
int filelen;
fseek( f, 0, SEEK_END );
filelen = ftell( f );
fseek( f, 0, SEEK_SET );
// allocate and load into memory
char *buffer = (char*)malloc( filelen );
char *bufend = buffer + filelen;
fread( buffer, filelen, 1, f );
fclose( f );
// search for all @pointclass, then @solidclass
ParseFGD( buffer, bufend, "PointClass" );
ParseFGD( buffer, bufend, "SolidClass" );
// reset the usage counts to 0
ClearUsageCountTable();
free( buffer );
}
// parse through all the bsp files
_finddata_t fileinfo;
int FHandle = _findfirst( fileMask, &fileinfo );
if ( FHandle == -1 )
{
printf( "error: no files found in current directory\n" );
return 1;
}
SetSearchWord( "\"classname\"" );
do {
// open the file
FILE *f = fopen( fileinfo.name, "rb" );
if ( !f )
{
printf( "error: couldn't open file %s\n", fileinfo.name );
return 2;
}
// calculate file length
int filelen;
fseek( f, 0, SEEK_END );
filelen = ftell( f );
fseek( f, 0, SEEK_SET );
// allocate and load into memory
char *buffer = (char*)malloc( filelen );
char *bufpos = buffer + strlen( "\"classname\"" ) - 1;
char *bufend = buffer + filelen;
fread( buffer, filelen, 1, f );
fclose( f );
bool entFound = false;
while ( 1 )
{
bufpos = FindSearchWord( bufpos, bufend );
if ( !bufpos )
break;
// find the next word
static char Token[256];
ParseToken( bufpos, Token );
// add the word to the list, filtering if necessary
if ( !filterEnt || !stricmp(filterEnt, Token) )
{
AddToTable( Token );
entFound = true;
}
bufpos += strlen( Token );
}
free( buffer );
// print the bsp name, if an ent is found, or we are not filtering for ents
if ( entFound || !filterEnt )
printf( "%s\n", fileinfo.name );
if ( printPerMap )
{
PrintOutTable();
ClearUsageCountTable();
}
} while ( _findnext(FHandle, &fileinfo) == 0 );
PrintOutTable();
return 0;
}
void ParseFGD( char *buffer, char *bufend, const char *searchKey )
{
char *bufpos = buffer + strlen( searchKey ) - 1;
SetSearchWord( searchKey );
while ( 1 )
{
bufpos = FindSearchWord( bufpos, bufend );
if ( !bufpos )
break;
// search for the corresponding '='
while ( *bufpos != '=' )
bufpos++;
bufpos++;
// find the classname
static char Token[256];
ParseToken( bufpos, Token );
AddToTable( Token );
bufpos += strlen( Token );
}
}
/////////// entity table stuff //////////////
const int MAX_ENTS = 2000;
int NumEnts = 0;
char *EntNames[ MAX_ENTS ];
int EntUsage[ MAX_ENTS ];
void ClearTable( void )
{
memset( EntNames, 0, sizeof(EntNames) );
memset( EntUsage, 0, sizeof(EntUsage) );
}
void ClearUsageCountTable( void )
{
memset( EntUsage, 0, sizeof(EntUsage) );
}
void AddToTable( const char *name )
{
// search for it in the table
for ( int i = 0; i < NumEnts; i++ )
{
if ( EntNames[i] && !strcmp(EntNames[i], name) )
{
// it's already in the table
// increment the usage count
EntUsage[i] += 1;
return;
}
}
// append to the table
EntNames[NumEnts] = (char*)malloc( strlen(name) + 1 );
strcpy( EntNames[NumEnts], name );
EntUsage[NumEnts] = 1;
NumEnts++;
}
void PrintOutTable( void )
{
while ( 1 )
{
// find the highest item
int highestUsage = -1;
int highestEnt = 0;
for ( int i = 0; i < NumEnts; i++ )
{
if ( EntNames[i] && highestUsage < EntUsage[i] )
{
highestUsage = EntUsage[i];
highestEnt = i;
}
}
// check for no more ents
if ( highestUsage == -1 )
return;
// display usage stats of item
printf( " %5d %s\n", highestUsage, EntNames[highestEnt] );
// remove item from list
free( EntNames[highestEnt] );
EntNames[highestEnt] = NULL;
}
}
////////// string search stuff ////////////
static unsigned char JumpTable[256];
static int SearchWordLen = 0;
static const char *SearchWord;
void SetSearchWord( const char *Word )
{
SearchWord = Word;
SearchWordLen = strlen( SearchWord );
// build the jump table
// initialize all values to jump the length of the string
memset( JumpTable, SearchWordLen, sizeof(JumpTable) );
// set the amount the searcher can jump forward, depending on the character
for ( int i = 0; i < SearchWordLen; i++ )
{
JumpTable[ (unsigned char)SearchWord[i] ] = max( SearchWordLen - i - 1, 1 );
}
}
char *FindSearchWord( char *buffer, char *bufend )
{
/*
for ( int i = SearchWordLen-1; i >= 0; i-- )
{
if ( *buffer != SearchWord[i] )
{
buffer += ( JumpTable[ (unsigned char)(*(buffer + i - SearchWordLen + 1)) ] - 1 );
// no more buffer to search
if ( buffer >= bufend )
return NULL;
// reset search counter
i = SearchWordLen;
}
else
{
// it's a match, move backwards to search
buffer--;
}
}
*/
while ( 1 )
{
if ( strnicmp(buffer - SearchWordLen, SearchWord, SearchWordLen) )
{
// strings not equal, jump ahead
buffer += JumpTable[ (unsigned char)*buffer ];
if ( buffer >= bufend )
return NULL;
}
else
{
break;
}
}
// we have a match!
// return a pointer just past the found key
return buffer;
}
/*
==============
ParseToken
Parse a token out of a string
outputs the parsed token into newToken
==============
*/
char *ParseToken( char *data, char *newToken )
{
int c;
int len;
len = 0;
newToken[0] = 0;
if (!data)
return NULL;
// skip whitespace
skipwhite:
while ( (c = *data) <= ' ')
{
if (c == 0)
return NULL; // end of file;
data++;
}
// skip // comments
if (c=='/' && data[1] == '/')
{
while (*data && *data != '\n')
data++;
goto skipwhite;
}
// handle quoted strings specially
if (c == '\"')
{
data++;
while ( len < 256 )
{
c = *data++;
if (c=='\"' || !c)
{
newToken[len] = 0;
return data;
}
newToken[len] = c;
len++;
}
if ( len >= 256 )
{
len--;
newToken[len] = 0;
}
}
// parse single characters
if ( c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' )
{
newToken[len] = c;
len++;
newToken[len] = 0;
return data+1;
}
// parse a regular word
do
{
newToken[len] = c;
data++;
len++;
c = *data;
if ( c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' )
break;
if ( len >= 256 )
{
len--;
newToken[len] = 0;
break;
}
} while (c>32);
newToken[len] = 0;
return data;
}