bool AStar::Save(IFileSystem *filesystem, const char *Filename) { CUtlBuffer buf; AStarNode *Node; NodeList_t Links; int TotalNumLinks = 0; int NodeTotal = Nodes.Count(); ////////////////////////////////////////////// // Nodes buf.PutInt(NodeTotal); for(int i = 0; i < NodeTotal; i++) { Node = Nodes[i]; buf.PutFloat(Node->GetPos().x); buf.PutFloat(Node->GetPos().y); buf.PutFloat(Node->GetPos().z); TotalNumLinks += Node->GetLinks().Count(); } ////////////////////////////////////////////// ////////////////////////////////////////////// // Links buf.PutInt(TotalNumLinks); for(int i = 0; i < NodeTotal; i++) { Node = Nodes[i]; Links = Node->GetLinks(); for(int li = 0; li < Links.Count(); li++) { buf.PutInt(Node->GetID()); buf.PutInt(Links[li]->GetID()); } } ////////////////////////////////////////////// ////////////////////////////////////////////// // Write File FileHandle_t fh = filesystem->Open(Filename, "wb"); if(!fh) { return false; } filesystem->Write(buf.Base(), buf.TellPut(), fh); filesystem->Close(fh); ////////////////////////////////////////////// return true; }
//----------------------------------------------------------------------------- // Binary buffer attribute //----------------------------------------------------------------------------- bool Serialize( CUtlBuffer &buf, const CUtlBinaryBlock &src ) { int nLength = src.Length(); if ( !buf.IsText() ) { buf.PutInt( nLength ); if ( nLength != 0 ) { buf.Put( src.Get(), nLength ); } return buf.IsValid(); } // Writes out uuencoded binaries for ( int i = 0; i < nLength; ++i ) { if ( (i % 40) == 0 ) { buf.PutChar( '\n' ); } char b1 = src[i] & 0xF; char b2 = src[i] >> 4; char c1 = ( b1 <= 9 ) ? b1 + '0' : b1 - 10 + 'A'; char c2 = ( b2 <= 9 ) ? b2 + '0' : b2 - 10 + 'A'; buf.PutChar( c2 ); buf.PutChar( c1 ); } buf.PutChar( '\n' ); return buf.IsValid(); }
/** * Save a navigation area to the opened binary stream */ void CNavArea::Save( CUtlBuffer &fileBuffer, unsigned int version ) const { // save ID fileBuffer.PutUnsignedInt( m_id ); // save attribute flags fileBuffer.PutInt( m_attributeFlags ); // save extent of area fileBuffer.Put( &m_nwCorner, 3*sizeof(float) ); fileBuffer.Put( &m_seCorner, 3*sizeof(float) ); // save heights of implicit corners fileBuffer.PutFloat( m_neZ ); fileBuffer.PutFloat( m_swZ ); // save connections to adjacent areas // in the enum order NORTH, EAST, SOUTH, WEST for( int d=0; d<NUM_DIRECTIONS; d++ ) { // save number of connections for this direction unsigned int count = m_connect[d].Count(); fileBuffer.PutUnsignedInt( count ); FOR_EACH_VEC( m_connect[d], it ) { NavConnect connect = m_connect[d][ it ]; fileBuffer.PutUnsignedInt( connect.area->m_id ); } }
//----------------------------------------------------------------------------- // Purpose: Saves a bit string to the given file // Input : // Output : //----------------------------------------------------------------------------- void SaveBitString(const int *pInts, int nInts, CUtlBuffer& buf) { buf.EnsureCapacity( buf.TellPut() + (sizeof( int )*nInts) ); for (int i=0;i<nInts;i++) { buf.PutInt( pInts[i] ); } }
bool CBaseGameStats::SaveToFileNOW( bool bForceSyncWrite /* = false */ ) { if ( !StatsTrackingIsFullyEnabled() ) return false; // this code path is only for old format stats. Products that use new format take a different path. if ( !gamestats->UseOldFormat() ) return false; CUtlBuffer buf; buf.PutShort( GAMESTATS_FILE_VERSION ); buf.Put( s_szPseudoUniqueID, 16 ); if( ShouldTrackStandardStats() ) m_BasicStats.SaveToBuffer( buf ); else buf.PutInt( GAMESTATS_STANDARD_NOT_SAVED ); gamestats->AppendCustomDataToSaveBuffer( buf ); char fullpath[ 512 ] = { 0 }; if ( filesystem->FileExists( GetStatSaveFileName(), GAMESTATS_PATHID ) ) { filesystem->RelativePathToFullPath( GetStatSaveFileName(), GAMESTATS_PATHID, fullpath, sizeof( fullpath ) ); } else { // filename is local to game dir for Steam, so we need to prepend game dir for regular file save char gamePath[256]; engine->GetGameDir( gamePath, 256 ); Q_StripTrailingSlash( gamePath ); Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", gamePath, GetStatSaveFileName() ); Q_strlower( fullpath ); Q_FixSlashes( fullpath ); } // StatsLog( "SaveToFileNOW '%s'\n", fullpath ); if( CBGSDriver.m_bShuttingDown || bForceSyncWrite ) //write synchronously { filesystem->WriteFile( fullpath, GAMESTATS_PATHID, buf ); StatsLog( "Shut down wrote to '%s'\n", fullpath ); } else { // Allocate memory for async system to use (and free afterward!!!) size_t nBufferSize = buf.TellPut(); void *pMem = malloc(nBufferSize); CUtlBuffer statsBuffer( pMem, nBufferSize ); statsBuffer.Put( buf.Base(), nBufferSize ); // Write data async filesystem->AsyncWrite( fullpath, statsBuffer.Base(), statsBuffer.TellPut(), true, false ); } return true; }
void CNetworkStringTableContainer::WriteStringTables( CUtlBuffer& buf ) { int numTables = m_Tables.Size(); buf.PutInt( numTables ); for ( int i = 0; i < numTables; i++ ) { CNetworkStringTable *table = m_Tables[ i ]; buf.PutString( table->GetTableName() ); table->WriteStringTable( buf ); } }
bool Serialize( CUtlBuffer &buf, const int &src ) { if ( buf.IsText() ) { buf.Printf( "%d", src ); } else { buf.PutInt( src ); } return buf.IsValid(); }
void CCompiledKeyValuesWriter::WriteFiles( CUtlBuffer &buf ) { int c = m_Files.Count(); buf.PutInt( c ); for ( int i = 0; i < c; ++i ) { KVFile_t &file = m_Files[ i ]; buf.PutShort( file.filename ); buf.PutShort( file.firstElement ); buf.PutShort( file.numElements ); } }
void CCompiledKeyValuesWriter::WriteData( CUtlBuffer& buf ) { int c = m_Data.Count(); buf.PutInt( c ); for ( int i = 0; i < c; ++i ) { KVInfo_t &info = m_Data[ i ]; buf.PutShort( info.key ); buf.PutShort( info.value ); buf.PutShort( info.GetParent() ); buf.PutChar( info.IsSubTree() ? 1 : 0 ); } }
void BasicGameStats_t::SaveToBuffer( CUtlBuffer& buf ) { buf.PutInt( m_nSecondsToCompleteGame ); m_Summary.SaveToBuffer( buf ); int c = m_MapTotals.Count(); buf.PutInt( c ); for ( int i = m_MapTotals.First(); i != m_MapTotals.InvalidIndex(); i = m_MapTotals.Next( i ) ) { char const *name = m_MapTotals.GetElementName( i ); BasicGameStatsRecord_t &rec = m_MapTotals[ i ]; buf.PutString( name ); rec.SaveToBuffer( buf ); } buf.PutChar( (char)m_nHL2ChaptureUnlocked ); buf.PutChar( m_bSteam ? 1 : 0 ); buf.PutChar( m_bCyberCafe ? 1 : 0 ); buf.PutShort( (short)m_nDXLevel ); }
void BasicGameStatsRecord_t::SaveToBuffer( CUtlBuffer &buf ) { buf.PutInt( m_nCount ); buf.PutInt( m_nSeconds ); buf.PutInt( m_nCommentary ); buf.PutInt( m_nHDR ); buf.PutInt( m_nCaptions ); for ( int i = 0; i < 3; ++i ) { buf.PutInt( m_nSkill[ i ] ); } buf.PutChar( m_bSteam ? 1 : 0 ); buf.PutChar( m_bCyberCafe ? 1 : 0 ); buf.PutInt( m_nDeaths ); }
void CNetworkStringTable::WriteStringTable( CUtlBuffer& buf ) { int numstrings = GetNumStrings(); buf.PutInt( numstrings ); for ( int i = 0 ; i < numstrings; i++ ) { buf.PutString( GetString( i ) ); int userDataSize; const void *pUserData = GetStringUserData( i, &userDataSize ); if ( userDataSize > 0 ) { buf.PutChar( 1 ); buf.PutShort( (short)userDataSize ); buf.Put( pUserData, userDataSize ); } else { buf.PutChar( 0 ); } } }
void CCompiledKeyValuesWriter::WriteStringTable( CUtlBuffer& buf ) { int i; CUtlVector< int > offsets; CUtlBuffer stringBuffer; offsets.AddToTail( stringBuffer.TellPut() ); stringBuffer.PutString( "" ); // save all the rest int c = m_StringTable.GetNumStrings(); for ( i = 1; i < c; i++) { offsets.AddToTail( stringBuffer.TellPut() ); stringBuffer.PutString( m_StringTable.String( i ) ); } buf.Put( offsets.Base(), offsets.Count() * sizeof( int ) ); buf.PutInt( stringBuffer.TellPut() ); buf.Put( stringBuffer.Base(), stringBuffer.TellPut() ); }
// This is a compressed save of just the data needed to drive phonemes in the engine (no word / sentence text, etc ) //----------------------------------------------------------------------------- // Purpose: // Input : buf - //----------------------------------------------------------------------------- void CSentence::CacheSaveToBuffer( CUtlBuffer& buf, int version ) { Assert( !buf.IsText() ); Assert( m_bIsCached ); int i; unsigned short pcount = GetRuntimePhonemeCount(); // header if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) { buf.PutChar( version ); buf.PutChar( 0 ); buf.PutChar( 0 ); buf.PutChar( 0 ); buf.PutInt( pcount ); } else { buf.PutChar( version ); buf.PutShort( pcount ); } // phoneme if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) { for ( i = 0; i < pcount; ++i ) { const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); Assert( phoneme ); buf.PutInt( phoneme->GetPhonemeCode() ); buf.PutFloat( phoneme->GetStartTime() ); buf.PutFloat( phoneme->GetEndTime() ); } } else { for ( i = 0; i < pcount; ++i ) { const CBasePhonemeTag *phoneme = GetRuntimePhoneme( i ); Assert( phoneme ); buf.PutShort( phoneme->GetPhonemeCode() ); buf.PutFloat( phoneme->GetStartTime() ); buf.PutFloat( phoneme->GetEndTime() ); } } // emphasis samples and voice duck int c = m_EmphasisSamples.Count(); Assert( c <= 32767 ); if ( version == CACHED_SENTENCE_VERSION_ALIGNED ) { buf.PutInt( c ); for ( i = 0; i < c; i++ ) { CEmphasisSample *sample = &m_EmphasisSamples[i]; Assert( sample ); buf.PutFloat( sample->time ); buf.PutFloat( sample->value ); } buf.PutInt( GetVoiceDuck() ? 1 : 0 ); } else { buf.PutShort( c ); for ( i = 0; i < c; i++ ) { CEmphasisSample *sample = &m_EmphasisSamples[i]; Assert( sample ); buf.PutFloat( sample->time ); short scaledValue = clamp( (short)( sample->value * 32767 ), (short)0, (short)32767 ); buf.PutShort( scaledValue ); } buf.PutChar( GetVoiceDuck() ? 1 : 0 ); } }
//----------------------------------------------------------------------------- // Purpose: Finds the value of a particular server variable //----------------------------------------------------------------------------- bool CServerRemoteAccess::LookupValue(const char *variable, CUtlBuffer &value) { Assert(value.IsText()); // first see if it's a cvar const char *strval = LookupStringValue(variable); if (strval) { value.PutString(strval); value.PutChar(0); } else if (!stricmp(variable, "stats")) { char stats[512]; GetStatsString(stats, sizeof(stats)); value.PutString(stats); value.PutChar(0); } else if (!stricmp(variable, "banlist")) { // returns a list of banned users and ip's GetUserBanList(value); } else if (!stricmp(variable, "playerlist")) { GetPlayerList(value); } else if (!stricmp(variable, "maplist")) { GetMapList(value); } else if (!stricmp(variable, "uptime")) { int timeSeconds = (int)(Plat_FloatTime()); value.PutInt(timeSeconds); value.PutChar(0); } else if (!stricmp(variable, "ipaddress")) { char addr[25]; Q_snprintf( addr, sizeof(addr), "%s:%i", net_local_adr.ToString(true), sv.GetUDPPort()); value.PutString( addr ); value.PutChar(0); } else if (!stricmp(variable, "mapcycle")) { ConVarRef mapcycle( "mapcyclefile" ); if ( mapcycle.IsValid() ) { // send the mapcycle list file FileHandle_t f = g_pFileSystem->Open(mapcycle.GetString(), "rb" ); if ( f == FILESYSTEM_INVALID_HANDLE ) return true; int len = g_pFileSystem->Size(f); char *mapcycleData = (char *)_alloca( len+1 ); if ( len && g_pFileSystem->Read( mapcycleData, len, f ) ) { mapcycleData[len] = 0; // Make sure it's null terminated. value.PutString((const char *)mapcycleData); value.PutChar(0); } else { value.PutString( "" ); value.PutChar(0); } g_pFileSystem->Close( f ); } } else { // value not found, null terminate value.PutChar(0); return false; } return true; }
bool KeyValues::WriteAsBinary(CUtlBuffer &buffer) { if (buffer.IsText()) return false; if (!buffer.IsValid()) return false; for (KeyValues *dat = this; dat != NULL; dat = dat->m_pPeer) { buffer.PutUnsignedChar(dat->m_iDataType); buffer.PutString(dat->GetName()); switch (dat->m_iDataType) { case TYPE_NONE: { dat->m_pSub->WriteAsBinary(buffer); break; } case TYPE_STRING: { if (dat->m_sValue && *(dat->m_sValue)) { buffer.PutString(dat->m_sValue); } else { buffer.PutString(""); } break; } case TYPE_WSTRING: { Assert(!"TYPE_WSTRING"); break; } case TYPE_INT: { buffer.PutInt(dat->m_iValue); break; } case TYPE_UINT64: { buffer.PutDouble(*((double *)dat->m_sValue)); break; } case TYPE_FLOAT: { buffer.PutFloat(dat->m_flValue); break; } case TYPE_COLOR: { buffer.PutUnsignedChar(dat->m_Color[0]); buffer.PutUnsignedChar(dat->m_Color[1]); buffer.PutUnsignedChar(dat->m_Color[2]); buffer.PutUnsignedChar(dat->m_Color[3]); break; } case TYPE_PTR: { buffer.PutUnsignedInt((int)dat->m_pValue); } default: { break; } } } buffer.PutUnsignedChar(TYPE_NUMTYPES); return buffer.IsValid(); }
bool CServerRemoteAccess::LookupValue(const char *variable, CUtlBuffer &value) { const char* strval = LookupStringValue(variable); if (strval) { value.PutString(strval); value.PutChar(0); return true; } if (!Q_stricmp(variable, "stats")) { char stats[512]; GetStatsString(stats, sizeof(stats)); value.PutString(stats); value.PutChar(0); return true; } if (!Q_stricmp(variable, "banlist")) { GetUserBanList(value); return true; } if (!Q_stricmp(variable, "playerlist")) { GetPlayerList(value); return true; } if (!Q_stricmp(variable, "maplist")) { GetMapList(value); return true; } if (!Q_stricmp(variable, "uptime")) { value.PutInt(int(Sys_FloatTime() - Host_GetStartTime())); value.PutChar(0); return true; } if (!Q_stricmp(variable, "ipaddress")) { value.PutString(NET_AdrToString(net_local_adr)); value.PutChar(0); return true; } if (!Q_stricmp(variable, "mapcycle")) { int len; void* mapcyclelist = COM_LoadFileForMe(mapcyclefile.string, &len); if (mapcyclelist && len) { value.PutString((char*)mapcyclelist); value.PutChar(0); COM_FreeFile(mapcyclelist); } return true; } value.PutChar(0); return false; }
void VMPI_DistributeLightData() { if ( !g_bUseMPI ) return; if ( g_bMPIMaster ) { const char *pVirtualFilename = "--plightdata--"; CUtlBuffer lightFaceData; // write out the light data lightFaceData.EnsureCapacity( pdlightdata->Count() + (numfaces * (MAXLIGHTMAPS+sizeof(int))) ); Q_memcpy( lightFaceData.PeekPut(), pdlightdata->Base(), pdlightdata->Count() ); lightFaceData.SeekPut( CUtlBuffer::SEEK_HEAD, pdlightdata->Count() ); // write out the relevant face info into the stream for ( int i = 0; i < numfaces; i++ ) { for ( int j = 0; j < MAXLIGHTMAPS; j++ ) { lightFaceData.PutChar(g_pFaces[i].styles[j]); } lightFaceData.PutInt(g_pFaces[i].lightofs); } VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, lightFaceData.Base(), lightFaceData.TellMaxPut() ); char cPacketID[2] = { VMPI_VRAD_PACKET_ID, VMPI_SUBPACKETID_PLIGHTDATA_RESULTS }; VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT ); } else { VMPI_SetCurrentStage( "VMPI_DistributeLightData" ); // Wait until we've received the filename from the master. while ( g_LightResultsFilename.Count() == 0 ) { VMPI_DispatchNextMessage(); } // Open FileHandle_t fp = g_pFileSystem->Open( g_LightResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID ); if ( !fp ) Error( "Can't open '%s' to read lighting info.", g_LightResultsFilename.Base() ); int size = g_pFileSystem->Size( fp ); int faceSize = (numfaces*(MAXLIGHTMAPS+sizeof(int))); if ( size > faceSize ) { int lightSize = size - faceSize; CUtlBuffer faceData; pdlightdata->EnsureCount( lightSize ); faceData.EnsureCapacity( faceSize ); g_pFileSystem->Read( pdlightdata->Base(), lightSize, fp ); g_pFileSystem->Read( faceData.Base(), faceSize, fp ); g_pFileSystem->Close( fp ); faceData.SeekPut( CUtlBuffer::SEEK_HEAD, faceSize ); // write out the face data for ( int i = 0; i < numfaces; i++ ) { for ( int j = 0; j < MAXLIGHTMAPS; j++ ) { g_pFaces[i].styles[j] = faceData.GetChar(); } g_pFaces[i].lightofs = faceData.GetInt(); } } } }
void WritePHXFile( const char *pName, const phyfile_t &file ) { if ( file.header.size != sizeof(file.header) || file.collide.solidCount <= 0 ) return; CUtlBuffer out; char outName[1024]; Q_snprintf( outName, sizeof(outName), "%s", pName ); Q_SetExtension( outName, ".phx", sizeof(outName) ); simplifyparams_t params; params.Defaults(); params.tolerance = (file.collide.solidCount > 1) ? 4.0f : 2.0f; // single solids constraint to AABB for placement help params.addAABBToSimplifiedHull = (file.collide.solidCount == 1) ? true : false; params.mergeConvexElements = true; params.mergeConvexTolerance = 0.025f; Q_FixSlashes(outName); Q_strlower(outName); char *pSearch = Q_strstr( outName,"models\\" ); if ( pSearch ) { char keyname[1024]; pSearch += strlen("models\\"); Q_StripExtension( pSearch, keyname, sizeof(keyname) ); OverrideDefaultsForModel( keyname, params ); } out.Put( &file.header, sizeof(file.header) ); int outSize = 0; bool bStoreSolidNames = file.collide.solidCount > 1 ? true : false; bStoreSolidNames = bStoreSolidNames || HasMultipleBones(outName); vcollide_t *pNewCollide = ConvertVCollideToPHX( &file.collide, params, &outSize, false, bStoreSolidNames); g_TotalOut += file.fileSize; for ( int i = 0; i < pNewCollide->solidCount; i++ ) { int collideSize = physcollision->CollideSize( pNewCollide->solids[i] ); out.PutInt( collideSize ); char *pMem = new char[collideSize]; physcollision->CollideWrite( pMem, pNewCollide->solids[i] ); out.Put( pMem, collideSize ); delete[] pMem; } if (!g_bQuiet) { Msg("%s Compressed %d (%d text) to %d (%d text)\n", outName, file.fileSize, file.collide.descSize, out.TellPut(), pNewCollide->descSize ); } out.Put( pNewCollide->pKeyValues, pNewCollide->descSize ); g_TotalCompress += out.TellPut(); #if 0 //Msg("OLD:\n-----------------------------------\n%s\n", file.collide.pKeyValues ); CPackedPhysicsDescription *pPacked = physcollision->CreatePackedDesc( pNewCollide->pKeyValues, pNewCollide->descSize ); Msg("NEW:\n-----------------------------------\n" ); for ( int i = 0; i < pPacked->m_solidCount; i++ ) { solid_t solid; pPacked->GetSolid( &solid, i ); Msg("index %d\n", solid.index ); Msg("name %s\n", solid.name ); Msg("mass %.2f\n", solid.params.mass ); Msg("surfaceprop %s\n", solid.surfaceprop); Msg("damping %.2f\n", solid.params.damping ); Msg("rotdamping %.2f\n", solid.params.rotdamping ); Msg("drag %.2f\n", solid.params.dragCoefficient ); Msg("inertia %.2f\n", solid.params.inertia ); Msg("volume %.2f\n", solid.params.volume ); } #endif DestroyPHX( pNewCollide ); if ( !g_pFullFileSystem->WriteFile( outName, NULL, out ) ) Warning("Can't write file: %s\n", outName ); }
//----------------------------------------------------------------------------- // A Scene image file contains all the compiled .XCD //----------------------------------------------------------------------------- bool CSceneImage::CreateSceneImageFile( CUtlBuffer &targetBuffer, char const *pchModPath, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *pStatus ) { CUtlVector<fileList_t> vcdFileList; CUtlSymbolTable vcdSymbolTable( 0, 32, true ); Msg( "\n" ); // get all the VCD files according to the seacrh paths char searchPaths[512]; g_pFullFileSystem->GetSearchPath( "GAME", false, searchPaths, sizeof( searchPaths ) ); char *pPath = strtok( searchPaths, ";" ); while ( pPath ) { int currentCount = vcdFileList.Count(); char szPath[MAX_PATH]; V_ComposeFileName( pPath, "scenes/*.vcd", szPath, sizeof( szPath ) ); scriptlib->FindFiles( szPath, true, vcdFileList ); Msg( "Scenes: Searching '%s' - Found %d scenes.\n", szPath, vcdFileList.Count() - currentCount ); pPath = strtok( NULL, ";" ); } if ( !vcdFileList.Count() ) { Msg( "Scenes: No Scene Files found!\n" ); return false; } // iterate and convert all the VCD files bool bGameIsTF = V_stristr( pchModPath, "\\tf" ) != NULL; for ( int i=0; i<vcdFileList.Count(); i++ ) { const char *pFilename = vcdFileList[i].fileName.String(); const char *pSceneName = V_stristr( pFilename, "scenes\\" ); if ( !pSceneName ) { continue; } if ( !bLittleEndian && bGameIsTF && V_stristr( pSceneName, "high\\" ) ) { continue; } // process files in order they would be found in search paths // i.e. skipping later processed files that match an earlier conversion UtlSymId_t symbol = vcdSymbolTable.Find( pSceneName ); if ( symbol == UTL_INVAL_SYMBOL ) { vcdSymbolTable.AddString( pSceneName ); pStatus->UpdateStatus( pFilename, bQuiet, i, vcdFileList.Count() ); if ( !CreateTargetFile_VCD( pFilename, "", false, bLittleEndian ) ) { Error( "CreateSceneImageFile: Failed on '%s' conversion!\n", pFilename ); } } } if ( !g_SceneFiles.Count() ) { // nothing to do return true; } Msg( "Scenes: Finalizing %d unique scenes.\n", g_SceneFiles.Count() ); // get the string pool CUtlVector< unsigned int > stringOffsets; CUtlBuffer stringPool; g_ChoreoStringPool.GetTableAndPool( stringOffsets, stringPool ); if ( !bQuiet ) { Msg( "Scenes: String Table: %d bytes\n", stringOffsets.Count() * sizeof( int ) ); Msg( "Scenes: String Pool: %d bytes\n", stringPool.TellMaxPut() ); } // first header, then lookup table, then string pool blob int stringPoolStart = sizeof( SceneImageHeader_t ) + stringOffsets.Count() * sizeof( int ); // then directory int sceneEntryStart = stringPoolStart + stringPool.TellMaxPut(); // then variable sized summaries int sceneSummaryStart = sceneEntryStart + g_SceneFiles.Count() * sizeof( SceneImageEntry_t ); // then variable sized compiled binary scene data int sceneDataStart = 0; // construct header SceneImageHeader_t imageHeader = { 0 }; imageHeader.nId = SCENE_IMAGE_ID; imageHeader.nVersion = SCENE_IMAGE_VERSION; imageHeader.nNumScenes = g_SceneFiles.Count(); imageHeader.nNumStrings = stringOffsets.Count(); imageHeader.nSceneEntryOffset = sceneEntryStart; if ( !bLittleEndian ) { imageHeader.nId = BigLong( imageHeader.nId ); imageHeader.nVersion = BigLong( imageHeader.nVersion ); imageHeader.nNumScenes = BigLong( imageHeader.nNumScenes ); imageHeader.nNumStrings = BigLong( imageHeader.nNumStrings ); imageHeader.nSceneEntryOffset = BigLong( imageHeader.nSceneEntryOffset ); } targetBuffer.Put( &imageHeader, sizeof( imageHeader ) ); // header is immediately followed by string table and pool for ( int i = 0; i < stringOffsets.Count(); i++ ) { unsigned int offset = stringPoolStart + stringOffsets[i]; if ( !bLittleEndian ) { offset = BigLong( offset ); } targetBuffer.PutInt( offset ); } Assert( stringPoolStart == targetBuffer.TellMaxPut() ); targetBuffer.Put( stringPool.Base(), stringPool.TellMaxPut() ); // construct directory CUtlSortVector< SceneImageEntry_t, CSceneImageEntryLessFunc > imageDirectory; imageDirectory.EnsureCapacity( g_SceneFiles.Count() ); // build directory // directory is linear sorted by filename checksum for later binary search for ( int i = 0; i < g_SceneFiles.Count(); i++ ) { SceneImageEntry_t imageEntry = { 0 }; // name needs to be normalized for determinstic later CRC name calc // calc crc based on scenes\anydir\anyscene.vcd char szCleanName[MAX_PATH]; V_strncpy( szCleanName, g_SceneFiles[i].fileName.String(), sizeof( szCleanName ) ); V_strlower( szCleanName ); V_FixSlashes( szCleanName ); char *pName = V_stristr( szCleanName, "scenes\\" ); if ( !pName ) { // must have scenes\ in filename Error( "CreateSceneImageFile: Unexpected lack of scenes prefix on %s\n", g_SceneFiles[i].fileName.String() ); } CRC32_t crcFilename = CRC32_ProcessSingleBuffer( pName, strlen( pName ) ); imageEntry.crcFilename = crcFilename; // temp store an index to its file, fixup later, necessary to access post sort imageEntry.nDataOffset = i; if ( imageDirectory.Find( imageEntry ) != imageDirectory.InvalidIndex() ) { // filename checksums must be unique or runtime binary search would be bogus Error( "CreateSceneImageFile: Unexpected filename checksum collision!\n" ); } imageDirectory.Insert( imageEntry ); } // determine sort order and start of data after dynamic summaries CUtlVector< int > writeOrder; writeOrder.EnsureCapacity( g_SceneFiles.Count() ); sceneDataStart = sceneSummaryStart; for ( int i = 0; i < imageDirectory.Count(); i++ ) { // reclaim offset, indicates write order of scene file int iScene = imageDirectory[i].nDataOffset; writeOrder.AddToTail( iScene ); // march past each variable sized summary to determine start of scene data int numSounds = g_SceneFiles[iScene].soundList.Count(); sceneDataStart += sizeof( SceneImageSummary_t ) + ( numSounds - 1 ) * sizeof( int ); } // finalize and write directory Assert( sceneEntryStart == targetBuffer.TellMaxPut() ); int nSummaryOffset = sceneSummaryStart; int nDataOffset = sceneDataStart; for ( int i = 0; i < imageDirectory.Count(); i++ ) { int iScene = writeOrder[i]; imageDirectory[i].nDataOffset = nDataOffset; imageDirectory[i].nDataLength = g_SceneFiles[iScene].compiledBuffer.TellMaxPut(); imageDirectory[i].nSceneSummaryOffset = nSummaryOffset; if ( !bLittleEndian ) { imageDirectory[i].crcFilename = BigLong( imageDirectory[i].crcFilename ); imageDirectory[i].nDataOffset = BigLong( imageDirectory[i].nDataOffset ); imageDirectory[i].nDataLength = BigLong( imageDirectory[i].nDataLength ); imageDirectory[i].nSceneSummaryOffset = BigLong( imageDirectory[i].nSceneSummaryOffset ); } targetBuffer.Put( &imageDirectory[i], sizeof( SceneImageEntry_t ) ); int numSounds = g_SceneFiles[iScene].soundList.Count(); nSummaryOffset += sizeof( SceneImageSummary_t ) + (numSounds - 1) * sizeof( int ); nDataOffset += g_SceneFiles[iScene].compiledBuffer.TellMaxPut(); } // finalize and write summaries Assert( sceneSummaryStart == targetBuffer.TellMaxPut() ); for ( int i = 0; i < imageDirectory.Count(); i++ ) { int iScene = writeOrder[i]; int msecs = g_SceneFiles[iScene].msecs; int soundCount = g_SceneFiles[iScene].soundList.Count(); if ( !bLittleEndian ) { msecs = BigLong( msecs ); soundCount = BigLong( soundCount ); } targetBuffer.PutInt( msecs ); targetBuffer.PutInt( soundCount ); for ( int j = 0; j < g_SceneFiles[iScene].soundList.Count(); j++ ) { int soundId = g_SceneFiles[iScene].soundList[j]; if ( !bLittleEndian ) { soundId = BigLong( soundId ); } targetBuffer.PutInt( soundId ); } } // finalize and write data Assert( sceneDataStart == targetBuffer.TellMaxPut() ); for ( int i = 0; i < imageDirectory.Count(); i++ ) { int iScene = writeOrder[i]; targetBuffer.Put( g_SceneFiles[iScene].compiledBuffer.Base(), g_SceneFiles[iScene].compiledBuffer.TellMaxPut() ); } if ( !bQuiet ) { Msg( "Scenes: Final size: %.2f MB\n", targetBuffer.TellMaxPut() / (1024.0f * 1024.0f ) ); } // cleanup g_SceneFiles.Purge(); return true; }