diff options
Diffstat (limited to 'scenefilecache/SceneFileCache.cpp')
| -rw-r--r-- | scenefilecache/SceneFileCache.cpp | 335 |
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 ); |