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.
430 lines
12 KiB
430 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Spews BSP Info |
|
// |
|
//=====================================================================================// |
|
|
|
#include "xbspinfo.h" |
|
|
|
BEGIN_BYTESWAP_DATADESC( dheader_t ) |
|
DEFINE_FIELD( ident, FIELD_INTEGER ), |
|
DEFINE_FIELD( version, FIELD_INTEGER ), |
|
DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ), |
|
DEFINE_FIELD( mapRevision, FIELD_INTEGER ), |
|
END_BYTESWAP_DATADESC() |
|
|
|
BEGIN_BYTESWAP_DATADESC( lump_t ) |
|
DEFINE_FIELD( fileofs, FIELD_INTEGER ), |
|
DEFINE_FIELD( filelen, FIELD_INTEGER ), |
|
DEFINE_FIELD( version, FIELD_INTEGER ), |
|
DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ), |
|
END_BYTESWAP_DATADESC() |
|
|
|
typedef struct |
|
{ |
|
const char *pFriendlyName; |
|
const char *pName; |
|
int lumpNum; |
|
} lumpName_t; |
|
|
|
lumpName_t g_lumpNames[] = |
|
{ |
|
{"Entities", "LUMP_ENTITIES", LUMP_ENTITIES}, |
|
{"Planes", "LUMP_PLANES", LUMP_PLANES}, |
|
{"TexData", "LUMP_TEXDATA", LUMP_TEXDATA}, |
|
{"Vertexes", "LUMP_VERTEXES", LUMP_VERTEXES}, |
|
{"Visibility", "LUMP_VISIBILITY", LUMP_VISIBILITY}, |
|
{"Nodes", "LUMP_NODES", LUMP_NODES}, |
|
{"TexInfo", "LUMP_TEXINFO", LUMP_TEXINFO}, |
|
{"Faces", "LUMP_FACES", LUMP_FACES}, |
|
{"Face IDs", "LUMP_FACEIDS", LUMP_FACEIDS}, |
|
{"Lighting", "LUMP_LIGHTING", LUMP_LIGHTING}, |
|
{"Occlusion", "LUMP_OCCLUSION", LUMP_OCCLUSION}, |
|
{"Leafs", "LUMP_LEAFS", LUMP_LEAFS}, |
|
{"Edges", "LUMP_EDGES", LUMP_EDGES}, |
|
{"Surf Edges", "LUMP_SURFEDGES", LUMP_SURFEDGES}, |
|
{"Models", "LUMP_MODELS", LUMP_MODELS}, |
|
{"World Lights", "LUMP_WORLDLIGHTS", LUMP_WORLDLIGHTS}, |
|
{"Leaf Faces", "LUMP_LEAFFACES", LUMP_LEAFFACES}, |
|
{"Leaf Brushes", "LUMP_LEAFBRUSHES", LUMP_LEAFBRUSHES}, |
|
{"Brushes", "LUMP_BRUSHES", LUMP_BRUSHES}, |
|
{"Brush Sides", "LUMP_BRUSHSIDES", LUMP_BRUSHSIDES}, |
|
{"Areas", "LUMP_AREAS", LUMP_AREAS}, |
|
{"Area Portals", "LUMP_AREAPORTALS", LUMP_AREAPORTALS}, |
|
{"Disp Info", "LUMP_DISPINFO", LUMP_DISPINFO}, |
|
{"Original Faces", "LUMP_ORIGINALFACES", LUMP_ORIGINALFACES}, |
|
{"Phys Disp", "LUMP_PHYSDISP", LUMP_PHYSDISP}, |
|
{"Phys Collide", "LUMP_PHYSCOLLIDE", LUMP_PHYSCOLLIDE}, |
|
{"Vert Normals", "LUMP_VERTNORMALS", LUMP_VERTNORMALS}, |
|
{"Vert Normal Indices", "LUMP_VERTNORMALINDICES", LUMP_VERTNORMALINDICES}, |
|
{"Disp Lightmap Alphas", "LUMP_DISP_LIGHTMAP_ALPHAS", LUMP_DISP_LIGHTMAP_ALPHAS}, |
|
{"Disp Verts", "LUMP_DISP_VERTS", LUMP_DISP_VERTS}, |
|
{"Disp Lightmap Sample Positions", "LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS", LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS}, |
|
{"Game Lump", "LUMP_GAME_LUMP", LUMP_GAME_LUMP}, |
|
{"Leaf Water Data", "LUMP_LEAFWATERDATA", LUMP_LEAFWATERDATA}, |
|
{"Primitives", "LUMP_PRIMITIVES", LUMP_PRIMITIVES}, |
|
{"Prim Verts", "LUMP_PRIMVERTS", LUMP_PRIMVERTS}, |
|
{"Prim Indices", "LUMP_PRIMINDICES", LUMP_PRIMINDICES}, |
|
{"Pak File", "LUMP_PAKFILE", LUMP_PAKFILE}, |
|
{"Clip Portal Verts", "LUMP_CLIPPORTALVERTS", LUMP_CLIPPORTALVERTS}, |
|
{"Cube Maps", "LUMP_CUBEMAPS", LUMP_CUBEMAPS}, |
|
{"Tex Data String Data", "LUMP_TEXDATA_STRING_DATA", LUMP_TEXDATA_STRING_DATA}, |
|
{"Tex Data String Table", "LUMP_TEXDATA_STRING_TABLE", LUMP_TEXDATA_STRING_TABLE}, |
|
{"Overlays", "LUMP_OVERLAYS", LUMP_OVERLAYS}, |
|
{"Leaf Min Dist To Water", "LUMP_LEAFMINDISTTOWATER", LUMP_LEAFMINDISTTOWATER}, |
|
{"Face Macro Texture Info", "LUMP_FACE_MACRO_TEXTURE_INFO", LUMP_FACE_MACRO_TEXTURE_INFO}, |
|
{"Disp Tris", "LUMP_DISP_TRIS", LUMP_DISP_TRIS}, |
|
{"Phys Collide Surface", "LUMP_PHYSCOLLIDESURFACE", LUMP_PHYSCOLLIDESURFACE}, |
|
{"Water Overlays", "LUMP_WATEROVERLAYS", LUMP_WATEROVERLAYS}, |
|
{"Leaf Ambient index HDR", "LUMP_LEAF_AMBIENT_INDEX_HDR", LUMP_LEAF_AMBIENT_INDEX_HDR}, |
|
{"Leaf Ambient index", "LUMP_LEAF_AMBIENT_INDEX", LUMP_LEAF_AMBIENT_INDEX}, |
|
{"Lighting (HDR)", "LUMP_LIGHTING_HDR", LUMP_LIGHTING_HDR}, |
|
{"World Lights (HDR)", "LUMP_WORLDLIGHTS_HDR", LUMP_WORLDLIGHTS_HDR}, |
|
{"Leaf Ambient Lighting (HDR)", "LUMP_LEAF_AMBIENT_LIGHTING_HDR", LUMP_LEAF_AMBIENT_LIGHTING_HDR}, |
|
{"Leaf Ambient Lighting", "LUMP_LEAF_AMBIENT_LIGHTING", LUMP_LEAF_AMBIENT_LIGHTING}, |
|
{"*** DEAD ***", "LUMP_XZIPPAKFILE", LUMP_XZIPPAKFILE}, |
|
{"Faces (HDR)", "LUMP_FACES_HDR", LUMP_FACES_HDR}, |
|
{"Flags", "LUMP_MAP_FLAGS", LUMP_MAP_FLAGS}, |
|
{"Fade Overlays", "LUMP_OVERLAY_FADES", LUMP_OVERLAY_FADES}, |
|
}; |
|
|
|
bool g_bQuiet; |
|
bool g_bAsPercent; |
|
bool g_bAsBytes; |
|
bool g_bSortByOffset; |
|
bool g_bSortBySize; |
|
bool g_bFriendlyNames; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Convert lump ID to descriptive Name |
|
//----------------------------------------------------------------------------- |
|
const char *BSP_LumpNumToName( int lumpNum ) |
|
{ |
|
int i; |
|
|
|
for ( i=0; i<ARRAYSIZE( g_lumpNames ); ++i ) |
|
{ |
|
if ( g_lumpNames[i].lumpNum == lumpNum ) |
|
{ |
|
if ( g_bFriendlyNames ) |
|
{ |
|
return g_lumpNames[i].pFriendlyName; |
|
} |
|
else |
|
{ |
|
return g_lumpNames[i].pName; |
|
} |
|
} |
|
} |
|
|
|
return "???"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Extract Lump Pak |
|
//----------------------------------------------------------------------------- |
|
void ExtractZip( const char *pFilename, void *pBSPFile ) |
|
{ |
|
dheader_t *pBSPHeader = (dheader_t *)pBSPFile; |
|
|
|
if ( pBSPHeader->lumps[LUMP_PAKFILE].filelen ) |
|
{ |
|
Msg( "Extracting Zip to %s\n", pFilename ); |
|
|
|
FILE *fp = fopen( pFilename, "wb" ); |
|
if ( !fp ) |
|
{ |
|
Warning( "Failed to create %s\n", pFilename ); |
|
return; |
|
} |
|
|
|
fwrite( (unsigned char *)pBSPFile + pBSPHeader->lumps[LUMP_PAKFILE].fileofs, pBSPHeader->lumps[LUMP_PAKFILE].filelen, 1, fp ); |
|
fclose( fp ); |
|
} |
|
else |
|
{ |
|
Msg( "Nothing to do!\n" ); |
|
} |
|
} |
|
|
|
// compare function for qsort below |
|
static dheader_t *g_pSortBSPHeader; |
|
static int LumpCompare( const void *pElem1, const void *pElem2 ) |
|
{ |
|
int lump1 = *(byte *)pElem1; |
|
int lump2 = *(byte *)pElem2; |
|
|
|
int fileOffset1 = g_pSortBSPHeader->lumps[lump1].fileofs; |
|
int fileOffset2 = g_pSortBSPHeader->lumps[lump2].fileofs; |
|
|
|
int fileSize1 = g_pSortBSPHeader->lumps[lump1].filelen; |
|
int fileSize2 = g_pSortBSPHeader->lumps[lump2].filelen; |
|
|
|
if ( g_bSortByOffset ) |
|
{ |
|
// invalid or empty lumps will get sorted together |
|
if ( !fileSize1 ) |
|
{ |
|
fileOffset1 = 0; |
|
} |
|
|
|
if ( !fileSize2 ) |
|
{ |
|
fileOffset2 = 0; |
|
} |
|
|
|
// compare by offset |
|
if ( fileOffset1 < fileOffset2 ) |
|
{ |
|
return -1; |
|
} |
|
else if ( fileOffset1 > fileOffset2 ) |
|
{ |
|
return 1; |
|
} |
|
} |
|
else if ( g_bSortBySize ) |
|
{ |
|
if ( fileSize1 < fileSize2 ) |
|
{ |
|
return -1; |
|
} |
|
else if ( fileSize1 > fileSize2 ) |
|
{ |
|
return 1; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Spew Info |
|
//----------------------------------------------------------------------------- |
|
void DumpInfo( const char *pFilename, void *pBSPFile, int bspSize ) |
|
{ |
|
const char *pName; |
|
dheader_t *pBSPHeader; |
|
|
|
pBSPHeader = (dheader_t *)pBSPFile; |
|
|
|
Msg( "\n" ); |
|
Msg( "%s\n", pFilename ); |
|
|
|
// sort by offset order |
|
int readOrder[HEADER_LUMPS]; |
|
for ( int i=0; i<HEADER_LUMPS; i++ ) |
|
{ |
|
readOrder[i] = i; |
|
} |
|
|
|
if ( g_bSortByOffset || g_bSortBySize ) |
|
{ |
|
g_pSortBSPHeader = pBSPHeader; |
|
qsort( readOrder, HEADER_LUMPS, sizeof( int ), LumpCompare ); |
|
} |
|
|
|
for ( int i=0; i<HEADER_LUMPS; ++i ) |
|
{ |
|
int lump = readOrder[i]; |
|
pName = BSP_LumpNumToName( lump ); |
|
if ( !pName ) |
|
continue; |
|
|
|
if ( g_bSortByOffset ) |
|
{ |
|
Msg( "[Offset: 0x%8.8x] ", pBSPHeader->lumps[lump].fileofs ); |
|
} |
|
|
|
if ( g_bAsPercent ) |
|
{ |
|
Msg( "%5.2f%s (%2d) %s\n", 100.0f*pBSPHeader->lumps[lump].filelen/( float )bspSize, "%%", lump, pName ); |
|
} |
|
else if ( g_bAsBytes ) |
|
{ |
|
Msg( "%8d: (%2d) %s\n", pBSPHeader->lumps[lump].filelen, lump, pName ); |
|
} |
|
else |
|
{ |
|
Msg( "%5.2f MB: (%2d) %s\n", pBSPHeader->lumps[lump].filelen/( 1024.0f*1024.0f ), lump, pName ); |
|
} |
|
} |
|
|
|
Msg( "-------\n" ); |
|
if ( g_bAsBytes ) |
|
{ |
|
Msg( "%8d: %s\n", bspSize, "Total Bytes" ); |
|
} |
|
else |
|
{ |
|
Msg( "%6.2f MB %s\n", bspSize/( 1024.0f*1024.0f ), "Total" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Load the bsp file |
|
//----------------------------------------------------------------------------- |
|
bool LoadBSPFile( const char* pFilename, void **ppBSPBuffer, int *pBSPSize ) |
|
{ |
|
CByteswap byteSwap; |
|
|
|
*ppBSPBuffer = NULL; |
|
*pBSPSize = 0; |
|
|
|
FILE *fp = fopen( pFilename, "rb" ); |
|
if ( fp ) |
|
{ |
|
fseek( fp, 0, SEEK_END ); |
|
int size = ftell( fp ); |
|
fseek( fp, 0, SEEK_SET ); |
|
|
|
*ppBSPBuffer = malloc( size ); |
|
if ( !*ppBSPBuffer ) |
|
{ |
|
Warning( "Failed to alloc %d bytes\n", size ); |
|
goto cleanUp; |
|
} |
|
|
|
*pBSPSize = size; |
|
fread( *ppBSPBuffer, size, 1, fp ); |
|
fclose( fp ); |
|
} |
|
else |
|
{ |
|
if ( !g_bQuiet ) |
|
{ |
|
Warning( "Missing %s\n", pFilename ); |
|
} |
|
goto cleanUp; |
|
} |
|
|
|
dheader_t *pBSPHeader = (dheader_t *)*ppBSPBuffer; |
|
|
|
if ( pBSPHeader->ident != IDBSPHEADER ) |
|
{ |
|
if ( pBSPHeader->ident != BigLong( IDBSPHEADER ) ) |
|
{ |
|
if ( !g_bQuiet ) |
|
{ |
|
Warning( "BSP %s has bad id: got %d, expected %d\n", pFilename, pBSPHeader->ident, IDBSPHEADER ); |
|
} |
|
goto cleanUp; |
|
} |
|
else |
|
{ |
|
// bsp is for 360, swap the header |
|
byteSwap.ActivateByteSwapping( true ); |
|
byteSwap.SwapFieldsToTargetEndian( pBSPHeader ); |
|
} |
|
} |
|
|
|
if ( pBSPHeader->version < MINBSPVERSION || pBSPHeader->version > BSPVERSION ) |
|
{ |
|
if ( !g_bQuiet ) |
|
{ |
|
Warning( "BSP %s has bad version: got %d, expected %d\n", pFilename, pBSPHeader->version, BSPVERSION ); |
|
} |
|
goto cleanUp; |
|
} |
|
|
|
// sucess |
|
return true; |
|
|
|
cleanUp: |
|
if ( *ppBSPBuffer ) |
|
{ |
|
free( *ppBSPBuffer ); |
|
*ppBSPBuffer = NULL; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Usage |
|
//----------------------------------------------------------------------------- |
|
void Usage( void ) |
|
{ |
|
Msg( "usage: bspinfo <bspfile> [options]\n" ); |
|
Msg( "options:\n" ); |
|
Msg( "-percent, -p: Show as percentages\n" ); |
|
Msg( "-bytes, -b: Show as bytes\n" ); |
|
Msg( "-q: Quiet, no header, no errors\n" ); |
|
Msg( "-names: Show friendly lump names\n" ); |
|
Msg( "-so: Sort by offset\n" ); |
|
Msg( "-ss: Sort by size\n" ); |
|
Msg( "-extract <zipname>: Extract pak file\n" ); |
|
|
|
exit( -1 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: default output func |
|
//----------------------------------------------------------------------------- |
|
SpewRetval_t OutputFunc( SpewType_t spewType, char const *pMsg ) |
|
{ |
|
printf( pMsg ); |
|
|
|
if ( spewType == SPEW_ERROR ) |
|
{ |
|
return SPEW_ABORT; |
|
} |
|
return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// main |
|
// |
|
//----------------------------------------------------------------------------- |
|
int main( int argc, char* argv[] ) |
|
{ |
|
char bspPath[MAX_PATH]; |
|
|
|
// set the valve library printer |
|
SpewOutputFunc( OutputFunc ); |
|
|
|
CommandLine()->CreateCmdLine( argc, argv ); |
|
|
|
Msg( "\nXBSPINFO - Valve Xbox 360 BSP Info ( Build: %s %s )\n", __DATE__, __TIME__ ); |
|
Msg( "( C ) Copyright 1996-2006, Valve Corporation, All rights reserved.\n\n" ); |
|
|
|
if ( argc < 2 || CommandLine()->FindParm( "?" ) || CommandLine()->FindParm( "-h" ) || CommandLine()->FindParm( "-help" ) ) |
|
{ |
|
Usage(); |
|
} |
|
|
|
if ( argc >= 2 && argv[1][0] != '-' ) |
|
{ |
|
strcpy( bspPath, argv[1] ); |
|
} |
|
else |
|
{ |
|
Usage(); |
|
} |
|
|
|
g_bQuiet = CommandLine()->FindParm( "-q" ) != 0; |
|
g_bAsPercent = CommandLine()->FindParm( "-p" ) != 0 || CommandLine()->FindParm( "-percent" ) != 0; |
|
g_bAsBytes = CommandLine()->FindParm( "-b" ) != 0 || CommandLine()->FindParm( "-bytes" ) != 0; |
|
g_bSortByOffset = CommandLine()->FindParm( "-so" ) != 0; |
|
g_bSortBySize = CommandLine()->FindParm( "-ss" ) != 0; |
|
g_bFriendlyNames = CommandLine()->FindParm( "-names" ) != 0; |
|
|
|
void *pBSPBuffer; |
|
int bspSize; |
|
if ( LoadBSPFile( bspPath, &pBSPBuffer, &bspSize ) ) |
|
{ |
|
const char *pZipName = CommandLine()->ParmValue( "-extract", "" ); |
|
if ( pZipName && pZipName[0] ) |
|
{ |
|
ExtractZip( pZipName, pBSPBuffer ); |
|
} |
|
else |
|
{ |
|
DumpInfo( bspPath, pBSPBuffer, bspSize ); |
|
} |
|
|
|
free( pBSPBuffer ); |
|
} |
|
|
|
return ( 0 ); |
|
} |