summaryrefslogtreecommitdiff
path: root/utils/entcount/entcount.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/entcount/entcount.cpp')
-rw-r--r--utils/entcount/entcount.cpp452
1 files changed, 452 insertions, 0 deletions
diff --git a/utils/entcount/entcount.cpp b/utils/entcount/entcount.cpp
new file mode 100644
index 0000000..6982bed
--- /dev/null
+++ b/utils/entcount/entcount.cpp
@@ -0,0 +1,452 @@
+//========= 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;
+}