|
|
//====== Copyright � 1996-2005, 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 = "scenes/scenes" PLATFORM_EXT ".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 * RESTRICT 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->m_fLastSpeakSecs = 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->m_fLastSpeakSecs = pSummary->GetDurToSpeechEnd(); 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
// Many vcd's in CSGO have a '.' in the filename, which breaks this call
// We're going to assume that all filenames have the correct extension
// 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; CLZMA lzma; bool bIsCompressed; bIsCompressed = lzma.IsCompressed( pData ); if ( bIsCompressed ) { int originalSize = lzma.GetActualSize( pData ); if ( pSceneData ) { int nMaxLen = *pSceneLength; if ( originalSize <= nMaxLen ) { lzma.Uncompress( pData, pSceneData ); } else { unsigned char *pOutputData = (unsigned char *)malloc( originalSize ); lzma.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 );
|