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.
383 lines
10 KiB
383 lines
10 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: .360 Creation for all studiomdl generated files (mdl, vvd, vtx, ani, phy) |
|
// |
|
//=====================================================================================// |
|
|
|
#include "MakeGameData.h" |
|
#include "filesystem.h" |
|
#include "../../common/bsplib.h" |
|
#include "ibsppack.h" |
|
#include "vtf/vtf.h" |
|
#include "../../game/server/ai_hull.h" |
|
#include "zip_utils.h" |
|
|
|
#define AINET_VERSION_NUMBER 37 |
|
#define MAX_NODES 1500 |
|
|
|
bool ReadBSPHeader( const char *pFilename, dheader_t *pHeader ) |
|
{ |
|
V_memset( pHeader, 0, sizeof( dheader_t ) ); |
|
|
|
int handle = _open( pFilename, _O_RDONLY|_O_BINARY ); |
|
if ( handle == -1 ) |
|
{ |
|
return false; |
|
} |
|
|
|
_read( handle, pHeader, sizeof( dheader_t ) ); |
|
close( handle ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Run possible lod culling fixup |
|
//----------------------------------------------------------------------------- |
|
bool ConvertVHV( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuffer, CUtlBuffer &targetBuffer ) |
|
{ |
|
// find strip info from model |
|
char vsiFilename[MAX_PATH]; |
|
V_strncpy( vsiFilename, pModelName, sizeof( vsiFilename ) ); |
|
V_SetExtension( vsiFilename, ".vsi", sizeof( vsiFilename ) ); |
|
|
|
CUtlBuffer vsiBuffer; |
|
if ( !g_pFullFileSystem->ReadFile( vsiFilename, NULL, vsiBuffer ) ) |
|
{ |
|
// cannot convert bsp's without model converions |
|
Msg( "Error! Missing expected model conversion file '%s'. Cannot perform VHV fixup.\n", vsiFilename ); |
|
return false; |
|
} |
|
|
|
IMdlStripInfo *pMdlStripInfo = NULL; |
|
if ( !mdllib->CreateNewStripInfo( &pMdlStripInfo ) ) |
|
{ |
|
Msg( "Error! Failed to allocate strip info object\n" ); |
|
return false; |
|
} |
|
|
|
if ( !pMdlStripInfo->UnSerialize( vsiBuffer ) ) |
|
{ |
|
Msg( "Error! Failed to unserialize strip info object '%s'\n", vsiFilename ); |
|
pMdlStripInfo->DeleteThis(); |
|
return false; |
|
} |
|
|
|
long originalChecksum = 0; |
|
long newChecksum = 0; |
|
if ( !pMdlStripInfo->GetCheckSum( &originalChecksum, &newChecksum ) ) |
|
{ |
|
Msg( "Error! Failed to get checksums from '%s'\n", vsiFilename ); |
|
pMdlStripInfo->DeleteThis(); |
|
return false; |
|
} |
|
|
|
HardwareVerts::FileHeader_t *pVHVhdr = (HardwareVerts::FileHeader_t*)sourceBuffer.Base(); |
|
if ( pVHVhdr->m_nChecksum != originalChecksum ) |
|
{ |
|
// vhv file should have matching original checksums |
|
Msg( "Error! Mismatched checksums from '%s' and '%s'\n", vsiFilename, pVhvFilename ); |
|
pMdlStripInfo->DeleteThis(); |
|
return false; |
|
} |
|
|
|
targetBuffer.EnsureCapacity( sourceBuffer.TellMaxPut() ); |
|
targetBuffer.Put( sourceBuffer.Base(), sourceBuffer.TellMaxPut() ); |
|
if ( !pMdlStripInfo->StripHardwareVertsBuffer( targetBuffer ) ) |
|
{ |
|
pMdlStripInfo->DeleteThis(); |
|
return false; |
|
} |
|
|
|
// success |
|
pMdlStripInfo->DeleteThis(); |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Generate .360 bsp |
|
//----------------------------------------------------------------------------- |
|
bool CreateTargetFile_BSP( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) |
|
{ |
|
CUtlBuffer targetBuffer; |
|
CUtlBuffer zipBuffer; |
|
CUtlBuffer fileBuffer; |
|
CUtlBuffer tempBuffer; |
|
char tempZipName[MAX_PATH]; |
|
char tempSwapName[MAX_PATH]; |
|
|
|
tempZipName[0] = '\0'; |
|
tempSwapName[0] = '\0'; |
|
void *pPakData = NULL; |
|
int pakSize = 0; |
|
CXZipTool *pNewXZip = NULL; |
|
|
|
if ( !g_bModPathIsValid ) |
|
{ |
|
Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" ); |
|
return false; |
|
} |
|
|
|
// Load bsppack.dll |
|
void *pBSPPack; |
|
CSysModule *pBSPModule; |
|
if ( !Sys_LoadInterface( "bsppack.dll", IBSPPACK_VERSION_STRING, &pBSPModule, &pBSPPack ) ) |
|
{ |
|
Msg( "Failed to load bsppack interface\n" ); |
|
return false; |
|
} |
|
|
|
scriptlib->MakeTemporaryFilename( g_szModPath, tempSwapName, sizeof( tempSwapName ) ); |
|
|
|
// Swaps the bsp directly to disk |
|
bool bOK = ((IBSPPack*)pBSPPack)->SwapBSPFile( g_pFullFileSystem, pSourceName, tempSwapName, false, ConvertVTFTo360Format, ConvertVHV, CompressCallback ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
// get the pak file from the swapped bsp |
|
bOK = ((IBSPPack*)pBSPPack)->GetPakFileLump( g_pFullFileSystem, tempSwapName, &pPakData, &pakSize ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
// build an xzip version of the pak file |
|
if ( pPakData && pakSize ) |
|
{ |
|
// mount current pak file |
|
IZip *pOldZip = IZip::CreateZip( false, true ); |
|
pOldZip->ParseFromBuffer( pPakData, pakSize ); |
|
|
|
// start a new xzip version |
|
scriptlib->MakeTemporaryFilename( g_szModPath, tempZipName, sizeof( tempZipName ) ); |
|
|
|
pNewXZip = new CXZipTool; |
|
pNewXZip->Begin( tempZipName, XBOX_DVD_SECTORSIZE ); |
|
|
|
// iterate each file in existing zip, add to new zip |
|
int zipIndex = -1; |
|
for ( ;; ) |
|
{ |
|
char filename[MAX_PATH]; |
|
filename[0] = '\0'; |
|
int fileSize = 0; |
|
zipIndex = pOldZip->GetNextFilename( zipIndex, filename, sizeof( filename ), fileSize ); |
|
if ( zipIndex == -1 ) |
|
{ |
|
break; |
|
} |
|
|
|
fileBuffer.Purge(); |
|
bOK = pOldZip->ReadFileFromZip( filename, false, fileBuffer ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
bOK = pNewXZip->AddBuffer( filename, fileBuffer, true ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
} |
|
|
|
IZip::ReleaseZip( pOldZip ); |
|
pNewXZip->End(); |
|
|
|
// read the new zip into memory |
|
bOK = scriptlib->ReadFileToBuffer( tempZipName, zipBuffer ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
// replace old pak lump with new zip |
|
bOK = ((IBSPPack*)pBSPPack)->SetPakFileLump( g_pFullFileSystem, tempSwapName, tempSwapName, zipBuffer.Base(), zipBuffer.TellMaxPut() ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
} |
|
|
|
bOK = scriptlib->ReadFileToBuffer( tempSwapName, targetBuffer ); |
|
if ( !bOK ) |
|
{ |
|
goto cleanUp; |
|
} |
|
|
|
// never zip, always write local file |
|
bOK = WriteBufferToFile( pTargetName, targetBuffer, false, WRITE_TO_DISK_ALWAYS ); |
|
|
|
cleanUp: |
|
if ( tempZipName[0] ) |
|
{ |
|
_unlink( tempZipName ); |
|
} |
|
if ( tempSwapName[0] ) |
|
{ |
|
_unlink( tempSwapName ); |
|
} |
|
|
|
Sys_UnloadModule( pBSPModule ); |
|
|
|
if ( pPakData ) |
|
{ |
|
free( pPakData ); |
|
} |
|
|
|
delete pNewXZip; |
|
|
|
return bOK; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Generate .360 node graphs |
|
//----------------------------------------------------------------------------- |
|
bool CreateTargetFile_AIN( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) |
|
{ |
|
CUtlBuffer sourceBuf; |
|
if ( !scriptlib->ReadFileToBuffer( pSourceName, sourceBuf ) ) |
|
{ |
|
return false; |
|
} |
|
|
|
// the pc ain is tied to the pc bsp and should have been generated after the bsp |
|
char szBspName[MAX_PATH]; |
|
char szBspPath[MAX_PATH]; |
|
V_FileBase( pSourceName, szBspName, sizeof( szBspName ) ); |
|
V_ExtractFilePath( pSourceName, szBspPath, sizeof( szBspPath ) ); |
|
V_AppendSlash( szBspPath, sizeof( szBspPath ) ); |
|
V_strncat( szBspPath, "..\\", sizeof( szBspPath ) ); |
|
V_strncat( szBspPath, szBspName, sizeof( szBspPath ) ); |
|
V_strncat( szBspPath, ".bsp", sizeof( szBspPath ) ); |
|
if ( scriptlib->CompareFileTime( pSourceName, szBspPath ) < 0 ) |
|
{ |
|
// ain has a smaller filetime, thus older than bsp |
|
Msg( "%s: Need to regenerate PC nodegraph (stale)\n", pSourceName ); |
|
if ( !g_bForce ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
// Check the version |
|
if ( sourceBuf.GetChar() == 'V' && sourceBuf.GetChar() == 'e' && sourceBuf.GetChar() == 'r' ) |
|
{ |
|
Msg( "%s: Need to regenerate PC nodegraph (bad format)\n", pSourceName ); |
|
return false; |
|
} |
|
|
|
// reset |
|
sourceBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); |
|
|
|
// Version number |
|
int version = sourceBuf.GetInt(); |
|
if ( version != AINET_VERSION_NUMBER ) |
|
{ |
|
Msg( "%s: Need to regenerate PC nodegraph (got version '%d', expected '%d')\n", pSourceName, version, AINET_VERSION_NUMBER ); |
|
return false; |
|
} |
|
|
|
// check the map revision |
|
int mapVersion = sourceBuf.GetInt(); |
|
dheader_t bspHeader; |
|
if ( ReadBSPHeader( szBspPath, &bspHeader ) ) |
|
{ |
|
if ( mapVersion != bspHeader.mapRevision ) |
|
{ |
|
Msg( "%s: Need to regenerate PC nodegraph (ai revision '%d' does not match bsp revision '%d')\n", pSourceName, mapVersion, bspHeader.mapRevision ); |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
Msg( "%s: Could not find expected bsp '%s'\n", pSourceName, szBspPath ); |
|
} |
|
|
|
// Nodes |
|
int nodeCt = sourceBuf.GetInt(); |
|
if ( nodeCt > MAX_NODES || nodeCt < 0 ) |
|
{ |
|
Msg( "%s: Need to regenerate PC nodegraph (corrupt)\n", pSourceName ); |
|
return false; |
|
} |
|
|
|
CUtlBuffer targetBuf; |
|
targetBuf.ActivateByteSwapping( true ); |
|
|
|
CByteswap swap; |
|
swap.ActivateByteSwapping( true ); |
|
|
|
targetBuf.PutInt( version ); |
|
targetBuf.PutInt( mapVersion ); |
|
targetBuf.PutInt( nodeCt ); |
|
|
|
int numFloats = NUM_HULLS + 4; |
|
for ( int node = 0; node < nodeCt; ++node ) |
|
{ |
|
targetBuf.EnsureCapacity( targetBuf.TellPut() + numFloats * sizeof( float ) ); |
|
swap.SwapBufferToTargetEndian<float>( (float*)targetBuf.PeekPut(), (float*)sourceBuf.PeekGet(), numFloats ); |
|
sourceBuf.SeekGet( CUtlBuffer::SEEK_CURRENT, numFloats * sizeof( float ) ); |
|
targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, numFloats * sizeof( float ) ); |
|
|
|
targetBuf.PutChar( sourceBuf.GetChar() ); |
|
|
|
// Align the remaining data |
|
targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, 3 ); |
|
|
|
targetBuf.PutUnsignedShort( sourceBuf.GetUnsignedShort() ); |
|
targetBuf.PutShort( sourceBuf.GetShort() ); |
|
} |
|
|
|
// Node links |
|
int totalNumLinks = sourceBuf.GetInt(); |
|
targetBuf.PutInt( totalNumLinks ); |
|
|
|
for ( int link = 0; link < totalNumLinks; ++link ) |
|
{ |
|
targetBuf.PutShort( sourceBuf.GetShort() ); |
|
targetBuf.PutShort( sourceBuf.GetShort() ); |
|
targetBuf.Put( sourceBuf.PeekGet(), NUM_HULLS ); |
|
sourceBuf.SeekGet( CUtlBuffer::SEEK_CURRENT, NUM_HULLS ); |
|
} |
|
|
|
// WC lookup table |
|
targetBuf.EnsureCapacity( targetBuf.TellPut() + nodeCt * sizeof( int ) ); |
|
swap.SwapBufferToTargetEndian<int>( (int*)targetBuf.PeekPut(), (int*)sourceBuf.PeekGet(), nodeCt ); |
|
targetBuf.SeekPut( CUtlBuffer::SEEK_CURRENT, nodeCt * sizeof( int ) ); |
|
|
|
// Write the file out |
|
return WriteBufferToFile( pTargetName, targetBuf, bWriteToZip, WRITE_TO_DISK_ALWAYS ); |
|
} |
|
|
|
bool GetDependants_BSP( const char *pBspName, CUtlVector< CUtlString > *pList ) |
|
{ |
|
if ( !g_bModPathIsValid ) |
|
{ |
|
Msg( "Indeterminate mod path, Cannot perform BSP conversion\n" ); |
|
return false; |
|
} |
|
|
|
// Load bsppack.dll |
|
void *pBSPPack; |
|
CSysModule *pBSPModule; |
|
if ( !Sys_LoadInterface( "bsppack.dll", IBSPPACK_VERSION_STRING, &pBSPModule, &pBSPPack ) ) |
|
{ |
|
Msg( "Failed to load bsppack interface\n" ); |
|
return false; |
|
} |
|
|
|
// 360 builds a more complete reslist that includes bsp internal files |
|
// build full path to bsp file |
|
char szBspFilename[MAX_PATH]; |
|
V_ComposeFileName( g_szGamePath, pBspName, szBspFilename, sizeof( szBspFilename ) ); |
|
|
|
bool bOK = ((IBSPPack*)pBSPPack)->GetBSPDependants( g_pFullFileSystem, szBspFilename, pList ); |
|
|
|
Sys_UnloadModule( pBSPModule ); |
|
|
|
return bOK; |
|
}
|
|
|