Counter Strike : Global Offensive Source Code
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.

800 lines
23 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "sceneimage.h"
  9. #include "choreoscene.h"
  10. #include "iscenetokenprocessor.h"
  11. #include "scenefilecache/SceneImageFile.h"
  12. #include "lzma/lzma.h"
  13. #include "tier1/utlbuffer.h"
  14. #include "tier1/UtlStringMap.h"
  15. #include "tier1/utlvector.h"
  16. #include "tier1/utlsortvector.h"
  17. #include "scriplib.h"
  18. #include "cmdlib.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. class CSceneImage : public ISceneImage
  22. {
  23. public:
  24. virtual bool CreateSceneImageFile( CUtlBuffer &targetBuffer, char const *pchModPath, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *Status );
  25. // This will update the scenes.image file, or create a new one if it doesn't exist
  26. virtual bool UpdateSceneImageFile( CUtlBuffer &targetBuffer, char const *pchModPath, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *Status, CUtlString *pFilesToUpdate, int nUpdateCount );
  27. private:
  28. bool WriteSceneImageFile( CUtlBuffer &targetBuffer, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *pStatus );
  29. };
  30. static CSceneImage g_SceneImage;
  31. ISceneImage *g_pSceneImage = &g_SceneImage;
  32. struct SceneFile_t
  33. {
  34. SceneFile_t()
  35. {
  36. msecs = 0;
  37. lastspeak_msecs = 0;
  38. crcFileName = (CRC32_t)0u;
  39. }
  40. // This gets used when we load back in from disk
  41. CRC32_t crcFileName;
  42. // Otherwise, it's this
  43. CUtlString fileName;
  44. CUtlBuffer compiledBuffer;
  45. unsigned int msecs;
  46. unsigned int lastspeak_msecs;
  47. CUtlVector< short > soundList;
  48. };
  49. CUtlVector< SceneFile_t > g_SceneFiles;
  50. //-----------------------------------------------------------------------------
  51. // Helper for parsing scene data file
  52. //-----------------------------------------------------------------------------
  53. class CSceneTokenProcessor : public ISceneTokenProcessor
  54. {
  55. public:
  56. const char *CurrentToken( void )
  57. {
  58. return token;
  59. }
  60. bool GetToken( bool crossline )
  61. {
  62. return ::GetToken( crossline ) ? true : false;
  63. }
  64. bool TokenAvailable( void )
  65. {
  66. return ::TokenAvailable() ? true : false;
  67. }
  68. void SetFilename( const char *pFilename )
  69. {
  70. m_Filename = pFilename;
  71. }
  72. void Error( const char *fmt, ... )
  73. {
  74. char string[2048];
  75. va_list argptr;
  76. va_start( argptr, fmt );
  77. Q_vsnprintf( string, sizeof(string), fmt, argptr );
  78. va_end( argptr );
  79. Warning( "%s: %s", m_Filename.Get(), string );
  80. Assert( 0 );
  81. }
  82. private:
  83. CUtlString m_Filename;
  84. };
  85. static CSceneTokenProcessor g_SceneTokenProcessor;
  86. ISceneTokenProcessor *tokenprocessor = &g_SceneTokenProcessor;
  87. // a simple case insensitive string pool
  88. // the final pool contains all the unique strings seperated by a null
  89. class CChoreoStringPool : public IChoreoStringPool
  90. {
  91. public:
  92. CChoreoStringPool() : m_StringMap( true )
  93. {
  94. m_nOffset = 0;
  95. }
  96. // Returns a valid id into the string table
  97. virtual short FindOrAddString( const char *pString )
  98. {
  99. int stringId = m_StringMap.Find( pString );
  100. if ( stringId != m_StringMap.InvalidIndex() )
  101. {
  102. // found in pool
  103. return stringId;
  104. }
  105. int &nOffset = m_StringMap[pString];
  106. nOffset = m_nOffset;
  107. // advance by string and null
  108. m_nOffset += strlen( pString ) + 1;
  109. stringId = m_StringMap.Find( pString );
  110. Assert( stringId >= 0 && stringId <= 32767 );
  111. return stringId;
  112. }
  113. virtual bool GetString( short stringId, char *buff, int buffSize )
  114. {
  115. if ( stringId < 0 || stringId >= m_StringMap.GetNumStrings() )
  116. {
  117. V_strncpy( buff, "", buffSize );
  118. return false;
  119. }
  120. V_strncpy( buff, m_StringMap.String( stringId ), buffSize );
  121. return true;
  122. }
  123. int GetNumStrings()
  124. {
  125. return m_StringMap.GetNumStrings();
  126. }
  127. unsigned int GetPoolSize()
  128. {
  129. return m_nOffset;
  130. }
  131. // build the final pool
  132. void GetTableAndPool( CUtlVector< unsigned int > &offsets, CUtlBuffer &buffer )
  133. {
  134. offsets.Purge();
  135. buffer.Purge();
  136. offsets.EnsureCapacity( m_StringMap.GetNumStrings() );
  137. buffer.EnsureCapacity( m_nOffset );
  138. unsigned int currentOffset = 0;
  139. for ( int i = 0; i < m_StringMap.GetNumStrings(); i++ )
  140. {
  141. offsets.AddToTail( currentOffset );
  142. const char *pString = m_StringMap.String( i );
  143. buffer.Put( pString, strlen( pString ) + 1 );
  144. currentOffset += strlen( pString ) + 1;
  145. }
  146. Assert( currentOffset == m_nOffset );
  147. // align string pool to end on dword boundary
  148. while ( buffer.TellPut() & 0x03 )
  149. {
  150. buffer.PutChar( '\0' );
  151. m_nOffset++;
  152. }
  153. }
  154. void DumpPool()
  155. {
  156. for ( int i = 0; i < m_StringMap.GetNumStrings(); i++ )
  157. {
  158. const char *pString;
  159. pString = m_StringMap.String( i );
  160. Msg( "%s\n", pString );
  161. }
  162. }
  163. void Reset()
  164. {
  165. m_StringMap.Purge();
  166. m_nOffset = 0;
  167. }
  168. private:
  169. CUtlStringMap< int > m_StringMap;
  170. unsigned int m_nOffset;
  171. };
  172. CChoreoStringPool g_ChoreoStringPool;
  173. //-----------------------------------------------------------------------------
  174. // Helper for crawling events to determine sounds
  175. //-----------------------------------------------------------------------------
  176. void FindSoundsInEvent( CChoreoEvent *pEvent, CUtlVector< short >& soundList )
  177. {
  178. if ( !pEvent || pEvent->GetType() != CChoreoEvent::SPEAK )
  179. return;
  180. unsigned short stringId = g_ChoreoStringPool.FindOrAddString( pEvent->GetParameters() );
  181. if ( soundList.Find( stringId ) == soundList.InvalidIndex() )
  182. {
  183. soundList.AddToTail( stringId );
  184. }
  185. if ( pEvent->GetCloseCaptionType() == CChoreoEvent::CC_MASTER )
  186. {
  187. char tok[ CChoreoEvent::MAX_CCTOKEN_STRING ];
  188. if ( pEvent->GetPlaybackCloseCaptionToken( tok, sizeof( tok ) ) )
  189. {
  190. stringId = g_ChoreoStringPool.FindOrAddString( tok );
  191. if ( soundList.Find( stringId ) == soundList.InvalidIndex() )
  192. {
  193. soundList.AddToTail( stringId );
  194. }
  195. }
  196. }
  197. }
  198. static void ChoreScene_MsgDummy( const char *fmt, ... )
  199. {
  200. }
  201. bool UpdateTargetFile_VCD( SceneFile_t *pEntry, const char *pSourceName, const char *pTargetName, bool bWriteToZip, bool bLittleEndian )
  202. {
  203. pEntry->crcFileName = (CRC32_t)0u;
  204. pEntry->fileName.Set( pSourceName );
  205. CUtlBuffer sourceBuf;
  206. if ( !scriptlib->ReadFileToBuffer( pSourceName, sourceBuf ) )
  207. {
  208. return false;
  209. }
  210. CRC32_t crcSource;
  211. CRC32_Init( &crcSource );
  212. CRC32_ProcessBuffer( &crcSource, sourceBuf.Base(), sourceBuf.TellPut() );
  213. CRC32_Final( &crcSource );
  214. ParseFromMemory( (char *)sourceBuf.Base(), sourceBuf.TellPut() );
  215. g_SceneTokenProcessor.SetFilename( pSourceName );
  216. void (*pfnMsgLoad)( const char *fmt, ... ) = ChoreScene_MsgDummy;
  217. #ifndef DBGFLAG_STRINGS_STRIP
  218. pfnMsgLoad = Msg;
  219. #endif
  220. CChoreoScene *pChoreoScene = ChoreoLoadScene( pSourceName, NULL, &g_SceneTokenProcessor, pfnMsgLoad );
  221. if ( !pChoreoScene )
  222. {
  223. return false;
  224. }
  225. // Walk all events looking for SPEAK events
  226. CChoreoEvent *pEvent;
  227. for ( int i = 0; i < pChoreoScene->GetNumEvents(); ++i )
  228. {
  229. pEvent = pChoreoScene->GetEvent( i );
  230. FindSoundsInEvent( pEvent, pEntry->soundList );
  231. }
  232. // calc duration
  233. pEntry->msecs = (unsigned int)( pChoreoScene->FindStopTime() * 1000.0f + 0.5f );
  234. pEntry->lastspeak_msecs = (unsigned int)( pChoreoScene->FindLastSpeakTime() * 1000.0f + 0.5f );
  235. pEntry->compiledBuffer.Clear();
  236. // compile to binary buffer
  237. pEntry->compiledBuffer.SetBigEndian( !bLittleEndian );
  238. pChoreoScene->SaveToBinaryBuffer( pEntry->compiledBuffer, crcSource, &g_ChoreoStringPool );
  239. unsigned int compressedSize;
  240. unsigned char *pCompressedBuffer = LZMA_Compress( (unsigned char *)pEntry->compiledBuffer.Base(), pEntry->compiledBuffer.TellPut(), &compressedSize );
  241. if ( pCompressedBuffer )
  242. {
  243. // replace the compiled buffer with the compressed version
  244. pEntry->compiledBuffer.Purge();
  245. pEntry->compiledBuffer.EnsureCapacity( compressedSize );
  246. pEntry->compiledBuffer.Put( pCompressedBuffer, compressedSize );
  247. free( pCompressedBuffer );
  248. }
  249. delete pChoreoScene;
  250. return true;
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Create binary compiled version of VCD. Stores to a dictionary for later
  254. // post processing
  255. //-----------------------------------------------------------------------------
  256. bool CreateTargetFile_VCD( const char *pSourceName, const char *pTargetName, bool bWriteToZip, bool bLittleEndian )
  257. {
  258. int iScene = g_SceneFiles.AddToTail();
  259. bool bRet = UpdateTargetFile_VCD( &g_SceneFiles[ iScene ], pSourceName, pTargetName, bWriteToZip, bLittleEndian );
  260. if ( !bRet )
  261. {
  262. g_SceneFiles.Remove( iScene );
  263. }
  264. return bRet;
  265. }
  266. class CSceneImageEntryLessFunc
  267. {
  268. public:
  269. bool Less( const SceneImageEntry_t &entryLHS, const SceneImageEntry_t &entryRHS, void *pCtx )
  270. {
  271. return entryLHS.crcFilename < entryRHS.crcFilename;
  272. }
  273. };
  274. //-----------------------------------------------------------------------------
  275. // A Scene image file contains all the compiled .XCD
  276. //-----------------------------------------------------------------------------
  277. bool CSceneImage::CreateSceneImageFile( CUtlBuffer &targetBuffer, char const *pchModPath, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *pStatus )
  278. {
  279. CUtlVector<fileList_t> vcdFileList;
  280. CUtlSymbolTable vcdSymbolTable( 0, 32, true );
  281. Msg( "\n" );
  282. // get all the VCD files according to the seacrh paths
  283. char searchPaths[512];
  284. g_pFullFileSystem->GetSearchPath( "GAME", false, searchPaths, sizeof( searchPaths ) );
  285. char *pPath = strtok( searchPaths, ";" );
  286. while ( pPath )
  287. {
  288. int currentCount;
  289. char szPath[MAX_PATH];
  290. V_ComposeFileName( pPath, "scenes/*.vcd", szPath, sizeof( szPath ) );
  291. scriptlib->FindFiles( szPath, true, vcdFileList );
  292. currentCount = vcdFileList.Count();
  293. Msg( "Scenes: Searching '%s' - Found %d scenes.\n", szPath, vcdFileList.Count() - currentCount );
  294. pPath = strtok( NULL, ";" );
  295. }
  296. if ( !vcdFileList.Count() )
  297. {
  298. Msg( "Scenes: No Scene Files found!\n" );
  299. return false;
  300. }
  301. // iterate and convert all the VCD files
  302. bool bGameIsTF = V_stristr( pchModPath, "\\tf" ) != NULL;
  303. for ( int i=0; i<vcdFileList.Count(); i++ )
  304. {
  305. const char *pFilename = vcdFileList[i].fileName.String();
  306. const char *pSceneName = V_stristr( pFilename, "scenes\\" );
  307. if ( !pSceneName )
  308. {
  309. continue;
  310. }
  311. if ( !bLittleEndian && bGameIsTF && V_stristr( pSceneName, "high\\" ) )
  312. {
  313. continue;
  314. }
  315. // process files in order they would be found in search paths
  316. // i.e. skipping later processed files that match an earlier conversion
  317. UtlSymId_t symbol = vcdSymbolTable.Find( pSceneName );
  318. if ( symbol == UTL_INVAL_SYMBOL )
  319. {
  320. vcdSymbolTable.AddString( pSceneName );
  321. pStatus->UpdateStatus( pFilename, bQuiet, i, vcdFileList.Count() );
  322. if ( !CreateTargetFile_VCD( pFilename, "", false, bLittleEndian ) )
  323. {
  324. Error( "CreateSceneImageFile: Failed on '%s' conversion!\n", pFilename );
  325. }
  326. }
  327. }
  328. if ( !g_SceneFiles.Count() )
  329. {
  330. // nothing to do
  331. return true;
  332. }
  333. return WriteSceneImageFile( targetBuffer, bLittleEndian, bQuiet, pStatus );
  334. }
  335. bool CSceneImage::WriteSceneImageFile( CUtlBuffer &targetBuffer, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *pStatus )
  336. {
  337. Msg( "Scenes: Finalizing %d unique scenes.\n", g_SceneFiles.Count() );
  338. // get the string pool
  339. CUtlVector< unsigned int > stringOffsets;
  340. CUtlBuffer stringPool;
  341. g_ChoreoStringPool.GetTableAndPool( stringOffsets, stringPool );
  342. if ( !bQuiet )
  343. {
  344. Msg( "Scenes: String Table: %d bytes\n", stringOffsets.Count() * sizeof( int ) );
  345. Msg( "Scenes: String Pool: %d bytes\n", stringPool.TellPut() );
  346. }
  347. // first header, then lookup table, then string pool blob
  348. int stringPoolStart = sizeof( SceneImageHeader_t ) + stringOffsets.Count() * sizeof( int );
  349. // then directory
  350. int sceneEntryStart = stringPoolStart + stringPool.TellPut();
  351. // then variable sized summaries
  352. int sceneSummaryStart = sceneEntryStart + g_SceneFiles.Count() * sizeof( SceneImageEntry_t );
  353. // then variable sized compiled binary scene data
  354. int sceneDataStart = 0;
  355. // construct header
  356. SceneImageHeader_t imageHeader = { 0 };
  357. imageHeader.nId = SCENE_IMAGE_ID;
  358. imageHeader.nVersion = SCENE_IMAGE_VERSION;
  359. imageHeader.nNumScenes = g_SceneFiles.Count();
  360. imageHeader.nNumStrings = stringOffsets.Count();
  361. imageHeader.nSceneEntryOffset = sceneEntryStart;
  362. if ( !bLittleEndian )
  363. {
  364. imageHeader.nId = BigLong( imageHeader.nId );
  365. imageHeader.nVersion = BigLong( imageHeader.nVersion );
  366. imageHeader.nNumScenes = BigLong( imageHeader.nNumScenes );
  367. imageHeader.nNumStrings = BigLong( imageHeader.nNumStrings );
  368. imageHeader.nSceneEntryOffset = BigLong( imageHeader.nSceneEntryOffset );
  369. }
  370. targetBuffer.Put( &imageHeader, sizeof( imageHeader ) );
  371. // header is immediately followed by string table and pool
  372. for ( int i = 0; i < stringOffsets.Count(); i++ )
  373. {
  374. unsigned int offset = stringPoolStart + stringOffsets[i];
  375. if ( !bLittleEndian )
  376. {
  377. offset = BigLong( offset );
  378. }
  379. targetBuffer.PutInt( offset );
  380. }
  381. Assert( stringPoolStart == targetBuffer.TellPut() );
  382. targetBuffer.Put( stringPool.Base(), stringPool.TellPut() );
  383. // construct directory
  384. CUtlSortVector< SceneImageEntry_t, CSceneImageEntryLessFunc > imageDirectory;
  385. imageDirectory.EnsureCapacity( g_SceneFiles.Count() );
  386. // build directory
  387. // directory is linear sorted by filename checksum for later binary search
  388. for ( int i = 0; i < g_SceneFiles.Count(); i++ )
  389. {
  390. SceneImageEntry_t imageEntry = { 0 };
  391. CRC32_t crcFilename = g_SceneFiles[ i ].crcFileName;
  392. if ( crcFilename == 0 )
  393. {
  394. Assert( Q_strlen( g_SceneFiles[i].fileName.String() ) > 0 );
  395. // name needs to be normalized for determinstic later CRC name calc
  396. // calc crc based on scenes\anydir\anyscene.vcd
  397. char szCleanName[MAX_PATH];
  398. V_strncpy( szCleanName, g_SceneFiles[i].fileName.String(), sizeof( szCleanName ) );
  399. V_strlower( szCleanName );
  400. V_FixSlashes( szCleanName );
  401. char *pName = V_stristr( szCleanName, "scenes\\" );
  402. if ( !pName )
  403. {
  404. // must have scenes\ in filename
  405. Error( "CreateSceneImageFile: Unexpected lack of scenes prefix on %s\n", g_SceneFiles[i].fileName.String() );
  406. }
  407. crcFilename = CRC32_ProcessSingleBuffer( pName, strlen( pName ) );
  408. }
  409. imageEntry.crcFilename = crcFilename;
  410. // temp store an index to its file, fixup later, necessary to access post sort
  411. imageEntry.nDataOffset = i;
  412. if ( imageDirectory.Find( imageEntry ) != imageDirectory.InvalidIndex() )
  413. {
  414. // filename checksums must be unique or runtime binary search would be bogus
  415. Error( "CreateSceneImageFile: Unexpected filename checksum collision!\n" );
  416. }
  417. imageDirectory.Insert( imageEntry );
  418. }
  419. // determine sort order and start of data after dynamic summaries
  420. CUtlVector< int > writeOrder;
  421. writeOrder.EnsureCapacity( g_SceneFiles.Count() );
  422. sceneDataStart = sceneSummaryStart;
  423. for ( int i = 0; i < imageDirectory.Count(); i++ )
  424. {
  425. // reclaim offset, indicates write order of scene file
  426. int iScene = imageDirectory[i].nDataOffset;
  427. writeOrder.AddToTail( iScene );
  428. // march past each variable sized summary to determine start of scene data
  429. int numSounds = g_SceneFiles[iScene].soundList.Count();
  430. sceneDataStart += sizeof( SceneImageSummary_t ) + ( numSounds - 1 ) * sizeof( int );
  431. }
  432. // finalize and write directory
  433. Assert( sceneEntryStart == targetBuffer.TellPut() );
  434. int nSummaryOffset = sceneSummaryStart;
  435. int nDataOffset = sceneDataStart;
  436. for ( int i = 0; i < imageDirectory.Count(); i++ )
  437. {
  438. int iScene = writeOrder[i];
  439. imageDirectory[i].nDataOffset = nDataOffset;
  440. imageDirectory[i].nDataLength = g_SceneFiles[iScene].compiledBuffer.TellPut();
  441. imageDirectory[i].nSceneSummaryOffset = nSummaryOffset;
  442. if ( !bLittleEndian )
  443. {
  444. imageDirectory[i].crcFilename = BigLong( imageDirectory[i].crcFilename );
  445. imageDirectory[i].nDataOffset = BigLong( imageDirectory[i].nDataOffset );
  446. imageDirectory[i].nDataLength = BigLong( imageDirectory[i].nDataLength );
  447. imageDirectory[i].nSceneSummaryOffset = BigLong( imageDirectory[i].nSceneSummaryOffset );
  448. }
  449. targetBuffer.Put( &imageDirectory[i], sizeof( SceneImageEntry_t ) );
  450. int numSounds = g_SceneFiles[iScene].soundList.Count();
  451. nSummaryOffset += sizeof( SceneImageSummary_t ) + (numSounds - 1) * sizeof( int );
  452. nDataOffset += g_SceneFiles[iScene].compiledBuffer.TellPut();
  453. }
  454. // finalize and write summaries
  455. Assert( sceneSummaryStart == targetBuffer.TellPut() );
  456. for ( int i = 0; i < imageDirectory.Count(); i++ )
  457. {
  458. int iScene = writeOrder[i];
  459. int msecs = g_SceneFiles[iScene].msecs;
  460. int soundCount = g_SceneFiles[iScene].soundList.Count();
  461. unsigned int lastspeak = g_SceneFiles[iScene].lastspeak_msecs;
  462. Assert( lastspeak < msecs );
  463. if ( !bLittleEndian )
  464. {
  465. msecs = BigLong( msecs );
  466. soundCount = BigLong( soundCount );
  467. lastspeak = BigLong( lastspeak );
  468. }
  469. targetBuffer.PutInt( msecs );
  470. targetBuffer.PutInt( lastspeak );
  471. targetBuffer.PutInt( soundCount );
  472. for ( int j = 0; j < g_SceneFiles[iScene].soundList.Count(); j++ )
  473. {
  474. int soundId = g_SceneFiles[iScene].soundList[j];
  475. if ( !bLittleEndian )
  476. {
  477. soundId = BigLong( soundId );
  478. }
  479. targetBuffer.PutInt( soundId );
  480. }
  481. }
  482. // finalize and write data
  483. Assert( sceneDataStart == targetBuffer.TellPut() );
  484. for ( int i = 0; i < imageDirectory.Count(); i++ )
  485. {
  486. int iScene = writeOrder[i];
  487. targetBuffer.Put( g_SceneFiles[iScene].compiledBuffer.Base(), g_SceneFiles[iScene].compiledBuffer.TellPut() );
  488. }
  489. if ( !bQuiet )
  490. {
  491. Msg( "Scenes: Final size: %.2f MB\n", targetBuffer.TellPut() / (1024.0f * 1024.0f ) );
  492. }
  493. // cleanup
  494. g_SceneFiles.Purge();
  495. return true;
  496. }
  497. static int FindSceneByCRC( CRC32_t crcFilename )
  498. {
  499. // use binary search, entries are sorted by ascending crc
  500. int nLowerIdx = 1;
  501. int nUpperIdx = g_SceneFiles.Count();
  502. for ( ;; )
  503. {
  504. if ( nUpperIdx < nLowerIdx )
  505. {
  506. return -1;
  507. }
  508. else
  509. {
  510. int nMiddleIndex = ( nLowerIdx + nUpperIdx )/2;
  511. CRC32_t nProbe = g_SceneFiles[nMiddleIndex-1].crcFileName;
  512. if ( crcFilename < nProbe )
  513. {
  514. nUpperIdx = nMiddleIndex - 1;
  515. }
  516. else
  517. {
  518. if ( crcFilename > nProbe )
  519. {
  520. nLowerIdx = nMiddleIndex + 1;
  521. }
  522. else
  523. {
  524. return nMiddleIndex - 1;
  525. }
  526. }
  527. }
  528. }
  529. return -1;
  530. }
  531. // This will update the scenes.image file, or create a new one if it doesn't exist
  532. // The caller should pass in the existing .image file in targetBuffer!!!
  533. bool CSceneImage::UpdateSceneImageFile( CUtlBuffer &targetBuffer, char const *pchModPath, bool bLittleEndian, bool bQuiet, ISceneCompileStatus *pStatus, CUtlString *pFilesToUpdate, int nUpdateCount )
  534. {
  535. // Prime everything using existing data file
  536. if ( targetBuffer.TellPut() <= 0 )
  537. {
  538. return CreateSceneImageFile( targetBuffer, pchModPath, bLittleEndian, bQuiet, pStatus );
  539. }
  540. bool bSuccess = true;
  541. g_SceneFiles.Purge();
  542. g_ChoreoStringPool.Reset();
  543. // Rewind to start
  544. targetBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  545. // Load stuff
  546. SceneImageHeader_t imageHeader;
  547. targetBuffer.Get( &imageHeader, sizeof( imageHeader ) );
  548. if ( !bLittleEndian )
  549. {
  550. imageHeader.nId = BigLong( imageHeader.nId );
  551. imageHeader.nVersion = BigLong( imageHeader.nVersion );
  552. imageHeader.nNumScenes = BigLong( imageHeader.nNumScenes );
  553. imageHeader.nNumStrings = BigLong( imageHeader.nNumStrings );
  554. imageHeader.nSceneEntryOffset = BigLong( imageHeader.nSceneEntryOffset );
  555. }
  556. if ( imageHeader.nId != SCENE_IMAGE_ID )
  557. {
  558. bSuccess = false;
  559. }
  560. if ( imageHeader.nVersion != SCENE_IMAGE_VERSION )
  561. {
  562. bSuccess = false;
  563. }
  564. if ( bSuccess )
  565. {
  566. // Re-Build the string pool
  567. // first header, then lookup table, then string pool blob
  568. int stringPoolStart = sizeof( SceneImageHeader_t ) + imageHeader.nNumStrings * sizeof( int );
  569. // then directory
  570. int sceneEntryStart = imageHeader.nSceneEntryOffset;
  571. // unsigned int *pOffset = (unsigned int *)( (byte *)targetBuffer.Base() + targetBuffer.TellGet() );
  572. targetBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, stringPoolStart );
  573. char str[ 4096 ];
  574. for ( int i = 0; i < imageHeader.nNumStrings; ++i )
  575. {
  576. targetBuffer.GetString( str, sizeof( str ) );
  577. g_ChoreoStringPool.FindOrAddString( str );
  578. }
  579. // Now read in the file data
  580. targetBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, sceneEntryStart );
  581. // get scene summary
  582. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)targetBuffer.Base() + sceneEntryStart );
  583. for ( int i = 0; i < imageHeader.nNumScenes; ++i )
  584. {
  585. SceneImageEntry_t *pEntry = &pEntries[ i ];
  586. if ( !bLittleEndian )
  587. {
  588. pEntry->crcFilename = BigLong( pEntry->crcFilename );
  589. pEntry->nDataOffset = BigLong( pEntry->nDataOffset );
  590. pEntry->nDataLength = BigLong( pEntry->nDataLength );
  591. pEntry->nSceneSummaryOffset = BigLong( pEntry->nSceneSummaryOffset );
  592. }
  593. SceneImageSummary_t * RESTRICT pSummary = (SceneImageSummary_t *)( (byte *)targetBuffer.Base() + pEntry->nSceneSummaryOffset );
  594. unsigned char *pData = (unsigned char *)targetBuffer.Base() + pEntry->nDataOffset;
  595. // Now read in the data
  596. int idx = g_SceneFiles.AddToTail();
  597. SceneFile_t &scene = g_SceneFiles[ idx ];
  598. // We only load the crc based filenames for appending/replacing
  599. scene.crcFileName = pEntry->crcFilename;
  600. scene.compiledBuffer.Put( pData, pEntry->nDataLength );
  601. scene.msecs = pSummary->msecs;
  602. scene.lastspeak_msecs = pSummary->lastspeech_msecs;
  603. // Load sounds
  604. for ( int j = 0 ; j < pSummary->numSounds; ++j )
  605. {
  606. scene.soundList.AddToTail( pSummary->soundStrings[ j ] );
  607. }
  608. }
  609. }
  610. Assert( g_SceneFiles.Count() == imageHeader.nNumScenes );
  611. // Now validate that the scenes list is sorted correctly
  612. CRC32_t current = (CRC32_t)0;
  613. for ( int i = 0 ; i < g_SceneFiles.Count(); ++i )
  614. {
  615. CRC32_t crc = g_SceneFiles[ i ].crcFileName;
  616. Assert( crc != (CRC32_t)0 );
  617. if ( crc <= current )
  618. {
  619. Error( "UpdateSceneImageFile: Scene Files not in CRC order\n" );
  620. }
  621. current = crc;
  622. }
  623. // Now add the additional files
  624. bool bGameIsTF = V_stristr( pchModPath, "\\tf" ) != NULL;
  625. for ( int i = 0; i < nUpdateCount; ++i )
  626. {
  627. const char *pFilename = pFilesToUpdate[i].String();
  628. const char *pSceneName = V_stristr( pFilename, "scenes\\" );
  629. if ( !pSceneName )
  630. {
  631. continue;
  632. }
  633. if ( !bLittleEndian && bGameIsTF && V_stristr( pSceneName, "high\\" ) )
  634. {
  635. continue;
  636. }
  637. // name needs to be normalized for determinstic later CRC name calc
  638. // calc crc based on scenes\anydir\anyscene.vcd
  639. char szCleanName[MAX_PATH];
  640. V_strncpy( szCleanName, pFilename, sizeof( szCleanName ) );
  641. V_strlower( szCleanName );
  642. V_FixSlashes( szCleanName );
  643. char *pName = V_stristr( szCleanName, "scenes\\" );
  644. if ( !pName )
  645. {
  646. // must have scenes\ in filename
  647. Error( "UpdateSceneImageFile: Unexpected lack of scenes prefix on %s\n", pFilename );
  648. }
  649. CRC32_t crcFilename = CRC32_ProcessSingleBuffer( pName, strlen( pName ) );
  650. pStatus->UpdateStatus( pFilename, bQuiet, i, nUpdateCount );
  651. int idx = FindSceneByCRC( crcFilename );
  652. // Not found, append entry
  653. if ( idx == -1 )
  654. {
  655. if ( !CreateTargetFile_VCD( pFilename, "", false, bLittleEndian ) )
  656. {
  657. Error( "CreateSceneImageFile: Failed on '%s' conversion!\n", pFilename );
  658. }
  659. }
  660. else
  661. // Found it, let's just update entry
  662. {
  663. if ( !UpdateTargetFile_VCD( &g_SceneFiles[ idx ], pFilename, "", false, bLittleEndian ) )
  664. {
  665. Error( "CreateSceneImageFile: Failed on '%s' update!\n", pFilename );
  666. }
  667. }
  668. }
  669. // Now write the final data out
  670. targetBuffer.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  671. return WriteSceneImageFile( targetBuffer, bLittleEndian, bQuiet, pStatus );
  672. }