Team Fortress 2 Source Code as on 22/4/2020
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

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: searches through all bsp files in the current directory parsing out entity details
  4. //
  5. //=============================================================================//
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include <io.h>
  9. #include <malloc.h>
  10. #define max(x,y) ( ((x) > (y)) ? (x) : (y) )
  11. void SetSearchWord( const char *searchWord );
  12. char *FindSearchWord( char *buffer, char *bufend );
  13. char *ParseToken( char *data, char *newToken );
  14. void ClearTable( void );
  15. void ClearUsageCountTable( void );
  16. void AddToTable( const char *name );
  17. void PrintOutTable( void );
  18. void ParseFGD( char *buffer, char *bufend, const char *searchKey );
  19. const char *g_UsageString = "usage: entcount [-fgd <fgdfile>] [-nofgd] [-permap] [-onlyent <entname>] [-files <searchmask>]\n";
  20. int main( int argc, char *argv[] )
  21. {
  22. if ( argc < 2 )
  23. {
  24. printf( g_UsageString );
  25. return 0;
  26. }
  27. bool printPerMap = false;
  28. const char *filterEnt = NULL;
  29. const char *fgdFile = NULL;
  30. const char *fileMask = "*.bsp";
  31. // parse the arguments
  32. for ( int count = 1; count < argc; count++ )
  33. {
  34. if ( !stricmp( argv[count], "-permap" ) )
  35. {
  36. printPerMap = true;
  37. }
  38. else if ( !stricmp( argv[count], "-onlyent" ) )
  39. {
  40. count++;
  41. if ( count < argc )
  42. {
  43. filterEnt = argv[count];
  44. }
  45. }
  46. else if ( !stricmp( argv[count], "-fgd" ) )
  47. {
  48. count++;
  49. if ( count < argc )
  50. {
  51. fgdFile = argv[count];
  52. }
  53. }
  54. else if ( !stricmp( argv[count], "-files" ) )
  55. {
  56. count++;
  57. if ( count < argc )
  58. {
  59. fileMask = argv[count];
  60. }
  61. }
  62. else if ( !stricmp( argv[count], "-nofgd" ) )
  63. {
  64. }
  65. else
  66. {
  67. printf( "error: unknown parameter \"%s\"\n", argv[count] );
  68. printf( g_UsageString );
  69. return 1;
  70. }
  71. }
  72. // clear the entity accumulator table
  73. ClearTable();
  74. // open and parse the FGD, unless the -nofgd flag is specified
  75. if ( fgdFile && !filterEnt && !printPerMap )
  76. {
  77. FILE *f = fopen( fgdFile, "rb" );
  78. if ( !f )
  79. {
  80. printf( "error: could not open file %s\n", fgdFile );
  81. return 2;
  82. }
  83. int filelen;
  84. fseek( f, 0, SEEK_END );
  85. filelen = ftell( f );
  86. fseek( f, 0, SEEK_SET );
  87. // allocate and load into memory
  88. char *buffer = (char*)malloc( filelen );
  89. char *bufend = buffer + filelen;
  90. fread( buffer, filelen, 1, f );
  91. fclose( f );
  92. // search for all @pointclass, then @solidclass
  93. ParseFGD( buffer, bufend, "PointClass" );
  94. ParseFGD( buffer, bufend, "SolidClass" );
  95. // reset the usage counts to 0
  96. ClearUsageCountTable();
  97. free( buffer );
  98. }
  99. // parse through all the bsp files
  100. _finddata_t fileinfo;
  101. int FHandle = _findfirst( fileMask, &fileinfo );
  102. if ( FHandle == -1 )
  103. {
  104. printf( "error: no files found in current directory\n" );
  105. return 1;
  106. }
  107. SetSearchWord( "\"classname\"" );
  108. do {
  109. // open the file
  110. FILE *f = fopen( fileinfo.name, "rb" );
  111. if ( !f )
  112. {
  113. printf( "error: couldn't open file %s\n", fileinfo.name );
  114. return 2;
  115. }
  116. // calculate file length
  117. int filelen;
  118. fseek( f, 0, SEEK_END );
  119. filelen = ftell( f );
  120. fseek( f, 0, SEEK_SET );
  121. // allocate and load into memory
  122. char *buffer = (char*)malloc( filelen );
  123. char *bufpos = buffer + strlen( "\"classname\"" ) - 1;
  124. char *bufend = buffer + filelen;
  125. fread( buffer, filelen, 1, f );
  126. fclose( f );
  127. bool entFound = false;
  128. while ( 1 )
  129. {
  130. bufpos = FindSearchWord( bufpos, bufend );
  131. if ( !bufpos )
  132. break;
  133. // find the next word
  134. static char Token[256];
  135. ParseToken( bufpos, Token );
  136. // add the word to the list, filtering if necessary
  137. if ( !filterEnt || !stricmp(filterEnt, Token) )
  138. {
  139. AddToTable( Token );
  140. entFound = true;
  141. }
  142. bufpos += strlen( Token );
  143. }
  144. free( buffer );
  145. // print the bsp name, if an ent is found, or we are not filtering for ents
  146. if ( entFound || !filterEnt )
  147. printf( "%s\n", fileinfo.name );
  148. if ( printPerMap )
  149. {
  150. PrintOutTable();
  151. ClearUsageCountTable();
  152. }
  153. } while ( _findnext(FHandle, &fileinfo) == 0 );
  154. PrintOutTable();
  155. return 0;
  156. }
  157. void ParseFGD( char *buffer, char *bufend, const char *searchKey )
  158. {
  159. char *bufpos = buffer + strlen( searchKey ) - 1;
  160. SetSearchWord( searchKey );
  161. while ( 1 )
  162. {
  163. bufpos = FindSearchWord( bufpos, bufend );
  164. if ( !bufpos )
  165. break;
  166. // search for the corresponding '='
  167. while ( *bufpos != '=' )
  168. bufpos++;
  169. bufpos++;
  170. // find the classname
  171. static char Token[256];
  172. ParseToken( bufpos, Token );
  173. AddToTable( Token );
  174. bufpos += strlen( Token );
  175. }
  176. }
  177. /////////// entity table stuff //////////////
  178. const int MAX_ENTS = 2000;
  179. int NumEnts = 0;
  180. char *EntNames[ MAX_ENTS ];
  181. int EntUsage[ MAX_ENTS ];
  182. void ClearTable( void )
  183. {
  184. memset( EntNames, 0, sizeof(EntNames) );
  185. memset( EntUsage, 0, sizeof(EntUsage) );
  186. }
  187. void ClearUsageCountTable( void )
  188. {
  189. memset( EntUsage, 0, sizeof(EntUsage) );
  190. }
  191. void AddToTable( const char *name )
  192. {
  193. // search for it in the table
  194. for ( int i = 0; i < NumEnts; i++ )
  195. {
  196. if ( EntNames[i] && !strcmp(EntNames[i], name) )
  197. {
  198. // it's already in the table
  199. // increment the usage count
  200. EntUsage[i] += 1;
  201. return;
  202. }
  203. }
  204. // append to the table
  205. EntNames[NumEnts] = (char*)malloc( strlen(name) + 1 );
  206. strcpy( EntNames[NumEnts], name );
  207. EntUsage[NumEnts] = 1;
  208. NumEnts++;
  209. }
  210. void PrintOutTable( void )
  211. {
  212. while ( 1 )
  213. {
  214. // find the highest item
  215. int highestUsage = -1;
  216. int highestEnt = 0;
  217. for ( int i = 0; i < NumEnts; i++ )
  218. {
  219. if ( EntNames[i] && highestUsage < EntUsage[i] )
  220. {
  221. highestUsage = EntUsage[i];
  222. highestEnt = i;
  223. }
  224. }
  225. // check for no more ents
  226. if ( highestUsage == -1 )
  227. return;
  228. // display usage stats of item
  229. printf( " %5d %s\n", highestUsage, EntNames[highestEnt] );
  230. // remove item from list
  231. free( EntNames[highestEnt] );
  232. EntNames[highestEnt] = NULL;
  233. }
  234. }
  235. ////////// string search stuff ////////////
  236. static unsigned char JumpTable[256];
  237. static int SearchWordLen = 0;
  238. static const char *SearchWord;
  239. void SetSearchWord( const char *Word )
  240. {
  241. SearchWord = Word;
  242. SearchWordLen = strlen( SearchWord );
  243. // build the jump table
  244. // initialize all values to jump the length of the string
  245. memset( JumpTable, SearchWordLen, sizeof(JumpTable) );
  246. // set the amount the searcher can jump forward, depending on the character
  247. for ( int i = 0; i < SearchWordLen; i++ )
  248. {
  249. JumpTable[ (unsigned char)SearchWord[i] ] = max( SearchWordLen - i - 1, 1 );
  250. }
  251. }
  252. char *FindSearchWord( char *buffer, char *bufend )
  253. {
  254. /*
  255. for ( int i = SearchWordLen-1; i >= 0; i-- )
  256. {
  257. if ( *buffer != SearchWord[i] )
  258. {
  259. buffer += ( JumpTable[ (unsigned char)(*(buffer + i - SearchWordLen + 1)) ] - 1 );
  260. // no more buffer to search
  261. if ( buffer >= bufend )
  262. return NULL;
  263. // reset search counter
  264. i = SearchWordLen;
  265. }
  266. else
  267. {
  268. // it's a match, move backwards to search
  269. buffer--;
  270. }
  271. }
  272. */
  273. while ( 1 )
  274. {
  275. if ( strnicmp(buffer - SearchWordLen, SearchWord, SearchWordLen) )
  276. {
  277. // strings not equal, jump ahead
  278. buffer += JumpTable[ (unsigned char)*buffer ];
  279. if ( buffer >= bufend )
  280. return NULL;
  281. }
  282. else
  283. {
  284. break;
  285. }
  286. }
  287. // we have a match!
  288. // return a pointer just past the found key
  289. return buffer;
  290. }
  291. /*
  292. ==============
  293. ParseToken
  294. Parse a token out of a string
  295. outputs the parsed token into newToken
  296. ==============
  297. */
  298. char *ParseToken( char *data, char *newToken )
  299. {
  300. int c;
  301. int len;
  302. len = 0;
  303. newToken[0] = 0;
  304. if (!data)
  305. return NULL;
  306. // skip whitespace
  307. skipwhite:
  308. while ( (c = *data) <= ' ')
  309. {
  310. if (c == 0)
  311. return NULL; // end of file;
  312. data++;
  313. }
  314. // skip // comments
  315. if (c=='/' && data[1] == '/')
  316. {
  317. while (*data && *data != '\n')
  318. data++;
  319. goto skipwhite;
  320. }
  321. // handle quoted strings specially
  322. if (c == '\"')
  323. {
  324. data++;
  325. while ( len < 256 )
  326. {
  327. c = *data++;
  328. if (c=='\"' || !c)
  329. {
  330. newToken[len] = 0;
  331. return data;
  332. }
  333. newToken[len] = c;
  334. len++;
  335. }
  336. if ( len >= 256 )
  337. {
  338. len--;
  339. newToken[len] = 0;
  340. }
  341. }
  342. // parse single characters
  343. if ( c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' )
  344. {
  345. newToken[len] = c;
  346. len++;
  347. newToken[len] = 0;
  348. return data+1;
  349. }
  350. // parse a regular word
  351. do
  352. {
  353. newToken[len] = c;
  354. data++;
  355. len++;
  356. c = *data;
  357. if ( c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' )
  358. break;
  359. if ( len >= 256 )
  360. {
  361. len--;
  362. newToken[len] = 0;
  363. break;
  364. }
  365. } while (c>32);
  366. newToken[len] = 0;
  367. return data;
  368. }