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.

339 lines
8.9 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose: Cache for VCDs. PC async loads and uses the datacache to manage.
  4. // 360 uses a baked resident image of aggregated compiled VCDs.
  5. //
  6. //=============================================================================
  7. #include "scenefilecache/ISceneFileCache.h"
  8. #include "filesystem.h"
  9. #include "tier1/utldict.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "tier1/lzmaDecoder.h"
  12. #include "scenefilecache/SceneImageFile.h"
  13. #include "choreoscene.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. IFileSystem *filesystem = NULL;
  17. bool IsBufferBinaryVCD( char *pBuffer, int bufferSize )
  18. {
  19. if ( bufferSize > 4 && *(int *)pBuffer == SCENE_BINARY_TAG )
  20. {
  21. return true;
  22. }
  23. return false;
  24. }
  25. class CSceneFileCache : public CBaseAppSystem< ISceneFileCache >
  26. {
  27. public:
  28. // IAppSystem
  29. virtual bool Connect( CreateInterfaceFn factory );
  30. virtual void Disconnect();
  31. virtual InitReturnVal_t Init();
  32. virtual void Shutdown();
  33. // ISceneFileCache
  34. // Physically reloads image from disk
  35. virtual void Reload();
  36. virtual size_t GetSceneBufferSize( char const *pFilename );
  37. virtual bool GetSceneData( char const *pFilename, byte *buf, size_t bufsize );
  38. // alternate resident image implementation
  39. virtual bool GetSceneCachedData( char const *pFilename, SceneCachedData_t *pData );
  40. virtual short GetSceneCachedSound( int iScene, int iSound );
  41. virtual const char *GetSceneString( short stringId );
  42. private:
  43. // alternate implementation - uses a resident baked image of the file cache, contains all the compiled VCDs
  44. // single i/o read at startup to mount the image
  45. int FindSceneInImage( const char *pSceneName );
  46. bool GetSceneDataFromImage( const char *pSceneName, int iIndex, byte *pData, size_t *pLength );
  47. private:
  48. CUtlBuffer m_SceneImageFile;
  49. };
  50. bool CSceneFileCache::Connect( CreateInterfaceFn factory )
  51. {
  52. if ( (filesystem = (IFileSystem *)factory( FILESYSTEM_INTERFACE_VERSION,NULL )) == NULL )
  53. {
  54. return false;
  55. }
  56. return true;
  57. }
  58. void CSceneFileCache::Disconnect()
  59. {
  60. }
  61. InitReturnVal_t CSceneFileCache::Init()
  62. {
  63. const char *pSceneImageName = "scenes/scenes" PLATFORM_EXT ".image";
  64. if ( m_SceneImageFile.TellMaxPut() == 0 )
  65. {
  66. MEM_ALLOC_CREDIT();
  67. if ( filesystem->ReadFile( pSceneImageName, "GAME", m_SceneImageFile ) )
  68. {
  69. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  70. if ( pHeader->nId != SCENE_IMAGE_ID ||
  71. pHeader->nVersion != SCENE_IMAGE_VERSION )
  72. {
  73. Error( "CSceneFileCache: Bad scene image file %s\n", pSceneImageName );
  74. }
  75. }
  76. else
  77. {
  78. if ( IsX360() )
  79. {
  80. if ( filesystem->GetDVDMode() == DVDMODE_STRICT )
  81. {
  82. // mandatory
  83. Error( "CSceneFileCache: Failed to load %s\n", pSceneImageName );
  84. }
  85. else
  86. {
  87. // relaxed
  88. Warning( "CSceneFileCache: Failed to load %s, scene playback disabled.\n", pSceneImageName );
  89. return INIT_OK;
  90. }
  91. }
  92. m_SceneImageFile.Purge();
  93. }
  94. }
  95. return INIT_OK;
  96. }
  97. void CSceneFileCache::Shutdown()
  98. {
  99. m_SceneImageFile.Purge();
  100. }
  101. // Physically reloads image from disk
  102. void CSceneFileCache::Reload()
  103. {
  104. Shutdown();
  105. Init();
  106. }
  107. size_t CSceneFileCache::GetSceneBufferSize( char const *pFilename )
  108. {
  109. size_t returnSize = 0;
  110. char fn[MAX_PATH];
  111. Q_strncpy( fn, pFilename, sizeof( fn ) );
  112. Q_FixSlashes( fn );
  113. Q_strlower( fn );
  114. GetSceneDataFromImage( pFilename, FindSceneInImage( fn ), NULL, &returnSize );
  115. return returnSize;
  116. }
  117. bool CSceneFileCache::GetSceneData( char const *pFilename, byte *buf, size_t bufsize )
  118. {
  119. Assert( pFilename );
  120. Assert( buf );
  121. Assert( bufsize > 0 );
  122. char fn[MAX_PATH];
  123. Q_strncpy( fn, pFilename, sizeof( fn ) );
  124. Q_FixSlashes( fn );
  125. Q_strlower( fn );
  126. size_t nLength = bufsize;
  127. return GetSceneDataFromImage( pFilename, FindSceneInImage( fn ), buf, &nLength );
  128. }
  129. bool CSceneFileCache::GetSceneCachedData( char const *pFilename, SceneCachedData_t * RESTRICT pData )
  130. {
  131. int iScene = FindSceneInImage( pFilename );
  132. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  133. if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
  134. {
  135. // not available
  136. pData->sceneId = -1;
  137. pData->msecs = 0;
  138. pData->m_fLastSpeakSecs = 0;
  139. pData->numSounds = 0;
  140. return false;
  141. }
  142. // get scene summary
  143. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  144. SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
  145. pData->sceneId = iScene;
  146. pData->msecs = pSummary->msecs;
  147. pData->m_fLastSpeakSecs = pSummary->GetDurToSpeechEnd();
  148. pData->numSounds = pSummary->numSounds;
  149. return true;
  150. }
  151. short CSceneFileCache::GetSceneCachedSound( int iScene, int iSound )
  152. {
  153. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  154. if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
  155. {
  156. // huh?, image file not present or bad index
  157. return -1;
  158. }
  159. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  160. SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
  161. if ( iSound < 0 || iSound >= pSummary->numSounds )
  162. {
  163. // bad index
  164. Assert( 0 );
  165. return -1;
  166. }
  167. return pSummary->soundStrings[iSound];
  168. }
  169. const char *CSceneFileCache::GetSceneString( short stringId )
  170. {
  171. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  172. if ( !pHeader || stringId < 0 || stringId >= pHeader->nNumStrings )
  173. {
  174. // huh?, image file not present, or index bad
  175. return NULL;
  176. }
  177. return pHeader->String( stringId );
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Returns -1 if not found, otherwise [0..n] index.
  181. //-----------------------------------------------------------------------------
  182. int CSceneFileCache::FindSceneInImage( const char *pSceneName )
  183. {
  184. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  185. if ( !pHeader )
  186. {
  187. return -1;
  188. }
  189. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  190. char szCleanName[MAX_PATH];
  191. V_strncpy( szCleanName, pSceneName, sizeof( szCleanName ) );
  192. V_strlower( szCleanName );
  193. #ifdef POSIX
  194. V_FixSlashes( szCleanName, '\\' );
  195. #else
  196. V_FixSlashes( szCleanName );
  197. #endif
  198. // Many vcd's in CSGO have a '.' in the filename, which breaks this call
  199. // We're going to assume that all filenames have the correct extension
  200. // V_SetExtension( szCleanName, ".vcd", sizeof( szCleanName ) );
  201. CRC32_t crcFilename = CRC32_ProcessSingleBuffer( szCleanName, strlen( szCleanName ) );
  202. // use binary search, entries are sorted by ascending crc
  203. int nLowerIdx = 1;
  204. int nUpperIdx = pHeader->nNumScenes;
  205. for ( ;; )
  206. {
  207. if ( nUpperIdx < nLowerIdx )
  208. {
  209. return -1;
  210. }
  211. else
  212. {
  213. int nMiddleIndex = ( nLowerIdx + nUpperIdx )/2;
  214. CRC32_t nProbe = pEntries[nMiddleIndex-1].crcFilename;
  215. if ( crcFilename < nProbe )
  216. {
  217. nUpperIdx = nMiddleIndex - 1;
  218. }
  219. else
  220. {
  221. if ( crcFilename > nProbe )
  222. {
  223. nLowerIdx = nMiddleIndex + 1;
  224. }
  225. else
  226. {
  227. return nMiddleIndex - 1;
  228. }
  229. }
  230. }
  231. }
  232. return -1;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Returns true if success, false otherwise. Caller must free ouput scene data
  236. //-----------------------------------------------------------------------------
  237. bool CSceneFileCache::GetSceneDataFromImage( const char *pFileName, int iScene, byte *pSceneData, size_t *pSceneLength )
  238. {
  239. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  240. if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
  241. {
  242. if ( pSceneData )
  243. {
  244. *pSceneData = NULL;
  245. }
  246. if ( pSceneLength )
  247. {
  248. *pSceneLength = 0;
  249. }
  250. return false;
  251. }
  252. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  253. unsigned char *pData = (unsigned char *)pHeader + pEntries[iScene].nDataOffset;
  254. CLZMA lzma;
  255. bool bIsCompressed;
  256. bIsCompressed = lzma.IsCompressed( pData );
  257. if ( bIsCompressed )
  258. {
  259. int originalSize = lzma.GetActualSize( pData );
  260. if ( pSceneData )
  261. {
  262. int nMaxLen = *pSceneLength;
  263. if ( originalSize <= nMaxLen )
  264. {
  265. lzma.Uncompress( pData, pSceneData );
  266. }
  267. else
  268. {
  269. unsigned char *pOutputData = (unsigned char *)malloc( originalSize );
  270. lzma.Uncompress( pData, pOutputData );
  271. V_memcpy( pSceneData, pOutputData, nMaxLen );
  272. free( pOutputData );
  273. }
  274. }
  275. if ( pSceneLength )
  276. {
  277. *pSceneLength = originalSize;
  278. }
  279. }
  280. else
  281. {
  282. if ( pSceneData )
  283. {
  284. size_t nCountToCopy = MIN(*pSceneLength, (size_t)pEntries[iScene].nDataLength );
  285. V_memcpy( pSceneData, pData, nCountToCopy );
  286. }
  287. if ( pSceneLength )
  288. {
  289. *pSceneLength = (size_t)pEntries[iScene].nDataLength;
  290. }
  291. }
  292. return true;
  293. }
  294. static CSceneFileCache g_SceneFileCache;
  295. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSceneFileCache, ISceneFileCache, SCENE_FILE_CACHE_INTERFACE_VERSION, g_SceneFileCache );