//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Filesystem abstraction for CSaveRestore - allows for storing temp save files
// either in memory or on disk.
//
//===========================================================================//
# ifdef _WIN32
# include "winerror.h"
# endif
# include "filesystem_engine.h"
# include "saverestore_filesystem.h"
# include "host_saverestore.h"
# include "host.h"
# include "sys.h"
# include "tier1/utlbuffer.h"
# include "tier1/lzss.h"
# include "tier1/convar.h"
# include "ixboxsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern ConVar save_spew ;
extern IXboxSystem * g_pXboxSystem ;
# define SaveMsg if ( !save_spew.GetBool() ) ; else Msg
void SaveInMemoryCallback ( IConVar * var , const char * pOldString , float flOldValue ) ;
# ifdef _X360
ConVar save_in_memory ( " save_in_memory " , " 1 " , 1 , " Set to 1 to save to memory instead of disk (Xbox 360) " , SaveInMemoryCallback ) ;
# else
ConVar save_in_memory ( " save_in_memory " , " 0 " , 0 , " Set to 1 to save to memory instead of disk (Xbox 360) " , SaveInMemoryCallback ) ;
# endif // _X360
# define INVALID_INDEX (GetDirectory().InvalidIndex())
enum { READ_ONLY , WRITE_ONLY } ;
static float g_fPrevSaveInMemoryValue ;
static bool SaveFileLessFunc ( const CUtlSymbol & lhs , const CUtlSymbol & rhs )
{
return lhs < rhs ;
}
//----------------------------------------------------------------------------
// Simulates the save directory in RAM.
//----------------------------------------------------------------------------
class CSaveDirectory
{
public :
CSaveDirectory ( )
{
m_Files . SetLessFunc ( SaveFileLessFunc ) ;
file_t dummy ;
dummy . name = m_SymbolTable . AddString ( " dummy " ) ;
m_Files . Insert ( dummy . name , dummy ) ;
}
~ CSaveDirectory ( )
{
int i = m_Files . FirstInorder ( ) ;
while ( m_Files . IsValidIndex ( i ) )
{
int idx = i ;
i = m_Files . NextInorder ( i ) ;
delete m_Files [ idx ] . pBuffer ;
delete m_Files [ idx ] . pCompressedBuffer ;
m_Files . RemoveAt ( idx ) ;
}
}
struct file_t
{
file_t ( )
{
pBuffer = NULL ;
pCompressedBuffer = NULL ;
nSize = 0 ;
nCompressedSize = NULL ;
}
int eType ;
CUtlSymbol name ;
unsigned int nSize ;
unsigned int nCompressedSize ;
CUtlBuffer * pBuffer ;
CUtlBuffer * pCompressedBuffer ;
} ;
CUtlSymbolTable m_SymbolTable ;
CUtlMap < CUtlSymbol , file_t > m_Files ;
} ;
typedef CSaveDirectory : : file_t SaveFile_t ;
//----------------------------------------------------------------------------
// CSaveRestoreFileSystem: Manipulates files in the CSaveDirectory
//----------------------------------------------------------------------------
class CSaveRestoreFileSystem : public ISaveRestoreFileSystem
{
public :
CSaveRestoreFileSystem ( )
{
m_pSaveDirectory = new CSaveDirectory ( ) ;
m_iContainerOpens = 0 ;
}
~ CSaveRestoreFileSystem ( )
{
delete m_pSaveDirectory ;
}
bool FileExists ( const char * pFileName , const char * pPathID = NULL ) ;
void RenameFile ( char const * pOldPath , char const * pNewPath , const char * pathID = NULL ) ;
void RemoveFile ( char const * pRelativePath , const char * pathID = NULL ) ;
FileHandle_t Open ( const char * pFileName , const char * pOptions , const char * pathID = NULL ) ;
void Close ( FileHandle_t ) ;
int Read ( void * pOutput , int size , FileHandle_t file ) ;
int Write ( void const * pInput , int size , FileHandle_t file ) ;
void Seek ( FileHandle_t file , int pos , FileSystemSeek_t method ) ;
unsigned int Tell ( FileHandle_t file ) ;
unsigned int Size ( FileHandle_t file ) ;
unsigned int Size ( const char * pFileName , const char * pPathID = NULL ) ;
void AsyncFinishAllWrites ( void ) ;
void AsyncRelease ( FSAsyncControl_t hControl ) ;
FSAsyncStatus_t AsyncWrite ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , bool bAppend , FSAsyncControl_t * pControl = NULL ) ;
FSAsyncStatus_t AsyncFinish ( FSAsyncControl_t hControl , bool wait = false ) ;
FSAsyncStatus_t AsyncAppend ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , FSAsyncControl_t * pControl = NULL ) ;
FSAsyncStatus_t AsyncAppendFile ( const char * pDestFileName , const char * pSrcFileName , FSAsyncControl_t * pControl = NULL ) ;
void DirectoryCopy ( const char * pPath , const char * pDestFileName , bool bIsXSave ) ;
void DirectorCopyToMemory ( const char * pPath , const char * pDestFileName ) ;
bool DirectoryExtract ( FileHandle_t pFile , int fileCount , bool bIsXSave ) ;
int DirectoryCount ( const char * pPath ) ;
void DirectoryClear ( const char * pPath , bool bIsXSave ) ;
void WriteSaveDirectoryToDisk ( void ) ;
void LoadSaveDirectoryFromDisk ( const char * pPath ) ;
void DumpSaveDirectory ( void ) ;
void Compress ( SaveFile_t * pFile ) ;
void Uncompress ( SaveFile_t * pFile ) ;
void AuditFiles ( void ) ;
bool LoadFileFromDisk ( const char * pFilename ) ;
private :
CSaveDirectory * m_pSaveDirectory ;
CUtlMap < CUtlSymbol , SaveFile_t > & GetDirectory ( void ) { return m_pSaveDirectory - > m_Files ; }
SaveFile_t & GetFile ( const int idx ) { return m_pSaveDirectory - > m_Files [ idx ] ; }
SaveFile_t & GetFile ( const FileHandle_t hFile ) { return GetFile ( ( uintp ) hFile ) ; }
FileHandle_t GetFileHandle ( const char * pFileName ) ;
int GetFileIndex ( const char * pFileName ) ;
bool HandleIsValid ( FileHandle_t hFile ) ;
unsigned int CompressedSize ( const char * pFileName ) ;
CUtlSymbol AddString ( const char * str )
{
char szString [ MAX_PATH ] ;
Q_strncpy ( szString , str , sizeof ( szString ) ) ;
return m_pSaveDirectory - > m_SymbolTable . AddString ( Q_strlower ( szString ) ) ;
}
const char * GetString ( CUtlSymbol & id ) { return m_pSaveDirectory - > m_SymbolTable . String ( id ) ; }
int m_iContainerOpens ;
} ;
//#define TEST_LZSS_WINDOW_SIZES
//----------------------------------------------------------------------------
// Compress the file data
//----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : Compress ( SaveFile_t * pFile )
{
pFile - > pCompressedBuffer - > Purge ( ) ;
# ifdef TEST_LZSS_WINDOW_SIZES
// Compress the data here
CLZSS compressor_test ;
CLZSS newcompressor_test ( 2048 ) ;
pFile - > nCompressedSize = 0 ;
float start = Plat_FloatTime ( ) ;
for ( int i = 0 ; i < 10 ; i + + )
{
uint32 sz ;
unsigned char * pCompressedBuffer = compressor_test . Compress (
( unsigned char * ) pFile - > pBuffer - > Base ( ) , pFile - > nSize , & sz ) ;
delete [ ] pCompressedBuffer ;
}
Warning ( " old compressor_test %f " , Plat_FloatTime ( ) - start ) ;
start = Plat_FloatTime ( ) ;
for ( int i = 0 ; i < 10 ; i + + )
{
uint32 sz ;
unsigned char * pCompressedBuffer = newcompressor_test . Compress (
( unsigned char * ) pFile - > pBuffer - > Base ( ) , pFile - > nSize , & sz ) ;
delete [ ] pCompressedBuffer ;
}
Warning ( " new compressor_test %f " , Plat_FloatTime ( ) - start ) ;
if ( 1 )
{
uint32 sz ;
uint32 sz1 ;
unsigned char * pNewCompressedBuffer = newcompressor_test . Compress (
( unsigned char * ) pFile - > pBuffer - > Base ( ) , pFile - > nSize , & sz ) ;
unsigned char * pOldCompressedBuffer = compressor_test . Compress (
( unsigned char * ) pFile - > pBuffer - > Base ( ) , pFile - > nSize , & sz1 ) ;
if ( ! pNewCompressedBuffer )
Warning ( " new no comp " ) ;
if ( ! pOldCompressedBuffer )
Warning ( " old no comp " ) ;
if ( pNewCompressedBuffer & & pOldCompressedBuffer )
{
if ( sz ! = sz1 )
Warning ( " new size = %d old = %d " , sz , sz1 ) ;
if ( memcmp ( pNewCompressedBuffer , pOldCompressedBuffer , sz ) )
Warning ( " data mismatch " ) ;
}
delete [ ] pOldCompressedBuffer ;
delete [ ] pNewCompressedBuffer ;
}
# endif
CLZSS compressor ( 2048 ) ;
unsigned char * pCompressedBuffer = compressor . Compress ( ( unsigned char * ) pFile - > pBuffer - > Base ( ) , pFile - > nSize , & pFile - > nCompressedSize ) ;
if ( pCompressedBuffer = = NULL )
{
// Just copy the buffer uncompressed
pFile - > pCompressedBuffer - > Put ( pFile - > pBuffer - > Base ( ) , pFile - > nSize ) ;
pFile - > nCompressedSize = pFile - > nSize ;
}
else
{
// Take the compressed buffer as our own
pFile - > pCompressedBuffer - > AssumeMemory ( pCompressedBuffer , pFile - > nCompressedSize , pFile - > nCompressedSize ) ; // ?
}
// end compression
pFile - > pCompressedBuffer - > SeekGet ( CUtlBuffer : : SEEK_HEAD , 0 ) ;
pFile - > pCompressedBuffer - > SeekPut ( CUtlBuffer : : SEEK_HEAD , pFile - > nCompressedSize ) ;
// Don't want the uncompressed memory hanging around
pFile - > pBuffer - > Purge ( ) ;
unsigned int srcBytes = pFile - > nSize ;
pFile - > nSize = 0 ;
unsigned int destBytes = pFile - > nCompressedSize ;
float percent = 0.f ;
if ( srcBytes )
percent = 100.0f * ( 1.0f - ( float ) destBytes / ( float ) srcBytes ) ;
SaveMsg ( " SIM: SaveDir: (%s) Compressed %d bytes to %d bytes. (%.0f%%) \n " , GetString ( pFile - > name ) , srcBytes , destBytes , percent ) ;
}
//----------------------------------------------------------------------------
// Uncompress the file data
//----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : Uncompress ( SaveFile_t * pFile )
{
pFile - > pBuffer - > Purge ( ) ;
// Uncompress the data here
CLZSS compressor ;
unsigned int nUncompressedSize = compressor . GetActualSize ( ( unsigned char * ) pFile - > pCompressedBuffer - > Base ( ) ) ;
if ( nUncompressedSize ! = 0 )
{
unsigned char * pUncompressBuffer = ( unsigned char * ) malloc ( nUncompressedSize ) ;
nUncompressedSize = compressor . Uncompress ( ( unsigned char * ) pFile - > pCompressedBuffer - > Base ( ) , pUncompressBuffer ) ;
pFile - > pBuffer - > AssumeMemory ( pUncompressBuffer , nUncompressedSize , nUncompressedSize ) ; // ?
}
else
{
// Put it directly into our target
pFile - > pBuffer - > Put ( ( unsigned char * ) pFile - > pCompressedBuffer - > Base ( ) , pFile - > nCompressedSize ) ;
}
// end decompression
pFile - > nSize = pFile - > pBuffer - > TellMaxPut ( ) ;
pFile - > pBuffer - > SeekGet ( CUtlBuffer : : SEEK_HEAD , 0 ) ;
pFile - > pBuffer - > SeekPut ( CUtlBuffer : : SEEK_HEAD , pFile - > nSize ) ;
unsigned int srcBytes = pFile - > nCompressedSize ;
unsigned int destBytes = pFile - > nSize ;
SaveMsg ( " SIM: SaveDir: (%s) Uncompressed %d bytes to %d bytes. \n " , GetString ( pFile - > name ) , srcBytes , destBytes ) ;
}
//----------------------------------------------------------------------------
// Access the save files
//----------------------------------------------------------------------------
int CSaveRestoreFileSystem : : GetFileIndex ( const char * filename )
{
CUtlSymbol id = AddString ( Q_UnqualifiedFileName ( filename ) ) ;
return GetDirectory ( ) . Find ( id ) ;
}
FileHandle_t CSaveRestoreFileSystem : : GetFileHandle ( const char * filename )
{
intp idx = GetFileIndex ( filename ) ;
if ( idx = = INVALID_INDEX )
{
idx = 0 ;
}
return ( FileHandle_t ) idx ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether the named memory block exists
//-----------------------------------------------------------------------------
bool CSaveRestoreFileSystem : : FileExists ( const char * pFileName , const char * pPathID )
{
return ( GetFileHandle ( pFileName ) ! = NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Validates a file handle
//-----------------------------------------------------------------------------
bool CSaveRestoreFileSystem : : HandleIsValid ( FileHandle_t hFile )
{
return hFile & & GetDirectory ( ) . IsValidIndex ( ( uintp ) hFile ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Renames a block of memory
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : RenameFile ( char const * pOldPath , char const * pNewPath , const char * pathID )
{
intp idx = GetFileIndex ( pOldPath ) ;
if ( idx ! = INVALID_INDEX )
{
CUtlSymbol newID = AddString ( Q_UnqualifiedFileName ( pNewPath ) ) ;
GetFile ( idx ) . name = newID ;
GetDirectory ( ) . Reinsert ( newID , idx ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Removes a memory block from CSaveDirectory and frees it.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : RemoveFile ( char const * pRelativePath , const char * pathID )
{
int idx = GetFileIndex ( pRelativePath ) ;
if ( idx ! = INVALID_INDEX )
{
delete GetFile ( idx ) . pBuffer ;
delete GetFile ( idx ) . pCompressedBuffer ;
GetDirectory ( ) . RemoveAt ( idx ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Access an existing memory block if it exists, else allocate one.
//-----------------------------------------------------------------------------
FileHandle_t CSaveRestoreFileSystem : : Open ( const char * pFullName , const char * pOptions , const char * pathID )
{
SaveFile_t * pFile = NULL ;
CUtlSymbol id = AddString ( Q_UnqualifiedFileName ( pFullName ) ) ;
intp idx = GetDirectory ( ) . Find ( id ) ;
if ( idx = = INVALID_INDEX )
{
// Don't create a read-only file
if ( Q_stricmp ( pOptions , " rb " ) )
{
// Create new file
SaveFile_t newFile ;
newFile . name = id ;
newFile . pBuffer = new CUtlBuffer ( ) ;
newFile . pCompressedBuffer = new CUtlBuffer ( ) ;
idx = GetDirectory ( ) . Insert ( id , newFile ) ;
}
else
{
return ( void * ) 0 ;
}
}
pFile = & GetFile ( idx ) ;
if ( ! Q_stricmp ( pOptions , " rb " ) )
{
Uncompress ( pFile ) ;
pFile - > eType = READ_ONLY ;
}
else if ( ! Q_stricmp ( pOptions , " wb " ) )
{
pFile - > pBuffer - > Clear ( ) ;
pFile - > eType = WRITE_ONLY ;
}
else if ( ! Q_stricmp ( pOptions , " a " ) )
{
Uncompress ( pFile ) ;
pFile - > eType = WRITE_ONLY ;
}
else if ( ! Q_stricmp ( pOptions , " ab+ " ) )
{
Uncompress ( pFile ) ;
pFile - > eType = WRITE_ONLY ;
pFile - > pBuffer - > SeekPut ( CUtlBuffer : : SEEK_TAIL , 0 ) ;
}
else
{
Assert ( 0 ) ;
Warning ( " CSaveRestoreFileSystem: Attempted to open %s with unsupported option %s \n " , pFullName , pOptions ) ;
return ( void * ) 0 ;
}
return ( void * ) idx ;
}
//-----------------------------------------------------------------------------
// Purpose: No need to close files in memory. Could perform post processing here.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : Close ( FileHandle_t hFile )
{
// Compress the file
if ( HandleIsValid ( hFile ) )
{
SaveFile_t & file = GetFile ( hFile ) ;
// Files opened for read don't need to be recompressed
if ( file . eType = = READ_ONLY )
{
SaveMsg ( " SIM: Closed file: %s \n " , GetString ( file . name ) ) ;
file . pBuffer - > Purge ( ) ;
file . nSize = 0 ;
}
else
{
Compress ( & file ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Reads data from memory.
//-----------------------------------------------------------------------------
int CSaveRestoreFileSystem : : Read ( void * pOutput , int size , FileHandle_t hFile )
{
int readSize = 0 ;
if ( HandleIsValid ( hFile ) )
{
SaveFile_t & file = GetFile ( hFile ) ;
if ( file . eType = = READ_ONLY )
{
readSize = file . pBuffer - > GetUpTo ( pOutput , size ) ;
}
else
{
Warning ( " Read: Attempted to read from a write-only file " ) ;
readSize = 0 ;
Assert ( 0 ) ;
}
}
return readSize ;
}
//-----------------------------------------------------------------------------
// Purpose: Writes data to memory.
//-----------------------------------------------------------------------------
int CSaveRestoreFileSystem : : Write ( void const * pInput , int size , FileHandle_t hFile )
{
int writeSize = 0 ;
if ( HandleIsValid ( hFile ) )
{
SaveFile_t & file = GetFile ( hFile ) ;
if ( file . eType = = WRITE_ONLY )
{
file . pBuffer - > Put ( pInput , size ) ;
file . nSize = file . pBuffer - > TellMaxPut ( ) ;
writeSize = size ;
}
else
{
Warning ( " Write: Attempted to write to a read-only file " ) ;
writeSize = 0 ;
Assert ( 0 ) ;
}
}
return writeSize ;
}
//-----------------------------------------------------------------------------
// Purpose: Seek in memory. Seeks the UtlBuffer put or get pos depending
// on whether the file was opened for read or write.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : Seek ( FileHandle_t hFile , int pos , FileSystemSeek_t method )
{
if ( HandleIsValid ( hFile ) )
{
SaveFile_t & file = GetFile ( hFile ) ;
if ( file . eType = = READ_ONLY )
{
file . pBuffer - > SeekGet ( ( CUtlBuffer : : SeekType_t ) method , pos ) ;
}
else if ( file . eType = = WRITE_ONLY )
{
file . pBuffer - > SeekPut ( ( CUtlBuffer : : SeekType_t ) method , pos ) ;
}
else
{
Assert ( 0 ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Return position in memory. Returns UtlBuffer put or get pos depending
// on whether the file was opened for read or write.
//-----------------------------------------------------------------------------
unsigned int CSaveRestoreFileSystem : : Tell ( FileHandle_t hFile )
{
unsigned int pos = 0 ;
if ( HandleIsValid ( hFile ) )
{
SaveFile_t & file = GetFile ( hFile ) ;
if ( file . eType = = READ_ONLY )
{
pos = file . pBuffer - > TellGet ( ) ;
}
else if ( file . eType = = WRITE_ONLY )
{
pos = file . pBuffer - > TellPut ( ) ;
}
else
{
Assert ( 0 ) ;
}
}
return pos ;
}
//-----------------------------------------------------------------------------
// Purpose: Return uncompressed memory size
//-----------------------------------------------------------------------------
unsigned int CSaveRestoreFileSystem : : Size ( FileHandle_t hFile )
{
if ( HandleIsValid ( hFile ) )
{
return GetFile ( hFile ) . nSize ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Return uncompressed size of data in memory
//-----------------------------------------------------------------------------
unsigned int CSaveRestoreFileSystem : : Size ( const char * pFileName , const char * pPathID )
{
return Size ( GetFileHandle ( pFileName ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Return compressed size of data in memory
//-----------------------------------------------------------------------------
unsigned int CSaveRestoreFileSystem : : CompressedSize ( const char * pFileName )
{
FileHandle_t hFile = GetFileHandle ( pFileName ) ;
if ( hFile )
{
return GetFile ( hFile ) . nCompressedSize ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Writes data to memory. Function is NOT async.
//-----------------------------------------------------------------------------
FSAsyncStatus_t CSaveRestoreFileSystem : : AsyncWrite ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , bool bAppend , FSAsyncControl_t * pControl )
{
FSAsyncStatus_t retval = FSASYNC_ERR_FAILURE ;
FileHandle_t hFile = Open ( pFileName , " wb " ) ;
if ( hFile )
{
SaveFile_t & file = GetFile ( ( uintp ) hFile ) ;
if ( file . eType = = WRITE_ONLY )
{
file . pBuffer - > Put ( pSrc , nSrcBytes ) ;
file . nSize = file . pBuffer - > TellMaxPut ( ) ;
Compress ( & file ) ;
retval = FSASYNC_OK ;
}
else
{
Warning ( " AsyncWrite: Attempted to write to a read-only file " ) ;
Assert ( 0 ) ;
}
}
if ( bFreeMemory )
free ( const_cast < void * > ( pSrc ) ) ;
return retval ;
}
//-----------------------------------------------------------------------------
// Purpose: Appends data to a memory block. Function is NOT async.
//-----------------------------------------------------------------------------
FSAsyncStatus_t CSaveRestoreFileSystem : : AsyncAppend ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , FSAsyncControl_t * pControl )
{
FSAsyncStatus_t retval = FSASYNC_ERR_FAILURE ;
FileHandle_t hFile = Open ( pFileName , " a " ) ;
if ( hFile )
{
SaveFile_t & file = GetFile ( hFile ) ;
if ( file . eType = = WRITE_ONLY )
{
file . pBuffer - > Put ( pSrc , nSrcBytes ) ;
file . nSize = file . pBuffer - > TellMaxPut ( ) ;
Compress ( & file ) ;
retval = FSASYNC_OK ;
}
else
{
Warning ( " AsyncAppend: Attempted to write to a read-only file " ) ;
Assert ( 0 ) ;
}
}
if ( bFreeMemory )
free ( const_cast < void * > ( pSrc ) ) ;
return retval ;
}
//-----------------------------------------------------------------------------
// Purpose: Appends a memory block to another memory block. Function is NOT async.
//-----------------------------------------------------------------------------
FSAsyncStatus_t CSaveRestoreFileSystem : : AsyncAppendFile ( const char * pDestFileName , const char * pSrcFileName , FSAsyncControl_t * pControl )
{
FileHandle_t hFile = Open ( pSrcFileName , " rb " ) ;
if ( hFile )
{
SaveFile_t & file = GetFile ( hFile ) ;
return AsyncAppend ( pDestFileName , file . pBuffer - > Base ( ) , file . nSize , false ) ;
}
return FSASYNC_ERR_FILEOPEN ;
}
//-----------------------------------------------------------------------------
// Purpose: All operations in memory are synchronous
//-----------------------------------------------------------------------------
FSAsyncStatus_t CSaveRestoreFileSystem : : AsyncFinish ( FSAsyncControl_t hControl , bool wait )
{
// do nothing
return FSASYNC_OK ;
}
void CSaveRestoreFileSystem : : AsyncRelease ( FSAsyncControl_t hControl )
{
// do nothing
}
void CSaveRestoreFileSystem : : AsyncFinishAllWrites ( void )
{
// Do nothing
}
//-----------------------------------------------------------------------------
// Purpose: Package up all intermediate files to a save game as per usual, but keep them in memory instead of commiting them to disk
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : DirectorCopyToMemory ( const char * pPath , const char * pDestFileName )
{
// Write the save file
FileHandle_t hSaveFile = Open ( pDestFileName , " ab+ " , pPath ) ;
if ( ! hSaveFile )
return ;
SaveFile_t & saveFile = GetFile ( hSaveFile ) ;
// At this point, we're going to be sneaky and spoof the uncompressed buffer back into the compressed one
// We need to do this because the file goes out to disk as a mixture of an uncompressed header and tags, and compressed
// intermediate files, so this emulates that in memory
saveFile . pCompressedBuffer - > Purge ( ) ;
saveFile . nCompressedSize = 0 ;
saveFile . pCompressedBuffer - > Put ( saveFile . pBuffer - > Base ( ) , saveFile . nSize ) ;
unsigned int nNumFilesPacked = 0 ;
for ( int i = GetDirectory ( ) . FirstInorder ( ) ; GetDirectory ( ) . IsValidIndex ( i ) ; i = GetDirectory ( ) . NextInorder ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
const char * pName = GetString ( file . name ) ;
char szFileName [ MAX_PATH ] ;
if ( Q_stristr ( pName , " .hl " ) )
{
int fileSize = CompressedSize ( pName ) ;
if ( fileSize )
{
Assert ( Q_strlen ( pName ) < = MAX_PATH ) ;
memset ( szFileName , 0 , sizeof ( szFileName ) ) ;
Q_strncpy ( szFileName , pName , sizeof ( szFileName ) ) ;
saveFile . pCompressedBuffer - > Put ( szFileName , sizeof ( szFileName ) ) ;
saveFile . pCompressedBuffer - > Put ( & fileSize , sizeof ( fileSize ) ) ;
saveFile . pCompressedBuffer - > Put ( file . pCompressedBuffer - > Base ( ) , file . nCompressedSize ) ;
SaveMsg ( " SIM: Packed: %s [Size: %.02f KB] \n " , GetString ( file . name ) , ( float ) file . nCompressedSize / 1024.0f ) ;
nNumFilesPacked + + ;
}
}
}
// Set the final, complete size of the file
saveFile . nCompressedSize = saveFile . pCompressedBuffer - > TellMaxPut ( ) ;
SaveMsg ( " SIM: (%s) Total Files Packed: %d [Size: %.02f KB] \n " , GetString ( saveFile . name ) , nNumFilesPacked , ( float ) saveFile . nCompressedSize / 1024.0f ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Copies the compressed contents of the CSaveDirectory into the save file on disk.
// Note: This expects standard saverestore behavior, and does NOT
// currently use pPath to filter the filename search.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : DirectoryCopy ( const char * pPath , const char * pDestFileName , bool bIsXSave )
{
if ( ! Q_stristr ( pPath , " *.hl? " ) )
{
// Function depends on known behavior
Assert ( 0 ) ;
return ;
}
// If we don't have a valid storage device, save this to memory instead
if ( saverestore - > StorageDeviceValid ( ) = = false )
{
DirectorCopyToMemory ( pPath , pDestFileName ) ;
return ;
}
// Write the save file
FileHandle_t hSaveFile = Open ( pDestFileName , " rb " , pPath ) ;
if ( ! hSaveFile )
return ;
SaveFile_t & saveFile = GetFile ( hSaveFile ) ;
// Find out how large this is going to be
unsigned int nWriteSize = saveFile . nSize ;
for ( int i = GetDirectory ( ) . FirstInorder ( ) ; GetDirectory ( ) . IsValidIndex ( i ) ; i = GetDirectory ( ) . NextInorder ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
const char * pName = GetString ( file . name ) ;
if ( Q_stristr ( pName , " .hl " ) )
{
// Account for the lump header size
nWriteSize + = MAX_PATH + sizeof ( int ) + file . nCompressedSize ;
}
}
// Fail to write
# if defined( _X360 )
if ( nWriteSize > XBX_SAVEGAME_BYTES )
{
// FIXME: This error is now lost in the ether!
return ;
}
# endif
g_pFileSystem - > AsyncWriteFile ( pDestFileName , saveFile . pBuffer , saveFile . nSize , true , false ) ;
// AsyncWriteFile() takes control of the utlbuffer, so don't let RemoveFile() delete it.
saveFile . pBuffer = NULL ;
RemoveFile ( pDestFileName ) ;
// write the list of files to the save file
for ( int i = GetDirectory ( ) . FirstInorder ( ) ; GetDirectory ( ) . IsValidIndex ( i ) ; i = GetDirectory ( ) . NextInorder ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
const char * pName = GetString ( file . name ) ;
if ( Q_stristr ( pName , " .hl " ) )
{
int fileSize = CompressedSize ( pName ) ;
if ( fileSize )
{
Assert ( Q_strlen ( pName ) < = MAX_PATH ) ;
g_pFileSystem - > AsyncAppend ( pDestFileName , memcpy ( new char [ MAX_PATH ] , pName , MAX_PATH ) , MAX_PATH , true ) ;
g_pFileSystem - > AsyncAppend ( pDestFileName , new int ( fileSize ) , sizeof ( int ) , true ) ;
// This behaves like AsyncAppendFile (due to 5th param) but gets src file from memory instead of off disk.
g_pFileSystem - > AsyncWriteFile ( pDestFileName , file . pCompressedBuffer , file . nCompressedSize , false , true ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Extracts all the files contained within pFile.
// Does not Uncompress the extracted data.
//-----------------------------------------------------------------------------
bool CSaveRestoreFileSystem : : DirectoryExtract ( FileHandle_t pFile , int fileCount , bool bIsXSave )
{
int fileSize ;
FileHandle_t pCopy ;
char fileName [ MAX_PATH ] ;
// Read compressed files from disk into the virtual directory
for ( int i = 0 ; i < fileCount ; i + + )
{
Read ( fileName , MAX_PATH , pFile ) ;
Read ( & fileSize , sizeof ( int ) , pFile ) ;
if ( ! fileSize )
return false ;
pCopy = Open ( fileName , " wb " ) ;
if ( ! pCopy )
return false ;
SaveFile_t & destFile = GetFile ( pCopy ) ;
destFile . pCompressedBuffer - > EnsureCapacity ( fileSize ) ;
// Must read in the correct amount of data
if ( Read ( destFile . pCompressedBuffer - > PeekPut ( ) , fileSize , pFile ) ! = fileSize )
return false ;
destFile . pCompressedBuffer - > SeekPut ( CUtlBuffer : : SEEK_HEAD , fileSize ) ;
destFile . nCompressedSize = fileSize ;
SaveMsg ( " SIM: Extracted: %s [Size: %d KB] \n " , GetString ( destFile . name ) , destFile . nCompressedSize / 1024 ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFilename -
//-----------------------------------------------------------------------------
bool CSaveRestoreFileSystem : : LoadFileFromDisk ( const char * pFilename )
{
// Open a new file for writing in memory
FileHandle_t hMemoryFile = Open ( pFilename , " wb " ) ;
if ( ! hMemoryFile )
return false ;
// Open the file off the disk
FileHandle_t hDiskFile = g_pFileSystem - > OpenEx ( pFilename , " rb " , ( IsX360 ( ) ) ? FSOPEN_NEVERINPACK : 0 ) ;
if ( ! hDiskFile )
return false ;
// Read it in off of the disk to memory
SaveFile_t & memoryFile = GetFile ( hMemoryFile ) ;
if ( g_pFileSystem - > ReadToBuffer ( hDiskFile , * memoryFile . pCompressedBuffer ) = = false )
return false ;
// Hold the compressed size
memoryFile . nCompressedSize = memoryFile . pCompressedBuffer - > TellMaxPut ( ) ;
// Close the disk file
g_pFileSystem - > Close ( hDiskFile ) ;
SaveMsg ( " SIM: Loaded %s into memory \n " , pFilename ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the number of save files in the specified filter
// Note: This expects standard saverestore behavior, and does NOT
// currently use pPath to filter file search.
//-----------------------------------------------------------------------------
int CSaveRestoreFileSystem : : DirectoryCount ( const char * pPath )
{
if ( ! Q_stristr ( pPath , " *.hl? " ) )
{
// Function depends on known behavior
Assert ( 0 ) ;
return 0 ;
}
int count = 0 ;
for ( int i = GetDirectory ( ) . FirstInorder ( ) ; GetDirectory ( ) . IsValidIndex ( i ) ; i = GetDirectory ( ) . NextInorder ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
if ( Q_stristr ( GetString ( file . name ) , " .hl " ) )
{
+ + count ;
}
}
return count ;
}
//-----------------------------------------------------------------------------
// Purpose: Clears the save directory of temporary save files (*.hl)
// Note: This expects standard saverestore behavior, and does NOT
// currently use pPath to filter file search.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : DirectoryClear ( const char * pPath , bool bIsXSave )
{
if ( ! Q_stristr ( pPath , " *.hl? " ) )
{
// Function depends on known behavior
Assert ( 0 ) ;
return ;
}
int i = GetDirectory ( ) . FirstInorder ( ) ;
while ( GetDirectory ( ) . IsValidIndex ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
i = GetDirectory ( ) . NextInorder ( i ) ;
if ( Q_stristr ( GetString ( file . name ) , " .hl " ) )
{
SaveMsg ( " SIM: Cleared: %s \n " , GetString ( file . name ) ) ;
// Delete the temporary save file
RemoveFile ( GetString ( file . name ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Transfer all save files from memory to disk.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : WriteSaveDirectoryToDisk ( void )
{
char szPath [ MAX_PATH ] ;
int i = GetDirectory ( ) . FirstInorder ( ) ;
while ( GetDirectory ( ) . IsValidIndex ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
i = GetDirectory ( ) . NextInorder ( i ) ;
const char * pName = GetString ( file . name ) ;
if ( Q_stristr ( pName , " .hl " ) )
{
// Write the temporary save file to disk
Open ( pName , " rb " ) ;
Q_snprintf ( szPath , sizeof ( szPath ) , " %s%s " , saverestore - > GetSaveDir ( ) , pName ) ;
g_pFileSystem - > WriteFile ( szPath , " GAME " , * file . pBuffer ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Transfer all save files from disk to memory.
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : LoadSaveDirectoryFromDisk ( const char * pPath )
{
char const * findfn ;
char szPath [ MAX_PATH ] ;
findfn = Sys_FindFirstEx ( pPath , " DEFAULT_WRITE_PATH " , NULL , 0 ) ;
while ( findfn ! = NULL )
{
Q_snprintf ( szPath , sizeof ( szPath ) , " %s%s " , saverestore - > GetSaveDir ( ) , findfn ) ;
// Add the temporary save file
FileHandle_t hFile = Open ( findfn , " wb " ) ;
if ( hFile )
{
SaveFile_t & file = GetFile ( hFile ) ;
g_pFileSystem - > ReadFile ( szPath , " GAME " , * file . pBuffer ) ;
file . nSize = file . pBuffer - > TellMaxPut ( ) ;
Close ( hFile ) ;
}
// Any more save files
findfn = Sys_FindNext ( NULL , 0 ) ;
}
Sys_FindClose ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : AuditFiles ( void )
{
unsigned int nTotalFiles = 0 ;
unsigned int nTotalCompressed = 0 ;
unsigned int nTotalUncompressed = 0 ;
int i = GetDirectory ( ) . FirstInorder ( ) ;
while ( GetDirectory ( ) . IsValidIndex ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
i = GetDirectory ( ) . NextInorder ( i ) ;
nTotalFiles + + ;
nTotalCompressed + = file . nCompressedSize ;
nTotalUncompressed + = file . nSize ;
Msg ( " SIM: File: %s [c: %.02f KB / u: %.02f KB] \n " , GetString ( file . name ) , ( float ) file . nCompressedSize / 1024.0f , ( float ) file . nSize / 1024.0f ) ;
}
Msg ( " SIM: ------------------------------------------------------------ " ) ;
Msg ( " SIM: Total files: %d [c: %.02f KB / c: %.02f KB] : Total Size: %.02f KB \n " , nTotalFiles , ( float ) nTotalCompressed / 1024.0f , ( float ) nTotalUncompressed / 1024.0f , ( float ) ( nTotalCompressed + nTotalUncompressed ) / 1024.0f ) ;
}
CON_COMMAND ( audit_save_in_memory , " Audit the memory usage and files in the save-to-memory system " )
{
if ( ! IsX360 ( ) )
return ;
g_pSaveRestoreFileSystem - > AuditFiles ( ) ;
}
CON_COMMAND ( dump_x360_saves , " Dump X360 save games to disk " )
{
if ( ! IsX360 ( ) )
{
Warning ( " dump_x360 only available on X360 platform! \n " ) ;
return ;
}
if ( XBX_GetStorageDeviceId ( ) = = XBX_INVALID_STORAGE_ID | | XBX_GetStorageDeviceId ( ) = = XBX_STORAGE_DECLINED )
{
Warning ( " No storage device attached! \n " ) ;
return ;
}
char szInName [ MAX_PATH ] ; // Read path from the container
char szOutName [ MAX_PATH ] ; // Output path to the disk
char szFileNameBase [ MAX_PATH ] ; // Name of the file minus directories or extensions
FileFindHandle_t findHandle ;
char szSearchPath [ MAX_PATH ] ;
Q_snprintf ( szSearchPath , sizeof ( szSearchPath ) , " %s: \\ *.* " , GetCurrentMod ( ) ) ;
const char * pFileName = g_pFileSystem - > FindFirst ( szSearchPath , & findHandle ) ;
while ( pFileName )
{
// Create the proper read path
Q_snprintf ( szInName , sizeof ( szInName ) , " %s: \\ %s " , GetCurrentMod ( ) , pFileName ) ;
// Read the file and blat it out
CUtlBuffer buf ( 0 , 0 , 0 ) ;
if ( g_pFileSystem - > ReadFile ( szInName , NULL , buf ) )
{
// Strip us down to just our filename
Q_FileBase ( pFileName , szFileNameBase , sizeof ( szFileNameBase ) ) ;
Q_snprintf ( szOutName , sizeof ( szOutName ) , " save/%s.sav " , szFileNameBase ) ;
g_pFileSystem - > WriteFile ( szOutName , NULL , buf ) ;
Msg ( " Copied file: %s to %s \n " , szInName , szOutName ) ;
}
// Clean up
buf . Clear ( ) ;
// Any more save files
pFileName = g_pFileSystem - > FindNext ( findHandle ) ;
}
g_pFileSystem - > FindClose ( findHandle ) ;
}
CON_COMMAND ( dump_x360_cfg , " Dump X360 config files to disk " )
{
if ( ! IsX360 ( ) )
{
Warning ( " dump_x360 only available on X360 platform! \n " ) ;
return ;
}
if ( XBX_GetStorageDeviceId ( ) = = XBX_INVALID_STORAGE_ID | | XBX_GetStorageDeviceId ( ) = = XBX_STORAGE_DECLINED )
{
Warning ( " No storage device attached! \n " ) ;
return ;
}
char szInName [ MAX_PATH ] ; // Read path from the container
char szOutName [ MAX_PATH ] ; // Output path to the disk
char szFileNameBase [ MAX_PATH ] ; // Name of the file minus directories or extensions
FileFindHandle_t findHandle ;
char szSearchPath [ MAX_PATH ] ;
Q_snprintf ( szSearchPath , sizeof ( szSearchPath ) , " cfg: \\ *.* " ) ;
const char * pFileName = g_pFileSystem - > FindFirst ( szSearchPath , & findHandle ) ;
while ( pFileName )
{
// Create the proper read path
Q_snprintf ( szInName , sizeof ( szInName ) , " cfg: \\ %s " , pFileName ) ;
// Read the file and blat it out
CUtlBuffer buf ( 0 , 0 , 0 ) ;
if ( g_pFileSystem - > ReadFile ( szInName , NULL , buf ) )
{
// Strip us down to just our filename
Q_FileBase ( pFileName , szFileNameBase , sizeof ( szFileNameBase ) ) ;
Q_snprintf ( szOutName , sizeof ( szOutName ) , " %s.cfg " , szFileNameBase ) ;
g_pFileSystem - > WriteFile ( szOutName , NULL , buf ) ;
Msg ( " Copied file: %s to %s \n " , szInName , szOutName ) ;
}
// Clean up
buf . Clear ( ) ;
// Any more save files
pFileName = g_pFileSystem - > FindNext ( findHandle ) ;
}
g_pFileSystem - > FindClose ( findHandle ) ;
}
# define FILECOPYBUFSIZE (1024 * 1024)
//-----------------------------------------------------------------------------
// Purpose: Copy one file to another file
//-----------------------------------------------------------------------------
static bool FileCopy ( FileHandle_t pOutput , FileHandle_t pInput , int fileSize )
{
// allocate a reasonably large file copy buffer, since otherwise write performance under steam suffers
char * buf = ( char * ) malloc ( FILECOPYBUFSIZE ) ;
int size ;
int readSize ;
bool success = true ;
while ( fileSize > 0 )
{
if ( fileSize > FILECOPYBUFSIZE )
size = FILECOPYBUFSIZE ;
else
size = fileSize ;
if ( ( readSize = g_pSaveRestoreFileSystem - > Read ( buf , size , pInput ) ) < size )
{
Warning ( " Unexpected end of file expanding save game \n " ) ;
fileSize = 0 ;
success = false ;
break ;
}
g_pSaveRestoreFileSystem - > Write ( buf , readSize , pOutput ) ;
fileSize - = size ;
}
free ( buf ) ;
return success ;
}
struct filelistelem_t
{
char szFileName [ MAX_PATH ] ;
} ;
//-----------------------------------------------------------------------------
// Purpose: Implementation to execute traditional save to disk behavior
//-----------------------------------------------------------------------------
class CSaveRestoreFileSystemPassthrough : public ISaveRestoreFileSystem
{
public :
CSaveRestoreFileSystemPassthrough ( ) : m_iContainerOpens ( 0 ) { }
bool FileExists ( const char * pFileName , const char * pPathID )
{
return g_pFileSystem - > FileExists ( pFileName , pPathID ) ;
}
void RemoveFile ( char const * pRelativePath , const char * pathID )
{
g_pFileSystem - > RemoveFile ( pRelativePath , pathID ) ;
}
void RenameFile ( char const * pOldPath , char const * pNewPath , const char * pathID )
{
g_pFileSystem - > RenameFile ( pOldPath , pNewPath , pathID ) ;
}
void AsyncFinishAllWrites ( void )
{
g_pFileSystem - > AsyncFinishAllWrites ( ) ;
}
FileHandle_t Open ( const char * pFullName , const char * pOptions , const char * pathID )
{
return g_pFileSystem - > OpenEx ( pFullName , pOptions , FSOPEN_NEVERINPACK , pathID ) ;
}
void Close ( FileHandle_t hSaveFile )
{
g_pFileSystem - > Close ( hSaveFile ) ;
}
int Read ( void * pOutput , int size , FileHandle_t hFile )
{
return g_pFileSystem - > Read ( pOutput , size , hFile ) ;
}
int Write ( void const * pInput , int size , FileHandle_t hFile )
{
return g_pFileSystem - > Write ( pInput , size , hFile ) ;
}
FSAsyncStatus_t AsyncWrite ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , bool bAppend , FSAsyncControl_t * pControl )
{
SaveMsg ( " AsyncWrite (%s/%d)... \n " , pFileName , nSrcBytes ) ;
return g_pFileSystem - > AsyncWrite ( pFileName , pSrc , nSrcBytes , bFreeMemory , bAppend , pControl ) ;
}
void Seek ( FileHandle_t hFile , int pos , FileSystemSeek_t method )
{
g_pFileSystem - > Seek ( hFile , pos , method ) ;
}
unsigned int Tell ( FileHandle_t hFile )
{
return g_pFileSystem - > Tell ( hFile ) ;
}
unsigned int Size ( FileHandle_t hFile )
{
return g_pFileSystem - > Size ( hFile ) ;
}
unsigned int Size ( const char * pFileName , const char * pPathID )
{
return g_pFileSystem - > Size ( pFileName , pPathID ) ;
}
FSAsyncStatus_t AsyncFinish ( FSAsyncControl_t hControl , bool wait )
{
return g_pFileSystem - > AsyncFinish ( hControl , wait ) ;
}
void AsyncRelease ( FSAsyncControl_t hControl )
{
g_pFileSystem - > AsyncRelease ( hControl ) ;
}
FSAsyncStatus_t AsyncAppend ( const char * pFileName , const void * pSrc , int nSrcBytes , bool bFreeMemory , FSAsyncControl_t * pControl )
{
return g_pFileSystem - > AsyncAppend ( pFileName , pSrc , nSrcBytes , bFreeMemory , pControl ) ;
}
FSAsyncStatus_t AsyncAppendFile ( const char * pDestFileName , const char * pSrcFileName , FSAsyncControl_t * pControl )
{
return g_pFileSystem - > AsyncAppendFile ( pDestFileName , pSrcFileName , pControl ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Copies the contents of the save directory into a single file
//-----------------------------------------------------------------------------
void DirectoryCopy ( const char * pPath , const char * pDestFileName , bool bIsXSave )
{
SaveMsg ( " DirectoryCopy.... \n " ) ;
CUtlVector < filelistelem_t > list ;
// force the writes to finish before trying to get the size/existence of a file
// @TODO: don't need this if retain sizes for files written earlier in process
SaveMsg ( " DirectoryCopy: AsyncFinishAllWrites \n " ) ;
g_pFileSystem - > AsyncFinishAllWrites ( ) ;
// build the directory list
char basefindfn [ MAX_PATH ] ;
const char * findfn = Sys_FindFirstEx ( pPath , " DEFAULT_WRITE_PATH " , basefindfn , sizeof ( basefindfn ) ) ;
while ( findfn )
{
int index = list . AddToTail ( ) ;
memset ( list [ index ] . szFileName , 0 , sizeof ( list [ index ] . szFileName ) ) ;
Q_strncpy ( list [ index ] . szFileName , findfn , sizeof ( list [ index ] . szFileName ) ) ;
findfn = Sys_FindNext ( basefindfn , sizeof ( basefindfn ) ) ;
}
Sys_FindClose ( ) ;
// write the list of files to the save file
char szName [ MAX_PATH ] ;
for ( int i = 0 ; i < list . Count ( ) ; i + + )
{
if ( ! bIsXSave )
{
Q_snprintf ( szName , sizeof ( szName ) , " %s%s " , saverestore - > GetSaveDir ( ) , list [ i ] . szFileName ) ;
}
else
{
Q_snprintf ( szName , sizeof ( szName ) , " %s: \\ %s " , GetCurrentMod ( ) , list [ i ] . szFileName ) ;
}
Q_FixSlashes ( szName ) ;
int fileSize = g_pFileSystem - > Size ( szName ) ;
if ( fileSize )
{
Assert ( sizeof ( list [ i ] . szFileName ) = = MAX_PATH ) ;
SaveMsg ( " DirectoryCopy: AsyncAppend %s, %s \n " , szName , pDestFileName ) ;
g_pFileSystem - > AsyncAppend ( pDestFileName , memcpy ( new char [ MAX_PATH ] , list [ i ] . szFileName , MAX_PATH ) , MAX_PATH , true ) ; // Filename can only be as long as a map name + extension
g_pFileSystem - > AsyncAppend ( pDestFileName , memcpy ( new char [ sizeof ( int ) ] , & fileSize , sizeof ( int ) ) , sizeof ( int ) , true ) ;
g_pFileSystem - > AsyncAppendFile ( pDestFileName , szName ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Extracts all the files contained within pFile
//-----------------------------------------------------------------------------
bool DirectoryExtract ( FileHandle_t pFile , int fileCount , bool bIsXSave )
{
int fileSize ;
FileHandle_t pCopy ;
char szName [ MAX_PATH ] , fileName [ MAX_PATH ] ;
bool success = true ;
for ( int i = 0 ; i < fileCount & & success ; i + + )
{
// Filename can only be as long as a map name + extension
if ( g_pSaveRestoreFileSystem - > Read ( fileName , MAX_PATH , pFile ) ! = MAX_PATH )
return false ;
if ( g_pSaveRestoreFileSystem - > Read ( & fileSize , sizeof ( int ) , pFile ) ! = sizeof ( int ) )
return false ;
if ( ! fileSize )
return false ;
if ( ! bIsXSave )
{
Q_snprintf ( szName , sizeof ( szName ) , " %s%s " , saverestore - > GetSaveDir ( ) , fileName ) ;
}
else
{
Q_snprintf ( szName , sizeof ( szName ) , " %s: \\ %s " , GetCurrentMod ( ) , fileName ) ;
}
Q_FixSlashes ( szName ) ;
pCopy = g_pSaveRestoreFileSystem - > Open ( szName , " wb " , " MOD " ) ;
if ( ! pCopy )
return false ;
success = FileCopy ( pCopy , pFile , fileSize ) ;
g_pSaveRestoreFileSystem - > Close ( pCopy ) ;
}
return success ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the number of files in the specified filter
//-----------------------------------------------------------------------------
int DirectoryCount ( const char * pPath )
{
int count = 0 ;
const char * findfn = Sys_FindFirstEx ( pPath , " DEFAULT_WRITE_PATH " , NULL , 0 ) ;
while ( findfn ! = NULL )
{
count + + ;
findfn = Sys_FindNext ( NULL , 0 ) ;
}
Sys_FindClose ( ) ;
return count ;
}
//-----------------------------------------------------------------------------
// Purpose: Clears the save directory of all temporary files (*.hl)
//-----------------------------------------------------------------------------
void DirectoryClear ( const char * pPath , bool bIsXSave )
{
char const * findfn ;
char szPath [ MAX_PATH ] ;
findfn = Sys_FindFirstEx ( pPath , " DEFAULT_WRITE_PATH " , NULL , 0 ) ;
while ( findfn ! = NULL )
{
if ( ! bIsXSave )
{
Q_snprintf ( szPath , sizeof ( szPath ) , " %s%s " , saverestore - > GetSaveDir ( ) , findfn ) ;
}
else
{
Q_snprintf ( szPath , sizeof ( szPath ) , " %s: \\ %s " , GetCurrentMod ( ) , findfn ) ;
}
// Delete the temporary save file
g_pFileSystem - > RemoveFile ( szPath , " MOD " ) ;
// Any more save files
findfn = Sys_FindNext ( NULL , 0 ) ;
}
Sys_FindClose ( ) ;
}
void AuditFiles ( void )
{
Msg ( " Not using save-in-memory path! \n " ) ;
}
bool LoadFileFromDisk ( const char * pFilename )
{
Msg ( " Not using save-in-memory path! \n " ) ;
return true ;
}
private :
int m_iContainerOpens ;
} ;
static CSaveRestoreFileSystem s_SaveRestoreFileSystem ;
static CSaveRestoreFileSystemPassthrough s_SaveRestoreFileSystemPassthrough ;
# ifdef _X360
ISaveRestoreFileSystem * g_pSaveRestoreFileSystem = & s_SaveRestoreFileSystem ;
# else
ISaveRestoreFileSystem * g_pSaveRestoreFileSystem = & s_SaveRestoreFileSystemPassthrough ;
# endif // _X360
//-----------------------------------------------------------------------------
// Purpose: Called when switching between saving in memory and saving to disk.
//-----------------------------------------------------------------------------
void SaveInMemoryCallback ( IConVar * pConVar , const char * pOldString , float flOldValue )
{
if ( ! IsX360 ( ) )
{
Warning ( " save_in_memory is compatible with only the Xbox 360! \n " ) ;
return ;
}
ConVarRef var ( pConVar ) ;
if ( var . GetFloat ( ) = = flOldValue )
return ;
// *.hl? files are transferred between disk and memory when this cvar changes
char szPath [ MAX_PATH ] ;
Q_snprintf ( szPath , sizeof ( szPath ) , " %s%s " , saverestore - > GetSaveDir ( ) , " *.hl? " ) ;
if ( var . GetBool ( ) )
{
g_pSaveRestoreFileSystem = & s_SaveRestoreFileSystem ;
// Clear memory and load
s_SaveRestoreFileSystem . DirectoryClear ( " *.hl? " , IsX360 ( ) ) ;
s_SaveRestoreFileSystem . LoadSaveDirectoryFromDisk ( szPath ) ;
// Clear disk
s_SaveRestoreFileSystemPassthrough . DirectoryClear ( szPath , IsX360 ( ) ) ;
}
else
{
g_pSaveRestoreFileSystem = & s_SaveRestoreFileSystemPassthrough ;
// Clear disk and write
s_SaveRestoreFileSystemPassthrough . DirectoryClear ( szPath , IsX360 ( ) ) ;
s_SaveRestoreFileSystem . WriteSaveDirectoryToDisk ( ) ;
// Clear memory
s_SaveRestoreFileSystem . DirectoryClear ( " *.hl? " , IsX360 ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Dump a list of the save directory contents (in memory) to the console
//-----------------------------------------------------------------------------
void CSaveRestoreFileSystem : : DumpSaveDirectory ( void )
{
unsigned int totalCompressedSize = 0 ;
unsigned int totalUncompressedSize = 0 ;
for ( int i = GetDirectory ( ) . FirstInorder ( ) ; GetDirectory ( ) . IsValidIndex ( i ) ; i = GetDirectory ( ) . NextInorder ( i ) )
{
SaveFile_t & file = GetFile ( i ) ;
Msg ( " File %d: %s Size:%d \n " , i , GetString ( file . name ) , file . nCompressedSize ) ;
totalUncompressedSize + = file . nSize ;
totalCompressedSize + = file . nCompressedSize ;
}
float percent = 0.f ;
if ( totalUncompressedSize )
percent = 100.f - ( totalCompressedSize / totalUncompressedSize * 100.f ) ;
Msg ( " Total Size: %.2f Mb (%d bytes) \n " , totalCompressedSize / ( 1024.f * 1024.f ) , totalCompressedSize ) ;
Msg ( " Compression: %.2f Mb to %.2f Mb (%.0f%%) \n " , totalUncompressedSize / ( 1024.f * 1024.f ) , totalCompressedSize / ( 1024.f * 1024.f ) , percent ) ;
}
CON_COMMAND ( dumpsavedir , " List the contents of the save directory in memory " )
{
s_SaveRestoreFileSystem . DumpSaveDirectory ( ) ;
}