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 );
|
|
}
|