summaryrefslogtreecommitdiff
path: root/scenefilecache/SceneFileCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scenefilecache/SceneFileCache.cpp')
-rw-r--r--scenefilecache/SceneFileCache.cpp335
1 files changed, 335 insertions, 0 deletions
diff --git a/scenefilecache/SceneFileCache.cpp b/scenefilecache/SceneFileCache.cpp
new file mode 100644
index 0000000..18f6731
--- /dev/null
+++ b/scenefilecache/SceneFileCache.cpp
@@ -0,0 +1,335 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Cache for VCDs. PC async loads and uses the datacache to manage.
+// 360 uses a baked resident image of aggregated compiled VCDs.
+//
+//=============================================================================
+
+#include "scenefilecache/ISceneFileCache.h"
+#include "filesystem.h"
+#include "tier1/utldict.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/lzmaDecoder.h"
+#include "scenefilecache/SceneImageFile.h"
+#include "choreoscene.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+IFileSystem *filesystem = NULL;
+
+bool IsBufferBinaryVCD( char *pBuffer, int bufferSize )
+{
+ if ( bufferSize > 4 && *(int *)pBuffer == SCENE_BINARY_TAG )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+class CSceneFileCache : public CBaseAppSystem< ISceneFileCache >
+{
+public:
+ // IAppSystem
+ virtual bool Connect( CreateInterfaceFn factory );
+ virtual void Disconnect();
+ virtual InitReturnVal_t Init();
+ virtual void Shutdown();
+
+ // ISceneFileCache
+ // Physically reloads image from disk
+ virtual void Reload();
+
+ virtual size_t GetSceneBufferSize( char const *pFilename );
+ virtual bool GetSceneData( char const *pFilename, byte *buf, size_t bufsize );
+
+ // alternate resident image implementation
+ virtual bool GetSceneCachedData( char const *pFilename, SceneCachedData_t *pData );
+ virtual short GetSceneCachedSound( int iScene, int iSound );
+ virtual const char *GetSceneString( short stringId );
+
+private:
+ // alternate implementation - uses a resident baked image of the file cache, contains all the compiled VCDs
+ // single i/o read at startup to mount the image
+ int FindSceneInImage( const char *pSceneName );
+ bool GetSceneDataFromImage( const char *pSceneName, int iIndex, byte *pData, size_t *pLength );
+
+private:
+ CUtlBuffer m_SceneImageFile;
+};
+
+bool CSceneFileCache::Connect( CreateInterfaceFn factory )
+{
+ if ( (filesystem = (IFileSystem *)factory( FILESYSTEM_INTERFACE_VERSION,NULL )) == NULL )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CSceneFileCache::Disconnect()
+{
+}
+
+InitReturnVal_t CSceneFileCache::Init()
+{
+ const char *pSceneImageName = IsX360() ? "scenes/scenes.360.image" : "scenes/scenes.image";
+
+ if ( m_SceneImageFile.TellMaxPut() == 0 )
+ {
+ MEM_ALLOC_CREDIT();
+
+ if ( filesystem->ReadFile( pSceneImageName, "GAME", m_SceneImageFile ) )
+ {
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( pHeader->nId != SCENE_IMAGE_ID ||
+ pHeader->nVersion != SCENE_IMAGE_VERSION )
+ {
+ Error( "CSceneFileCache: Bad scene image file %s\n", pSceneImageName );
+ }
+ }
+ else
+ {
+ if ( IsX360() )
+ {
+ if ( filesystem->GetDVDMode() == DVDMODE_STRICT )
+ {
+ // mandatory
+ Error( "CSceneFileCache: Failed to load %s\n", pSceneImageName );
+ }
+ else
+ {
+ // relaxed
+ Warning( "CSceneFileCache: Failed to load %s, scene playback disabled.\n", pSceneImageName );
+ return INIT_OK;
+ }
+ }
+
+ m_SceneImageFile.Purge();
+ }
+ }
+
+ return INIT_OK;
+}
+
+void CSceneFileCache::Shutdown()
+{
+ m_SceneImageFile.Purge();
+}
+
+// Physically reloads image from disk
+void CSceneFileCache::Reload()
+{
+ Shutdown();
+ Init();
+}
+
+size_t CSceneFileCache::GetSceneBufferSize( char const *pFilename )
+{
+ size_t returnSize = 0;
+
+ char fn[MAX_PATH];
+ Q_strncpy( fn, pFilename, sizeof( fn ) );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+
+ GetSceneDataFromImage( pFilename, FindSceneInImage( fn ), NULL, &returnSize );
+ return returnSize;
+}
+
+bool CSceneFileCache::GetSceneData( char const *pFilename, byte *buf, size_t bufsize )
+{
+ Assert( pFilename );
+ Assert( buf );
+ Assert( bufsize > 0 );
+
+ char fn[MAX_PATH];
+ Q_strncpy( fn, pFilename, sizeof( fn ) );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+
+ size_t nLength = bufsize;
+ return GetSceneDataFromImage( pFilename, FindSceneInImage( fn ), buf, &nLength );
+}
+
+bool CSceneFileCache::GetSceneCachedData( char const *pFilename, SceneCachedData_t *pData )
+{
+ int iScene = FindSceneInImage( pFilename );
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
+ {
+ // not available
+ pData->sceneId = -1;
+ pData->msecs = 0;
+ pData->numSounds = 0;
+ return false;
+ }
+
+ // get scene summary
+ SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
+ SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
+
+ pData->sceneId = iScene;
+ pData->msecs = pSummary->msecs;
+ pData->numSounds = pSummary->numSounds;
+
+ return true;
+}
+
+short CSceneFileCache::GetSceneCachedSound( int iScene, int iSound )
+{
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
+ {
+ // huh?, image file not present or bad index
+ return -1;
+ }
+
+ SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
+ SceneImageSummary_t *pSummary = (SceneImageSummary_t *)( (byte *)pHeader + pEntries[iScene].nSceneSummaryOffset );
+ if ( iSound < 0 || iSound >= pSummary->numSounds )
+ {
+ // bad index
+ Assert( 0 );
+ return -1;
+ }
+
+ return pSummary->soundStrings[iSound];
+}
+
+const char *CSceneFileCache::GetSceneString( short stringId )
+{
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( !pHeader || stringId < 0 || stringId >= pHeader->nNumStrings )
+ {
+ // huh?, image file not present, or index bad
+ return NULL;
+ }
+
+ return pHeader->String( stringId );
+}
+
+//-----------------------------------------------------------------------------
+// Returns -1 if not found, otherwise [0..n] index.
+//-----------------------------------------------------------------------------
+int CSceneFileCache::FindSceneInImage( const char *pSceneName )
+{
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( !pHeader )
+ {
+ return -1;
+ }
+ SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
+
+ char szCleanName[MAX_PATH];
+
+ V_strncpy( szCleanName, pSceneName, sizeof( szCleanName ) );
+ V_strlower( szCleanName );
+#ifdef POSIX
+ V_FixSlashes( szCleanName, '\\' );
+#else
+ V_FixSlashes( szCleanName );
+#endif
+ V_SetExtension( szCleanName, ".vcd", sizeof( szCleanName ) );
+
+ CRC32_t crcFilename = CRC32_ProcessSingleBuffer( szCleanName, strlen( szCleanName ) );
+
+ // use binary search, entries are sorted by ascending crc
+ int nLowerIdx = 1;
+ int nUpperIdx = pHeader->nNumScenes;
+ for ( ;; )
+ {
+ if ( nUpperIdx < nLowerIdx )
+ {
+ return -1;
+ }
+ else
+ {
+ int nMiddleIndex = ( nLowerIdx + nUpperIdx )/2;
+ CRC32_t nProbe = pEntries[nMiddleIndex-1].crcFilename;
+ if ( crcFilename < nProbe )
+ {
+ nUpperIdx = nMiddleIndex - 1;
+ }
+ else
+ {
+ if ( crcFilename > nProbe )
+ {
+ nLowerIdx = nMiddleIndex + 1;
+ }
+ else
+ {
+ return nMiddleIndex - 1;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if success, false otherwise. Caller must free ouput scene data
+//-----------------------------------------------------------------------------
+bool CSceneFileCache::GetSceneDataFromImage( const char *pFileName, int iScene, byte *pSceneData, size_t *pSceneLength )
+{
+ SceneImageHeader_t *pHeader = (SceneImageHeader_t *)m_SceneImageFile.Base();
+ if ( !pHeader || iScene < 0 || iScene >= pHeader->nNumScenes )
+ {
+ if ( pSceneData )
+ {
+ *pSceneData = NULL;
+ }
+ if ( pSceneLength )
+ {
+ *pSceneLength = 0;
+ }
+ return false;
+ }
+
+ SceneImageEntry_t *pEntries = (SceneImageEntry_t *)( (byte *)pHeader + pHeader->nSceneEntryOffset );
+ unsigned char *pData = (unsigned char *)pHeader + pEntries[iScene].nDataOffset;
+ bool bIsCompressed;
+ bIsCompressed = CLZMA::IsCompressed( pData );
+ if ( bIsCompressed )
+ {
+ int originalSize = CLZMA::GetActualSize( pData );
+ if ( pSceneData )
+ {
+ int nMaxLen = *pSceneLength;
+ if ( originalSize <= nMaxLen )
+ {
+ CLZMA::Uncompress( pData, pSceneData );
+ }
+ else
+ {
+ unsigned char *pOutputData = (unsigned char *)malloc( originalSize );
+ CLZMA::Uncompress( pData, pOutputData );
+ V_memcpy( pSceneData, pOutputData, nMaxLen );
+ free( pOutputData );
+ }
+ }
+ if ( pSceneLength )
+ {
+ *pSceneLength = originalSize;
+ }
+ }
+ else
+ {
+ if ( pSceneData )
+ {
+ size_t nCountToCopy = min(*pSceneLength, (size_t)pEntries[iScene].nDataLength );
+ V_memcpy( pSceneData, pData, nCountToCopy );
+ }
+ if ( pSceneLength )
+ {
+ *pSceneLength = (size_t)pEntries[iScene].nDataLength;
+ }
+ }
+ return true;
+}
+
+static CSceneFileCache g_SceneFileCache;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CSceneFileCache, ISceneFileCache, SCENE_FILE_CACHE_INTERFACE_VERSION, g_SceneFileCache );