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.

429 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Spews BSP Info
  4. //
  5. //=====================================================================================//
  6. #include "xbspinfo.h"
  7. BEGIN_BYTESWAP_DATADESC( dheader_t )
  8. DEFINE_FIELD( ident, FIELD_INTEGER ),
  9. DEFINE_FIELD( version, FIELD_INTEGER ),
  10. DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ),
  11. DEFINE_FIELD( mapRevision, FIELD_INTEGER ),
  12. END_BYTESWAP_DATADESC()
  13. BEGIN_BYTESWAP_DATADESC( lump_t )
  14. DEFINE_FIELD( fileofs, FIELD_INTEGER ),
  15. DEFINE_FIELD( filelen, FIELD_INTEGER ),
  16. DEFINE_FIELD( version, FIELD_INTEGER ),
  17. DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ),
  18. END_BYTESWAP_DATADESC()
  19. typedef struct
  20. {
  21. const char *pFriendlyName;
  22. const char *pName;
  23. int lumpNum;
  24. } lumpName_t;
  25. lumpName_t g_lumpNames[] =
  26. {
  27. {"Entities", "LUMP_ENTITIES", LUMP_ENTITIES},
  28. {"Planes", "LUMP_PLANES", LUMP_PLANES},
  29. {"TexData", "LUMP_TEXDATA", LUMP_TEXDATA},
  30. {"Vertexes", "LUMP_VERTEXES", LUMP_VERTEXES},
  31. {"Visibility", "LUMP_VISIBILITY", LUMP_VISIBILITY},
  32. {"Nodes", "LUMP_NODES", LUMP_NODES},
  33. {"TexInfo", "LUMP_TEXINFO", LUMP_TEXINFO},
  34. {"Faces", "LUMP_FACES", LUMP_FACES},
  35. {"Face IDs", "LUMP_FACEIDS", LUMP_FACEIDS},
  36. {"Lighting", "LUMP_LIGHTING", LUMP_LIGHTING},
  37. {"Occlusion", "LUMP_OCCLUSION", LUMP_OCCLUSION},
  38. {"Leafs", "LUMP_LEAFS", LUMP_LEAFS},
  39. {"Edges", "LUMP_EDGES", LUMP_EDGES},
  40. {"Surf Edges", "LUMP_SURFEDGES", LUMP_SURFEDGES},
  41. {"Models", "LUMP_MODELS", LUMP_MODELS},
  42. {"World Lights", "LUMP_WORLDLIGHTS", LUMP_WORLDLIGHTS},
  43. {"Leaf Faces", "LUMP_LEAFFACES", LUMP_LEAFFACES},
  44. {"Leaf Brushes", "LUMP_LEAFBRUSHES", LUMP_LEAFBRUSHES},
  45. {"Brushes", "LUMP_BRUSHES", LUMP_BRUSHES},
  46. {"Brush Sides", "LUMP_BRUSHSIDES", LUMP_BRUSHSIDES},
  47. {"Areas", "LUMP_AREAS", LUMP_AREAS},
  48. {"Area Portals", "LUMP_AREAPORTALS", LUMP_AREAPORTALS},
  49. {"Disp Info", "LUMP_DISPINFO", LUMP_DISPINFO},
  50. {"Original Faces", "LUMP_ORIGINALFACES", LUMP_ORIGINALFACES},
  51. {"Phys Disp", "LUMP_PHYSDISP", LUMP_PHYSDISP},
  52. {"Phys Collide", "LUMP_PHYSCOLLIDE", LUMP_PHYSCOLLIDE},
  53. {"Vert Normals", "LUMP_VERTNORMALS", LUMP_VERTNORMALS},
  54. {"Vert Normal Indices", "LUMP_VERTNORMALINDICES", LUMP_VERTNORMALINDICES},
  55. {"Disp Lightmap Alphas", "LUMP_DISP_LIGHTMAP_ALPHAS", LUMP_DISP_LIGHTMAP_ALPHAS},
  56. {"Disp Verts", "LUMP_DISP_VERTS", LUMP_DISP_VERTS},
  57. {"Disp Lightmap Sample Positions", "LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS", LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS},
  58. {"Game Lump", "LUMP_GAME_LUMP", LUMP_GAME_LUMP},
  59. {"Leaf Water Data", "LUMP_LEAFWATERDATA", LUMP_LEAFWATERDATA},
  60. {"Primitives", "LUMP_PRIMITIVES", LUMP_PRIMITIVES},
  61. {"Prim Verts", "LUMP_PRIMVERTS", LUMP_PRIMVERTS},
  62. {"Prim Indices", "LUMP_PRIMINDICES", LUMP_PRIMINDICES},
  63. {"Pak File", "LUMP_PAKFILE", LUMP_PAKFILE},
  64. {"Clip Portal Verts", "LUMP_CLIPPORTALVERTS", LUMP_CLIPPORTALVERTS},
  65. {"Cube Maps", "LUMP_CUBEMAPS", LUMP_CUBEMAPS},
  66. {"Tex Data String Data", "LUMP_TEXDATA_STRING_DATA", LUMP_TEXDATA_STRING_DATA},
  67. {"Tex Data String Table", "LUMP_TEXDATA_STRING_TABLE", LUMP_TEXDATA_STRING_TABLE},
  68. {"Overlays", "LUMP_OVERLAYS", LUMP_OVERLAYS},
  69. {"Leaf Min Dist To Water", "LUMP_LEAFMINDISTTOWATER", LUMP_LEAFMINDISTTOWATER},
  70. {"Face Macro Texture Info", "LUMP_FACE_MACRO_TEXTURE_INFO", LUMP_FACE_MACRO_TEXTURE_INFO},
  71. {"Disp Tris", "LUMP_DISP_TRIS", LUMP_DISP_TRIS},
  72. {"Phys Collide Surface", "LUMP_PHYSCOLLIDESURFACE", LUMP_PHYSCOLLIDESURFACE},
  73. {"Water Overlays", "LUMP_WATEROVERLAYS", LUMP_WATEROVERLAYS},
  74. {"Leaf Ambient index HDR", "LUMP_LEAF_AMBIENT_INDEX_HDR", LUMP_LEAF_AMBIENT_INDEX_HDR},
  75. {"Leaf Ambient index", "LUMP_LEAF_AMBIENT_INDEX", LUMP_LEAF_AMBIENT_INDEX},
  76. {"Lighting (HDR)", "LUMP_LIGHTING_HDR", LUMP_LIGHTING_HDR},
  77. {"World Lights (HDR)", "LUMP_WORLDLIGHTS_HDR", LUMP_WORLDLIGHTS_HDR},
  78. {"Leaf Ambient Lighting (HDR)", "LUMP_LEAF_AMBIENT_LIGHTING_HDR", LUMP_LEAF_AMBIENT_LIGHTING_HDR},
  79. {"Leaf Ambient Lighting", "LUMP_LEAF_AMBIENT_LIGHTING", LUMP_LEAF_AMBIENT_LIGHTING},
  80. {"*** DEAD ***", "LUMP_XZIPPAKFILE", LUMP_XZIPPAKFILE},
  81. {"Faces (HDR)", "LUMP_FACES_HDR", LUMP_FACES_HDR},
  82. {"Flags", "LUMP_MAP_FLAGS", LUMP_MAP_FLAGS},
  83. {"Fade Overlays", "LUMP_OVERLAY_FADES", LUMP_OVERLAY_FADES},
  84. };
  85. bool g_bQuiet;
  86. bool g_bAsPercent;
  87. bool g_bAsBytes;
  88. bool g_bSortByOffset;
  89. bool g_bSortBySize;
  90. bool g_bFriendlyNames;
  91. //-----------------------------------------------------------------------------
  92. // Convert lump ID to descriptive Name
  93. //-----------------------------------------------------------------------------
  94. const char *BSP_LumpNumToName( int lumpNum )
  95. {
  96. int i;
  97. for ( i=0; i<ARRAYSIZE( g_lumpNames ); ++i )
  98. {
  99. if ( g_lumpNames[i].lumpNum == lumpNum )
  100. {
  101. if ( g_bFriendlyNames )
  102. {
  103. return g_lumpNames[i].pFriendlyName;
  104. }
  105. else
  106. {
  107. return g_lumpNames[i].pName;
  108. }
  109. }
  110. }
  111. return "???";
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Extract Lump Pak
  115. //-----------------------------------------------------------------------------
  116. void ExtractZip( const char *pFilename, void *pBSPFile )
  117. {
  118. dheader_t *pBSPHeader = (dheader_t *)pBSPFile;
  119. if ( pBSPHeader->lumps[LUMP_PAKFILE].filelen )
  120. {
  121. Msg( "Extracting Zip to %s\n", pFilename );
  122. FILE *fp = fopen( pFilename, "wb" );
  123. if ( !fp )
  124. {
  125. Warning( "Failed to create %s\n", pFilename );
  126. return;
  127. }
  128. fwrite( (unsigned char *)pBSPFile + pBSPHeader->lumps[LUMP_PAKFILE].fileofs, pBSPHeader->lumps[LUMP_PAKFILE].filelen, 1, fp );
  129. fclose( fp );
  130. }
  131. else
  132. {
  133. Msg( "Nothing to do!\n" );
  134. }
  135. }
  136. // compare function for qsort below
  137. static dheader_t *g_pSortBSPHeader;
  138. static int LumpCompare( const void *pElem1, const void *pElem2 )
  139. {
  140. int lump1 = *(byte *)pElem1;
  141. int lump2 = *(byte *)pElem2;
  142. int fileOffset1 = g_pSortBSPHeader->lumps[lump1].fileofs;
  143. int fileOffset2 = g_pSortBSPHeader->lumps[lump2].fileofs;
  144. int fileSize1 = g_pSortBSPHeader->lumps[lump1].filelen;
  145. int fileSize2 = g_pSortBSPHeader->lumps[lump2].filelen;
  146. if ( g_bSortByOffset )
  147. {
  148. // invalid or empty lumps will get sorted together
  149. if ( !fileSize1 )
  150. {
  151. fileOffset1 = 0;
  152. }
  153. if ( !fileSize2 )
  154. {
  155. fileOffset2 = 0;
  156. }
  157. // compare by offset
  158. if ( fileOffset1 < fileOffset2 )
  159. {
  160. return -1;
  161. }
  162. else if ( fileOffset1 > fileOffset2 )
  163. {
  164. return 1;
  165. }
  166. }
  167. else if ( g_bSortBySize )
  168. {
  169. if ( fileSize1 < fileSize2 )
  170. {
  171. return -1;
  172. }
  173. else if ( fileSize1 > fileSize2 )
  174. {
  175. return 1;
  176. }
  177. }
  178. return 0;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Spew Info
  182. //-----------------------------------------------------------------------------
  183. void DumpInfo( const char *pFilename, void *pBSPFile, int bspSize )
  184. {
  185. const char *pName;
  186. dheader_t *pBSPHeader;
  187. pBSPHeader = (dheader_t *)pBSPFile;
  188. Msg( "\n" );
  189. Msg( "%s\n", pFilename );
  190. // sort by offset order
  191. int readOrder[HEADER_LUMPS];
  192. for ( int i=0; i<HEADER_LUMPS; i++ )
  193. {
  194. readOrder[i] = i;
  195. }
  196. if ( g_bSortByOffset || g_bSortBySize )
  197. {
  198. g_pSortBSPHeader = pBSPHeader;
  199. qsort( readOrder, HEADER_LUMPS, sizeof( int ), LumpCompare );
  200. }
  201. for ( int i=0; i<HEADER_LUMPS; ++i )
  202. {
  203. int lump = readOrder[i];
  204. pName = BSP_LumpNumToName( lump );
  205. if ( !pName )
  206. continue;
  207. if ( g_bSortByOffset )
  208. {
  209. Msg( "[Offset: 0x%8.8x] ", pBSPHeader->lumps[lump].fileofs );
  210. }
  211. if ( g_bAsPercent )
  212. {
  213. Msg( "%5.2f%s (%2d) %s\n", 100.0f*pBSPHeader->lumps[lump].filelen/( float )bspSize, "%%", lump, pName );
  214. }
  215. else if ( g_bAsBytes )
  216. {
  217. Msg( "%8d: (%2d) %s\n", pBSPHeader->lumps[lump].filelen, lump, pName );
  218. }
  219. else
  220. {
  221. Msg( "%5.2f MB: (%2d) %s\n", pBSPHeader->lumps[lump].filelen/( 1024.0f*1024.0f ), lump, pName );
  222. }
  223. }
  224. Msg( "-------\n" );
  225. if ( g_bAsBytes )
  226. {
  227. Msg( "%8d: %s\n", bspSize, "Total Bytes" );
  228. }
  229. else
  230. {
  231. Msg( "%6.2f MB %s\n", bspSize/( 1024.0f*1024.0f ), "Total" );
  232. }
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Load the bsp file
  236. //-----------------------------------------------------------------------------
  237. bool LoadBSPFile( const char* pFilename, void **ppBSPBuffer, int *pBSPSize )
  238. {
  239. CByteswap byteSwap;
  240. *ppBSPBuffer = NULL;
  241. *pBSPSize = 0;
  242. FILE *fp = fopen( pFilename, "rb" );
  243. if ( fp )
  244. {
  245. fseek( fp, 0, SEEK_END );
  246. int size = ftell( fp );
  247. fseek( fp, 0, SEEK_SET );
  248. *ppBSPBuffer = malloc( size );
  249. if ( !*ppBSPBuffer )
  250. {
  251. Warning( "Failed to alloc %d bytes\n", size );
  252. goto cleanUp;
  253. }
  254. *pBSPSize = size;
  255. fread( *ppBSPBuffer, size, 1, fp );
  256. fclose( fp );
  257. }
  258. else
  259. {
  260. if ( !g_bQuiet )
  261. {
  262. Warning( "Missing %s\n", pFilename );
  263. }
  264. goto cleanUp;
  265. }
  266. dheader_t *pBSPHeader = (dheader_t *)*ppBSPBuffer;
  267. if ( pBSPHeader->ident != IDBSPHEADER )
  268. {
  269. if ( pBSPHeader->ident != BigLong( IDBSPHEADER ) )
  270. {
  271. if ( !g_bQuiet )
  272. {
  273. Warning( "BSP %s has bad id: got %d, expected %d\n", pFilename, pBSPHeader->ident, IDBSPHEADER );
  274. }
  275. goto cleanUp;
  276. }
  277. else
  278. {
  279. // bsp is for 360, swap the header
  280. byteSwap.ActivateByteSwapping( true );
  281. byteSwap.SwapFieldsToTargetEndian( pBSPHeader );
  282. }
  283. }
  284. if ( pBSPHeader->version < MINBSPVERSION || pBSPHeader->version > BSPVERSION )
  285. {
  286. if ( !g_bQuiet )
  287. {
  288. Warning( "BSP %s has bad version: got %d, expected %d\n", pFilename, pBSPHeader->version, BSPVERSION );
  289. }
  290. goto cleanUp;
  291. }
  292. // sucess
  293. return true;
  294. cleanUp:
  295. if ( *ppBSPBuffer )
  296. {
  297. free( *ppBSPBuffer );
  298. *ppBSPBuffer = NULL;
  299. }
  300. return false;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Usage
  304. //-----------------------------------------------------------------------------
  305. void Usage( void )
  306. {
  307. Msg( "usage: bspinfo <bspfile> [options]\n" );
  308. Msg( "options:\n" );
  309. Msg( "-percent, -p: Show as percentages\n" );
  310. Msg( "-bytes, -b: Show as bytes\n" );
  311. Msg( "-q: Quiet, no header, no errors\n" );
  312. Msg( "-names: Show friendly lump names\n" );
  313. Msg( "-so: Sort by offset\n" );
  314. Msg( "-ss: Sort by size\n" );
  315. Msg( "-extract <zipname>: Extract pak file\n" );
  316. exit( -1 );
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: default output func
  320. //-----------------------------------------------------------------------------
  321. SpewRetval_t OutputFunc( SpewType_t spewType, char const *pMsg )
  322. {
  323. printf( pMsg );
  324. if ( spewType == SPEW_ERROR )
  325. {
  326. return SPEW_ABORT;
  327. }
  328. return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
  329. }
  330. //-----------------------------------------------------------------------------
  331. // main
  332. //
  333. //-----------------------------------------------------------------------------
  334. int main( int argc, char* argv[] )
  335. {
  336. char bspPath[MAX_PATH];
  337. // set the valve library printer
  338. SpewOutputFunc( OutputFunc );
  339. CommandLine()->CreateCmdLine( argc, argv );
  340. Msg( "\nXBSPINFO - Valve Xbox 360 BSP Info ( Build: %s %s )\n", __DATE__, __TIME__ );
  341. Msg( "( C ) Copyright 1996-2006, Valve Corporation, All rights reserved.\n\n" );
  342. if ( argc < 2 || CommandLine()->FindParm( "?" ) || CommandLine()->FindParm( "-h" ) || CommandLine()->FindParm( "-help" ) )
  343. {
  344. Usage();
  345. }
  346. if ( argc >= 2 && argv[1][0] != '-' )
  347. {
  348. strcpy( bspPath, argv[1] );
  349. }
  350. else
  351. {
  352. Usage();
  353. }
  354. g_bQuiet = CommandLine()->FindParm( "-q" ) != 0;
  355. g_bAsPercent = CommandLine()->FindParm( "-p" ) != 0 || CommandLine()->FindParm( "-percent" ) != 0;
  356. g_bAsBytes = CommandLine()->FindParm( "-b" ) != 0 || CommandLine()->FindParm( "-bytes" ) != 0;
  357. g_bSortByOffset = CommandLine()->FindParm( "-so" ) != 0;
  358. g_bSortBySize = CommandLine()->FindParm( "-ss" ) != 0;
  359. g_bFriendlyNames = CommandLine()->FindParm( "-names" ) != 0;
  360. void *pBSPBuffer;
  361. int bspSize;
  362. if ( LoadBSPFile( bspPath, &pBSPBuffer, &bspSize ) )
  363. {
  364. const char *pZipName = CommandLine()->ParmValue( "-extract", "" );
  365. if ( pZipName && pZipName[0] )
  366. {
  367. ExtractZip( pZipName, pBSPBuffer );
  368. }
  369. else
  370. {
  371. DumpInfo( bspPath, pBSPBuffer, bspSize );
  372. }
  373. free( pBSPBuffer );
  374. }
  375. return ( 0 );
  376. }