Team Fortress 2 Source Code as on 22/4/2020
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.

335 lines
8.7 KiB

  1. //========= Copyright 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 = IsX360() ? "scenes/scenes.360.image" : "scenes/scenes.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 *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->numSounds = 0;
  139. return false;
  140. }
  141. // get scene summary
  142. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  143. SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
  144. pData->sceneId = iScene;
  145. pData->msecs = pSummary->msecs;
  146. pData->numSounds = pSummary->numSounds;
  147. return true;
  148. }
  149. short CSceneFileCache::GetSceneCachedSound( int iScene, int iSound )
  150. {
  151. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  152. if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
  153. {
  154. // huh?, image file not present or bad index
  155. return -1;
  156. }
  157. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  158. SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
  159. if ( iSound < 0 || iSound >= pSummary->numSounds )
  160. {
  161. // bad index
  162. Assert( 0 );
  163. return -1;
  164. }
  165. return pSummary->soundStrings[iSound];
  166. }
  167. const char *CSceneFileCache::GetSceneString( short stringId )
  168. {
  169. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  170. if ( !pHeader || stringId < 0 || stringId >= pHeader->nNumStrings )
  171. {
  172. // huh?, image file not present, or index bad
  173. return NULL;
  174. }
  175. return pHeader->String( stringId );
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Returns -1 if not found, otherwise [0..n] index.
  179. //-----------------------------------------------------------------------------
  180. int CSceneFileCache::FindSceneInImage( const char *pSceneName )
  181. {
  182. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  183. if ( !pHeader )
  184. {
  185. return -1;
  186. }
  187. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  188. char szCleanName[MAX_PATH];
  189. V_strncpy( szCleanName, pSceneName, sizeof( szCleanName ) );
  190. V_strlower( szCleanName );
  191. #ifdef POSIX
  192. V_FixSlashes( szCleanName, '\\' );
  193. #else
  194. V_FixSlashes( szCleanName );
  195. #endif
  196. V_SetExtension( szCleanName, ".vcd", sizeof( szCleanName ) );
  197. CRC32_t crcFilename = CRC32_ProcessSingleBuffer( szCleanName, strlen( szCleanName ) );
  198. // use binary search, entries are sorted by ascending crc
  199. int nLowerIdx = 1;
  200. int nUpperIdx = pHeader->nNumScenes;
  201. for ( ;; )
  202. {
  203. if ( nUpperIdx < nLowerIdx )
  204. {
  205. return -1;
  206. }
  207. else
  208. {
  209. int nMiddleIndex = ( nLowerIdx + nUpperIdx )/2;
  210. CRC32_t nProbe = pEntries[nMiddleIndex-1].crcFilename;
  211. if ( crcFilename < nProbe )
  212. {
  213. nUpperIdx = nMiddleIndex - 1;
  214. }
  215. else
  216. {
  217. if ( crcFilename > nProbe )
  218. {
  219. nLowerIdx = nMiddleIndex + 1;
  220. }
  221. else
  222. {
  223. return nMiddleIndex - 1;
  224. }
  225. }
  226. }
  227. }
  228. return -1;
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Returns true if success, false otherwise. Caller must free ouput scene data
  232. //-----------------------------------------------------------------------------
  233. bool CSceneFileCache::GetSceneDataFromImage( const char *pFileName, int iScene, byte *pSceneData, size_t *pSceneLength )
  234. {
  235. SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
  236. if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
  237. {
  238. if ( pSceneData )
  239. {
  240. *pSceneData = NULL;
  241. }
  242. if ( pSceneLength )
  243. {
  244. *pSceneLength = 0;
  245. }
  246. return false;
  247. }
  248. SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
  249. unsigned char *pData = (unsigned char *)pHeader + pEntries[iScene].nDataOffset;
  250. bool bIsCompressed;
  251. bIsCompressed = CLZMA::IsCompressed( pData );
  252. if ( bIsCompressed )
  253. {
  254. int originalSize = CLZMA::GetActualSize( pData );
  255. if ( pSceneData )
  256. {
  257. int nMaxLen = *pSceneLength;
  258. if ( originalSize <= nMaxLen )
  259. {
  260. CLZMA::Uncompress( pData, pSceneData );
  261. }
  262. else
  263. {
  264. unsigned char *pOutputData = (unsigned char *)malloc( originalSize );
  265. CLZMA::Uncompress( pData, pOutputData );
  266. V_memcpy( pSceneData, pOutputData, nMaxLen );
  267. free( pOutputData );
  268. }
  269. }
  270. if ( pSceneLength )
  271. {
  272. *pSceneLength = originalSize;
  273. }
  274. }
  275. else
  276. {
  277. if ( pSceneData )
  278. {
  279. size_t nCountToCopy = min(*pSceneLength, (size_t)pEntries[iScene].nDataLength );
  280. V_memcpy( pSceneData, pData, nCountToCopy );
  281. }
  282. if ( pSceneLength )
  283. {
  284. *pSceneLength = (size_t)pEntries[iScene].nDataLength;
  285. }
  286. }
  287. return true;
  288. }
  289. static CSceneFileCache g_SceneFileCache;
  290. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSceneFileCache, ISceneFileCache, SCENE_FILE_CACHE_INTERFACE_VERSION, g_SceneFileCache );