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