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.

1478 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Filesystem abstraction for CSaveRestore - allows for storing temp save files
  4. // either in memory or on disk.
  5. //
  6. //===========================================================================//
  7. #ifdef _WIN32
  8. #include "winerror.h"
  9. #endif
  10. #include "filesystem_engine.h"
  11. #include "saverestore_filesystem.h"
  12. #include "host_saverestore.h"
  13. #include "host.h"
  14. #include "sys.h"
  15. #include "tier1/utlbuffer.h"
  16. #include "tier1/lzss.h"
  17. #include "tier1/convar.h"
  18. #include "ixboxsystem.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. extern ConVar save_spew;
  22. extern IXboxSystem *g_pXboxSystem;
  23. #define SaveMsg if ( !save_spew.GetBool() ) ; else Msg
  24. void SaveInMemoryCallback( IConVar *var, const char *pOldString, float flOldValue );
  25. #ifdef _X360
  26. ConVar save_in_memory( "save_in_memory", "1", 1, "Set to 1 to save to memory instead of disk (Xbox 360)", SaveInMemoryCallback );
  27. #else
  28. ConVar save_in_memory( "save_in_memory", "0", 0, "Set to 1 to save to memory instead of disk (Xbox 360)", SaveInMemoryCallback );
  29. #endif // _X360
  30. #define INVALID_INDEX (GetDirectory().InvalidIndex())
  31. enum { READ_ONLY, WRITE_ONLY };
  32. static float g_fPrevSaveInMemoryValue;
  33. static bool SaveFileLessFunc( const CUtlSymbol &lhs, const CUtlSymbol &rhs )
  34. {
  35. return lhs < rhs;
  36. }
  37. //----------------------------------------------------------------------------
  38. // Simulates the save directory in RAM.
  39. //----------------------------------------------------------------------------
  40. class CSaveDirectory
  41. {
  42. public:
  43. CSaveDirectory()
  44. {
  45. m_Files.SetLessFunc( SaveFileLessFunc );
  46. file_t dummy;
  47. dummy.name = m_SymbolTable.AddString( "dummy" );
  48. m_Files.Insert( dummy.name, dummy );
  49. }
  50. ~CSaveDirectory()
  51. {
  52. int i = m_Files.FirstInorder();
  53. while ( m_Files.IsValidIndex( i ) )
  54. {
  55. int idx = i;
  56. i = m_Files.NextInorder( i );
  57. delete m_Files[idx].pBuffer;
  58. delete m_Files[idx].pCompressedBuffer;
  59. m_Files.RemoveAt( idx );
  60. }
  61. }
  62. struct file_t
  63. {
  64. file_t()
  65. {
  66. pBuffer = NULL;
  67. pCompressedBuffer = NULL;
  68. nSize = 0;
  69. nCompressedSize = NULL;
  70. }
  71. int eType;
  72. CUtlSymbol name;
  73. unsigned int nSize;
  74. unsigned int nCompressedSize;
  75. CUtlBuffer *pBuffer;
  76. CUtlBuffer *pCompressedBuffer;
  77. };
  78. CUtlSymbolTable m_SymbolTable;
  79. CUtlMap<CUtlSymbol, file_t> m_Files;
  80. };
  81. typedef CSaveDirectory::file_t SaveFile_t;
  82. //----------------------------------------------------------------------------
  83. // CSaveRestoreFileSystem: Manipulates files in the CSaveDirectory
  84. //----------------------------------------------------------------------------
  85. class CSaveRestoreFileSystem : public ISaveRestoreFileSystem
  86. {
  87. public:
  88. CSaveRestoreFileSystem()
  89. {
  90. m_pSaveDirectory = new CSaveDirectory();
  91. m_iContainerOpens = 0;
  92. }
  93. ~CSaveRestoreFileSystem()
  94. {
  95. delete m_pSaveDirectory;
  96. }
  97. bool FileExists( const char *pFileName, const char *pPathID = NULL );
  98. void RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID = NULL );
  99. void RemoveFile( char const* pRelativePath, const char *pathID = NULL );
  100. FileHandle_t Open( const char *pFileName, const char *pOptions, const char *pathID = NULL );
  101. void Close( FileHandle_t );
  102. int Read( void *pOutput, int size, FileHandle_t file );
  103. int Write( void const* pInput, int size, FileHandle_t file );
  104. void Seek( FileHandle_t file, int pos, FileSystemSeek_t method );
  105. unsigned int Tell( FileHandle_t file );
  106. unsigned int Size( FileHandle_t file );
  107. unsigned int Size( const char *pFileName, const char *pPathID = NULL );
  108. void AsyncFinishAllWrites( void );
  109. void AsyncRelease( FSAsyncControl_t hControl );
  110. FSAsyncStatus_t AsyncWrite( const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl = NULL );
  111. FSAsyncStatus_t AsyncFinish( FSAsyncControl_t hControl, bool wait = false );
  112. FSAsyncStatus_t AsyncAppend( const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl = NULL );
  113. FSAsyncStatus_t AsyncAppendFile( const char *pDestFileName, const char *pSrcFileName, FSAsyncControl_t *pControl = NULL );
  114. void DirectoryCopy( const char *pPath, const char *pDestFileName, bool bIsXSave );
  115. void DirectorCopyToMemory( const char *pPath, const char *pDestFileName );
  116. bool DirectoryExtract( FileHandle_t pFile, int fileCount, bool bIsXSave );
  117. int DirectoryCount( const char *pPath );
  118. void DirectoryClear( const char *pPath, bool bIsXSave );
  119. void WriteSaveDirectoryToDisk( void );
  120. void LoadSaveDirectoryFromDisk( const char *pPath );
  121. void DumpSaveDirectory( void );
  122. void Compress( SaveFile_t *pFile );
  123. void Uncompress( SaveFile_t *pFile );
  124. void AuditFiles( void );
  125. bool LoadFileFromDisk( const char *pFilename );
  126. private:
  127. CSaveDirectory *m_pSaveDirectory;
  128. CUtlMap<CUtlSymbol, SaveFile_t> &GetDirectory( void ) { return m_pSaveDirectory->m_Files; }
  129. SaveFile_t &GetFile( const int idx ) { return m_pSaveDirectory->m_Files[idx]; }
  130. SaveFile_t &GetFile( const FileHandle_t hFile ) { return GetFile( (unsigned int)hFile ); }
  131. FileHandle_t GetFileHandle( const char *pFileName );
  132. int GetFileIndex( const char *pFileName );
  133. bool HandleIsValid( FileHandle_t hFile );
  134. unsigned int CompressedSize( const char *pFileName );
  135. CUtlSymbol AddString( const char *str )
  136. {
  137. char szString[ MAX_PATH ];
  138. Q_strncpy( szString, str, sizeof( szString ) );
  139. return m_pSaveDirectory->m_SymbolTable.AddString( Q_strlower( szString ) );
  140. }
  141. const char *GetString( CUtlSymbol &id ) { return m_pSaveDirectory->m_SymbolTable.String( id ); }
  142. int m_iContainerOpens;
  143. };
  144. //#define TEST_LZSS_WINDOW_SIZES
  145. //----------------------------------------------------------------------------
  146. // Compress the file data
  147. //----------------------------------------------------------------------------
  148. void CSaveRestoreFileSystem::Compress( SaveFile_t *pFile )
  149. {
  150. pFile->pCompressedBuffer->Purge();
  151. #ifdef TEST_LZSS_WINDOW_SIZES
  152. // Compress the data here
  153. CLZSS compressor_test;
  154. CLZSS newcompressor_test( 2048 );
  155. pFile->nCompressedSize = 0;
  156. float start = Plat_FloatTime();
  157. for(int i=0;i<10;i++)
  158. {
  159. uint32 sz;
  160. unsigned char *pCompressedBuffer = compressor_test.Compress(
  161. (unsigned char *) pFile->pBuffer->Base(), pFile->nSize, &sz );
  162. delete[] pCompressedBuffer;
  163. }
  164. Warning(" old compressor_test %f", Plat_FloatTime() - start );
  165. start = Plat_FloatTime();
  166. for(int i=0;i<10;i++)
  167. {
  168. uint32 sz;
  169. unsigned char *pCompressedBuffer = newcompressor_test.Compress(
  170. (unsigned char *) pFile->pBuffer->Base(), pFile->nSize, &sz );
  171. delete[] pCompressedBuffer;
  172. }
  173. Warning(" new compressor_test %f", Plat_FloatTime() - start );
  174. if ( 1)
  175. {
  176. uint32 sz;
  177. uint32 sz1;
  178. unsigned char *pNewCompressedBuffer = newcompressor_test.Compress(
  179. (unsigned char *) pFile->pBuffer->Base(), pFile->nSize, &sz );
  180. unsigned char *pOldCompressedBuffer = compressor_test.Compress(
  181. (unsigned char *) pFile->pBuffer->Base(), pFile->nSize, &sz1 );
  182. if ( ! pNewCompressedBuffer )
  183. Warning("new no comp");
  184. if ( ! pOldCompressedBuffer )
  185. Warning("old no comp");
  186. if ( pNewCompressedBuffer && pOldCompressedBuffer )
  187. {
  188. if ( sz != sz1 )
  189. Warning(" new size = %d old = %d", sz, sz1 );
  190. if ( memcmp( pNewCompressedBuffer, pOldCompressedBuffer, sz ) )
  191. Warning("data mismatch");
  192. }
  193. delete[] pOldCompressedBuffer;
  194. delete[] pNewCompressedBuffer;
  195. }
  196. #endif
  197. CLZSS compressor( 2048 );
  198. unsigned char *pCompressedBuffer = compressor.Compress( (unsigned char *) pFile->pBuffer->Base(), pFile->nSize, &pFile->nCompressedSize );
  199. if ( pCompressedBuffer == NULL )
  200. {
  201. // Just copy the buffer uncompressed
  202. pFile->pCompressedBuffer->Put( pFile->pBuffer->Base(), pFile->nSize );
  203. pFile->nCompressedSize = pFile->nSize;
  204. }
  205. else
  206. {
  207. // Take the compressed buffer as our own
  208. pFile->pCompressedBuffer->AssumeMemory( pCompressedBuffer, pFile->nCompressedSize, pFile->nCompressedSize ); // ?
  209. }
  210. // end compression
  211. pFile->pCompressedBuffer->SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  212. pFile->pCompressedBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, pFile->nCompressedSize );
  213. // Don't want the uncompressed memory hanging around
  214. pFile->pBuffer->Purge();
  215. unsigned int srcBytes = pFile->nSize;
  216. pFile->nSize = 0;
  217. unsigned int destBytes = pFile->nCompressedSize;
  218. float percent = 0.f;
  219. if ( srcBytes )
  220. percent = 100.0f * (1.0f - (float)destBytes/(float)srcBytes);
  221. SaveMsg( "SIM: SaveDir: (%s) Compressed %d bytes to %d bytes. (%.0f%%)\n", GetString( pFile->name ), srcBytes, destBytes, percent );
  222. }
  223. //----------------------------------------------------------------------------
  224. // Uncompress the file data
  225. //----------------------------------------------------------------------------
  226. void CSaveRestoreFileSystem::Uncompress( SaveFile_t *pFile )
  227. {
  228. pFile->pBuffer->Purge();
  229. // Uncompress the data here
  230. CLZSS compressor;
  231. unsigned int nUncompressedSize = compressor.GetActualSize( (unsigned char *) pFile->pCompressedBuffer->Base() );
  232. if ( nUncompressedSize != 0 )
  233. {
  234. unsigned char *pUncompressBuffer = (unsigned char *) malloc( nUncompressedSize );
  235. nUncompressedSize = compressor.Uncompress( (unsigned char *) pFile->pCompressedBuffer->Base(), pUncompressBuffer );
  236. pFile->pBuffer->AssumeMemory( pUncompressBuffer, nUncompressedSize, nUncompressedSize ); // ?
  237. }
  238. else
  239. {
  240. // Put it directly into our target
  241. pFile->pBuffer->Put( (unsigned char *) pFile->pCompressedBuffer->Base(), pFile->nCompressedSize );
  242. }
  243. // end decompression
  244. pFile->nSize = pFile->pBuffer->TellMaxPut();
  245. pFile->pBuffer->SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  246. pFile->pBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, pFile->nSize );
  247. unsigned int srcBytes = pFile->nCompressedSize;
  248. unsigned int destBytes = pFile->nSize;
  249. SaveMsg( "SIM: SaveDir: (%s) Uncompressed %d bytes to %d bytes.\n", GetString( pFile->name ), srcBytes, destBytes );
  250. }
  251. //----------------------------------------------------------------------------
  252. // Access the save files
  253. //----------------------------------------------------------------------------
  254. int CSaveRestoreFileSystem::GetFileIndex( const char *filename )
  255. {
  256. CUtlSymbol id = AddString( Q_UnqualifiedFileName( filename ) );
  257. return GetDirectory().Find( id );
  258. }
  259. FileHandle_t CSaveRestoreFileSystem::GetFileHandle( const char *filename )
  260. {
  261. int idx = GetFileIndex( filename );
  262. if ( idx == INVALID_INDEX )
  263. {
  264. idx = 0;
  265. }
  266. return (void*)idx;
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Purpose: Returns whether the named memory block exists
  270. //-----------------------------------------------------------------------------
  271. bool CSaveRestoreFileSystem::FileExists( const char *pFileName, const char *pPathID )
  272. {
  273. return ( GetFileHandle( pFileName ) != NULL );
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: Validates a file handle
  277. //-----------------------------------------------------------------------------
  278. bool CSaveRestoreFileSystem::HandleIsValid( FileHandle_t hFile )
  279. {
  280. return hFile && GetDirectory().IsValidIndex( (unsigned int)hFile );
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Renames a block of memory
  284. //-----------------------------------------------------------------------------
  285. void CSaveRestoreFileSystem::RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID )
  286. {
  287. int idx = GetFileIndex( pOldPath );
  288. if ( idx != INVALID_INDEX )
  289. {
  290. CUtlSymbol newID = AddString( Q_UnqualifiedFileName( pNewPath ) );
  291. GetFile( idx ).name = newID;
  292. GetDirectory().Reinsert( newID, idx );
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose: Removes a memory block from CSaveDirectory and frees it.
  297. //-----------------------------------------------------------------------------
  298. void CSaveRestoreFileSystem::RemoveFile( char const* pRelativePath, const char *pathID )
  299. {
  300. int idx = GetFileIndex( pRelativePath );
  301. if ( idx != INVALID_INDEX )
  302. {
  303. delete GetFile( idx ).pBuffer;
  304. delete GetFile( idx ).pCompressedBuffer;
  305. GetDirectory().RemoveAt( idx );
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: Access an existing memory block if it exists, else allocate one.
  310. //-----------------------------------------------------------------------------
  311. FileHandle_t CSaveRestoreFileSystem::Open( const char *pFullName, const char *pOptions, const char *pathID )
  312. {
  313. SaveFile_t *pFile = NULL;
  314. CUtlSymbol id = AddString( Q_UnqualifiedFileName( pFullName ) );
  315. int idx = GetDirectory().Find( id );
  316. if ( idx == INVALID_INDEX )
  317. {
  318. // Don't create a read-only file
  319. if ( Q_stricmp( pOptions, "rb" ) )
  320. {
  321. // Create new file
  322. SaveFile_t newFile;
  323. newFile.name = id;
  324. newFile.pBuffer = new CUtlBuffer();
  325. newFile.pCompressedBuffer = new CUtlBuffer();
  326. idx = GetDirectory().Insert( id, newFile );
  327. }
  328. else
  329. {
  330. return (void*)0;
  331. }
  332. }
  333. pFile = &GetFile( idx );
  334. if ( !Q_stricmp( pOptions, "rb" ) )
  335. {
  336. Uncompress( pFile );
  337. pFile->eType = READ_ONLY;
  338. }
  339. else if ( !Q_stricmp( pOptions, "wb" ) )
  340. {
  341. pFile->pBuffer->Clear();
  342. pFile->eType = WRITE_ONLY;
  343. }
  344. else if ( !Q_stricmp( pOptions, "a" ) )
  345. {
  346. Uncompress( pFile );
  347. pFile->eType = WRITE_ONLY;
  348. }
  349. else if ( !Q_stricmp( pOptions, "ab+" ) )
  350. {
  351. Uncompress( pFile );
  352. pFile->eType = WRITE_ONLY;
  353. pFile->pBuffer->SeekPut( CUtlBuffer::SEEK_TAIL, 0 );
  354. }
  355. else
  356. {
  357. Assert( 0 );
  358. Warning( "CSaveRestoreFileSystem: Attempted to open %s with unsupported option %s\n", pFullName, pOptions );
  359. return (void*)0;
  360. }
  361. return (void*)idx;
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: No need to close files in memory. Could perform post processing here.
  365. //-----------------------------------------------------------------------------
  366. void CSaveRestoreFileSystem::Close( FileHandle_t hFile )
  367. {
  368. // Compress the file
  369. if ( HandleIsValid( hFile ) )
  370. {
  371. SaveFile_t &file = GetFile( hFile );
  372. // Files opened for read don't need to be recompressed
  373. if ( file.eType == READ_ONLY )
  374. {
  375. SaveMsg("SIM: Closed file: %s\n", GetString( file.name ) );
  376. file.pBuffer->Purge();
  377. file.nSize = 0;
  378. }
  379. else
  380. {
  381. Compress( &file );
  382. }
  383. }
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: Reads data from memory.
  387. //-----------------------------------------------------------------------------
  388. int CSaveRestoreFileSystem::Read( void *pOutput, int size, FileHandle_t hFile )
  389. {
  390. int readSize = 0;
  391. if ( HandleIsValid( hFile ) )
  392. {
  393. SaveFile_t &file = GetFile( hFile );
  394. if( file.eType == READ_ONLY )
  395. {
  396. readSize = file.pBuffer->GetUpTo( pOutput, size );
  397. }
  398. else
  399. {
  400. Warning( "Read: Attempted to read from a write-only file" );
  401. readSize = 0;
  402. Assert( 0 );
  403. }
  404. }
  405. return readSize;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: Writes data to memory.
  409. //-----------------------------------------------------------------------------
  410. int CSaveRestoreFileSystem::Write( void const* pInput, int size, FileHandle_t hFile )
  411. {
  412. int writeSize = 0;
  413. if ( HandleIsValid( hFile ) )
  414. {
  415. SaveFile_t &file = GetFile( hFile );
  416. if( file.eType == WRITE_ONLY )
  417. {
  418. file.pBuffer->Put( pInput, size );
  419. file.nSize = file.pBuffer->TellMaxPut();
  420. writeSize = size;
  421. }
  422. else
  423. {
  424. Warning( "Write: Attempted to write to a read-only file" );
  425. writeSize = 0;
  426. Assert( 0 );
  427. }
  428. }
  429. return writeSize;
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Purpose: Seek in memory. Seeks the UtlBuffer put or get pos depending
  433. // on whether the file was opened for read or write.
  434. //-----------------------------------------------------------------------------
  435. void CSaveRestoreFileSystem::Seek( FileHandle_t hFile, int pos, FileSystemSeek_t method )
  436. {
  437. if ( HandleIsValid( hFile ) )
  438. {
  439. SaveFile_t &file = GetFile( hFile );
  440. if ( file.eType == READ_ONLY )
  441. {
  442. file.pBuffer->SeekGet( (CUtlBuffer::SeekType_t)method, pos );
  443. }
  444. else if ( file.eType == WRITE_ONLY )
  445. {
  446. file.pBuffer->SeekPut( (CUtlBuffer::SeekType_t)method, pos );
  447. }
  448. else
  449. {
  450. Assert( 0 );
  451. }
  452. }
  453. }
  454. //-----------------------------------------------------------------------------
  455. // Purpose: Return position in memory. Returns UtlBuffer put or get pos depending
  456. // on whether the file was opened for read or write.
  457. //-----------------------------------------------------------------------------
  458. unsigned int CSaveRestoreFileSystem::Tell( FileHandle_t hFile )
  459. {
  460. unsigned int pos = 0;
  461. if ( HandleIsValid( hFile ) )
  462. {
  463. SaveFile_t &file = GetFile( hFile );
  464. if ( file.eType == READ_ONLY )
  465. {
  466. pos = file.pBuffer->TellGet();
  467. }
  468. else if ( file.eType == WRITE_ONLY )
  469. {
  470. pos = file.pBuffer->TellPut();
  471. }
  472. else
  473. {
  474. Assert( 0 );
  475. }
  476. }
  477. return pos;
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose: Return uncompressed memory size
  481. //-----------------------------------------------------------------------------
  482. unsigned int CSaveRestoreFileSystem::Size( FileHandle_t hFile )
  483. {
  484. if ( HandleIsValid( hFile ) )
  485. {
  486. return GetFile( hFile ).nSize;
  487. }
  488. return 0;
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Return uncompressed size of data in memory
  492. //-----------------------------------------------------------------------------
  493. unsigned int CSaveRestoreFileSystem::Size( const char *pFileName, const char *pPathID )
  494. {
  495. return Size( GetFileHandle( pFileName ) );
  496. }
  497. //-----------------------------------------------------------------------------
  498. // Purpose: Return compressed size of data in memory
  499. //-----------------------------------------------------------------------------
  500. unsigned int CSaveRestoreFileSystem::CompressedSize( const char *pFileName )
  501. {
  502. FileHandle_t hFile = GetFileHandle( pFileName );
  503. if ( hFile )
  504. {
  505. return GetFile( hFile ).nCompressedSize;
  506. }
  507. return 0;
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Purpose: Writes data to memory. Function is NOT async.
  511. //-----------------------------------------------------------------------------
  512. FSAsyncStatus_t CSaveRestoreFileSystem::AsyncWrite( const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl )
  513. {
  514. FSAsyncStatus_t retval = FSASYNC_ERR_FAILURE;
  515. FileHandle_t hFile = Open( pFileName, "wb" );
  516. if ( hFile )
  517. {
  518. SaveFile_t &file = GetFile( (unsigned int)hFile );
  519. if( file.eType == WRITE_ONLY )
  520. {
  521. file.pBuffer->Put( pSrc, nSrcBytes );
  522. file.nSize = file.pBuffer->TellMaxPut();
  523. Compress( &file );
  524. retval = FSASYNC_OK;
  525. }
  526. else
  527. {
  528. Warning( "AsyncWrite: Attempted to write to a read-only file" );
  529. Assert( 0 );
  530. }
  531. }
  532. if ( bFreeMemory )
  533. free( const_cast< void * >( pSrc ) );
  534. return retval;
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Purpose: Appends data to a memory block. Function is NOT async.
  538. //-----------------------------------------------------------------------------
  539. FSAsyncStatus_t CSaveRestoreFileSystem::AsyncAppend(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl )
  540. {
  541. FSAsyncStatus_t retval = FSASYNC_ERR_FAILURE;
  542. FileHandle_t hFile = Open( pFileName, "a" );
  543. if ( hFile )
  544. {
  545. SaveFile_t &file = GetFile( hFile );
  546. if( file.eType == WRITE_ONLY )
  547. {
  548. file.pBuffer->Put( pSrc, nSrcBytes );
  549. file.nSize = file.pBuffer->TellMaxPut();
  550. Compress( &file );
  551. retval = FSASYNC_OK;
  552. }
  553. else
  554. {
  555. Warning( "AsyncAppend: Attempted to write to a read-only file" );
  556. Assert( 0 );
  557. }
  558. }
  559. if ( bFreeMemory )
  560. free( const_cast< void * >( pSrc ) );
  561. return retval;
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose: Appends a memory block to another memory block. Function is NOT async.
  565. //-----------------------------------------------------------------------------
  566. FSAsyncStatus_t CSaveRestoreFileSystem::AsyncAppendFile(const char *pDestFileName, const char *pSrcFileName, FSAsyncControl_t *pControl )
  567. {
  568. FileHandle_t hFile = Open( pSrcFileName, "rb" );
  569. if ( hFile )
  570. {
  571. SaveFile_t &file = GetFile( hFile );
  572. return AsyncAppend( pDestFileName, file.pBuffer->Base(), file.nSize, false );
  573. }
  574. return FSASYNC_ERR_FILEOPEN;
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose: All operations in memory are synchronous
  578. //-----------------------------------------------------------------------------
  579. FSAsyncStatus_t CSaveRestoreFileSystem::AsyncFinish( FSAsyncControl_t hControl, bool wait )
  580. {
  581. // do nothing
  582. return FSASYNC_OK;
  583. }
  584. void CSaveRestoreFileSystem::AsyncRelease( FSAsyncControl_t hControl )
  585. {
  586. // do nothing
  587. }
  588. void CSaveRestoreFileSystem::AsyncFinishAllWrites( void )
  589. {
  590. // Do nothing
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose: Package up all intermediate files to a save game as per usual, but keep them in memory instead of commiting them to disk
  594. //-----------------------------------------------------------------------------
  595. void CSaveRestoreFileSystem::DirectorCopyToMemory( const char *pPath, const char *pDestFileName )
  596. {
  597. // Write the save file
  598. FileHandle_t hSaveFile = Open( pDestFileName, "ab+", pPath );
  599. if ( !hSaveFile )
  600. return;
  601. SaveFile_t &saveFile = GetFile( hSaveFile );
  602. // At this point, we're going to be sneaky and spoof the uncompressed buffer back into the compressed one
  603. // We need to do this because the file goes out to disk as a mixture of an uncompressed header and tags, and compressed
  604. // intermediate files, so this emulates that in memory
  605. saveFile.pCompressedBuffer->Purge();
  606. saveFile.nCompressedSize = 0;
  607. saveFile.pCompressedBuffer->Put( saveFile.pBuffer->Base(), saveFile.nSize );
  608. unsigned int nNumFilesPacked = 0;
  609. for ( int i = GetDirectory().FirstInorder(); GetDirectory().IsValidIndex( i ); i = GetDirectory().NextInorder( i ) )
  610. {
  611. SaveFile_t &file = GetFile( i );
  612. const char *pName = GetString( file.name );
  613. char szFileName[MAX_PATH];
  614. if ( Q_stristr( pName, ".hl" ) )
  615. {
  616. int fileSize = CompressedSize( pName );
  617. if ( fileSize )
  618. {
  619. Assert( Q_strlen( pName ) <= MAX_PATH );
  620. memset( szFileName, 0, sizeof( szFileName ) );
  621. Q_strncpy( szFileName, pName, sizeof( szFileName ) );
  622. saveFile.pCompressedBuffer->Put( szFileName, sizeof( szFileName ) );
  623. saveFile.pCompressedBuffer->Put( &fileSize, sizeof(fileSize) );
  624. saveFile.pCompressedBuffer->Put( file.pCompressedBuffer->Base(), file.nCompressedSize );
  625. SaveMsg("SIM: Packed: %s [Size: %.02f KB]\n", GetString( file.name ), (float)file.nCompressedSize / 1024.0f );
  626. nNumFilesPacked++;
  627. }
  628. }
  629. }
  630. // Set the final, complete size of the file
  631. saveFile.nCompressedSize = saveFile.pCompressedBuffer->TellMaxPut();
  632. SaveMsg("SIM: (%s) Total Files Packed: %d [Size: %.02f KB]\n", GetString( saveFile.name ), nNumFilesPacked, (float) saveFile.nCompressedSize / 1024.0f );
  633. }
  634. //-----------------------------------------------------------------------------
  635. // Purpose: Copies the compressed contents of the CSaveDirectory into the save file on disk.
  636. // Note: This expects standard saverestore behavior, and does NOT
  637. // currently use pPath to filter the filename search.
  638. //-----------------------------------------------------------------------------
  639. void CSaveRestoreFileSystem::DirectoryCopy( const char *pPath, const char *pDestFileName, bool bIsXSave )
  640. {
  641. if ( !Q_stristr( pPath, "*.hl?" ) )
  642. {
  643. // Function depends on known behavior
  644. Assert( 0 );
  645. return;
  646. }
  647. // If we don't have a valid storage device, save this to memory instead
  648. if ( saverestore->StorageDeviceValid() == false )
  649. {
  650. DirectorCopyToMemory( pPath, pDestFileName );
  651. return;
  652. }
  653. // Write the save file
  654. FileHandle_t hSaveFile = Open( pDestFileName, "rb", pPath );
  655. if ( !hSaveFile )
  656. return;
  657. SaveFile_t &saveFile = GetFile( hSaveFile );
  658. // Find out how large this is going to be
  659. unsigned int nWriteSize = saveFile.nSize;
  660. for ( int i = GetDirectory().FirstInorder(); GetDirectory().IsValidIndex( i ); i = GetDirectory().NextInorder( i ) )
  661. {
  662. SaveFile_t &file = GetFile( i );
  663. const char *pName = GetString( file.name );
  664. if ( Q_stristr( pName, ".hl" ) )
  665. {
  666. // Account for the lump header size
  667. nWriteSize += MAX_PATH + sizeof(int) + file.nCompressedSize;
  668. }
  669. }
  670. // Fail to write
  671. #if defined( _X360 )
  672. if ( nWriteSize > XBX_SAVEGAME_BYTES )
  673. {
  674. // FIXME: This error is now lost in the ether!
  675. return;
  676. }
  677. #endif
  678. g_pFileSystem->AsyncWriteFile( pDestFileName, saveFile.pBuffer, saveFile.nSize, true, false );
  679. // AsyncWriteFile() takes control of the utlbuffer, so don't let RemoveFile() delete it.
  680. saveFile.pBuffer = NULL;
  681. RemoveFile( pDestFileName );
  682. // write the list of files to the save file
  683. for ( int i = GetDirectory().FirstInorder(); GetDirectory().IsValidIndex( i ); i = GetDirectory().NextInorder( i ) )
  684. {
  685. SaveFile_t &file = GetFile( i );
  686. const char *pName = GetString( file.name );
  687. if ( Q_stristr( pName, ".hl" ) )
  688. {
  689. int fileSize = CompressedSize( pName );
  690. if ( fileSize )
  691. {
  692. Assert( Q_strlen( pName ) <= MAX_PATH );
  693. g_pFileSystem->AsyncAppend( pDestFileName, memcpy( new char[MAX_PATH], pName, MAX_PATH), MAX_PATH, true );
  694. g_pFileSystem->AsyncAppend( pDestFileName, new int(fileSize), sizeof(int), true );
  695. // This behaves like AsyncAppendFile (due to 5th param) but gets src file from memory instead of off disk.
  696. g_pFileSystem->AsyncWriteFile( pDestFileName, file.pCompressedBuffer, file.nCompressedSize, false, true );
  697. }
  698. }
  699. }
  700. }
  701. //-----------------------------------------------------------------------------
  702. // Purpose: Extracts all the files contained within pFile.
  703. // Does not Uncompress the extracted data.
  704. //-----------------------------------------------------------------------------
  705. bool CSaveRestoreFileSystem::DirectoryExtract( FileHandle_t pFile, int fileCount, bool bIsXSave )
  706. {
  707. int fileSize;
  708. FileHandle_t pCopy;
  709. char fileName[ MAX_PATH ];
  710. // Read compressed files from disk into the virtual directory
  711. for ( int i = 0; i < fileCount; i++ )
  712. {
  713. Read( fileName, MAX_PATH, pFile );
  714. Read( &fileSize, sizeof(int), pFile );
  715. if ( !fileSize )
  716. return false;
  717. pCopy = Open( fileName, "wb" );
  718. if ( !pCopy )
  719. return false;
  720. SaveFile_t &destFile = GetFile( pCopy );
  721. destFile.pCompressedBuffer->EnsureCapacity( fileSize );
  722. // Must read in the correct amount of data
  723. if ( Read( destFile.pCompressedBuffer->PeekPut(), fileSize, pFile ) != fileSize )
  724. return false;
  725. destFile.pCompressedBuffer->SeekPut( CUtlBuffer::SEEK_HEAD, fileSize );
  726. destFile.nCompressedSize = fileSize;
  727. SaveMsg("SIM: Extracted: %s [Size: %d KB]\n", GetString( destFile.name ), destFile.nCompressedSize / 1024 );
  728. }
  729. return true;
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose:
  733. // Input : *pFilename -
  734. //-----------------------------------------------------------------------------
  735. bool CSaveRestoreFileSystem::LoadFileFromDisk( const char *pFilename )
  736. {
  737. // Open a new file for writing in memory
  738. FileHandle_t hMemoryFile = Open( pFilename, "wb" );
  739. if ( !hMemoryFile )
  740. return false;
  741. // Open the file off the disk
  742. FileHandle_t hDiskFile = g_pFileSystem->OpenEx( pFilename, "rb", ( IsX360() ) ? FSOPEN_NEVERINPACK : 0 );
  743. if ( !hDiskFile )
  744. return false;
  745. // Read it in off of the disk to memory
  746. SaveFile_t &memoryFile = GetFile( hMemoryFile );
  747. if ( g_pFileSystem->ReadToBuffer( hDiskFile, *memoryFile.pCompressedBuffer ) == false )
  748. return false;
  749. // Hold the compressed size
  750. memoryFile.nCompressedSize = memoryFile.pCompressedBuffer->TellMaxPut();
  751. // Close the disk file
  752. g_pFileSystem->Close( hDiskFile );
  753. SaveMsg("SIM: Loaded %s into memory\n", pFilename );
  754. return true;
  755. }
  756. //-----------------------------------------------------------------------------
  757. // Purpose: Returns the number of save files in the specified filter
  758. // Note: This expects standard saverestore behavior, and does NOT
  759. // currently use pPath to filter file search.
  760. //-----------------------------------------------------------------------------
  761. int CSaveRestoreFileSystem::DirectoryCount( const char *pPath )
  762. {
  763. if ( !Q_stristr( pPath, "*.hl?" ) )
  764. {
  765. // Function depends on known behavior
  766. Assert( 0 );
  767. return 0;
  768. }
  769. int count = 0;
  770. for ( int i = GetDirectory().FirstInorder(); GetDirectory().IsValidIndex( i ); i = GetDirectory().NextInorder( i ) )
  771. {
  772. SaveFile_t &file = GetFile( i );
  773. if ( Q_stristr( GetString( file.name ), ".hl" ) )
  774. {
  775. ++count;
  776. }
  777. }
  778. return count;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Purpose: Clears the save directory of temporary save files (*.hl)
  782. // Note: This expects standard saverestore behavior, and does NOT
  783. // currently use pPath to filter file search.
  784. //-----------------------------------------------------------------------------
  785. void CSaveRestoreFileSystem::DirectoryClear( const char *pPath, bool bIsXSave )
  786. {
  787. if ( !Q_stristr( pPath, "*.hl?" ) )
  788. {
  789. // Function depends on known behavior
  790. Assert( 0 );
  791. return;
  792. }
  793. int i = GetDirectory().FirstInorder();
  794. while ( GetDirectory().IsValidIndex( i ) )
  795. {
  796. SaveFile_t &file = GetFile( i );
  797. i = GetDirectory().NextInorder( i );
  798. if ( Q_stristr( GetString( file.name ), ".hl" ) )
  799. {
  800. SaveMsg("SIM: Cleared: %s\n", GetString( file.name ) );
  801. // Delete the temporary save file
  802. RemoveFile( GetString( file.name ) );
  803. }
  804. }
  805. }
  806. //-----------------------------------------------------------------------------
  807. // Purpose: Transfer all save files from memory to disk.
  808. //-----------------------------------------------------------------------------
  809. void CSaveRestoreFileSystem::WriteSaveDirectoryToDisk( void )
  810. {
  811. char szPath[ MAX_PATH ];
  812. int i = GetDirectory().FirstInorder();
  813. while ( GetDirectory().IsValidIndex( i ) )
  814. {
  815. SaveFile_t &file = GetFile( i );
  816. i = GetDirectory().NextInorder( i );
  817. const char *pName = GetString( file.name );
  818. if ( Q_stristr( pName, ".hl" ) )
  819. {
  820. // Write the temporary save file to disk
  821. Open( pName, "rb" );
  822. Q_snprintf( szPath, sizeof( szPath ), "%s%s", saverestore->GetSaveDir(), pName );
  823. g_pFileSystem->WriteFile( szPath, "GAME", *file.pBuffer );
  824. }
  825. }
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Purpose: Transfer all save files from disk to memory.
  829. //-----------------------------------------------------------------------------
  830. void CSaveRestoreFileSystem::LoadSaveDirectoryFromDisk( const char *pPath )
  831. {
  832. char const *findfn;
  833. char szPath[ MAX_PATH ];
  834. findfn = Sys_FindFirstEx( pPath, "DEFAULT_WRITE_PATH", NULL, 0 );
  835. while ( findfn != NULL )
  836. {
  837. Q_snprintf( szPath, sizeof( szPath ), "%s%s", saverestore->GetSaveDir(), findfn );
  838. // Add the temporary save file
  839. FileHandle_t hFile = Open( findfn, "wb" );
  840. if ( hFile )
  841. {
  842. SaveFile_t &file = GetFile( hFile );
  843. g_pFileSystem->ReadFile( szPath, "GAME", *file.pBuffer );
  844. file.nSize = file.pBuffer->TellMaxPut();
  845. Close( hFile );
  846. }
  847. // Any more save files
  848. findfn = Sys_FindNext( NULL, 0 );
  849. }
  850. Sys_FindClose();
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Purpose:
  854. //-----------------------------------------------------------------------------
  855. void CSaveRestoreFileSystem::AuditFiles( void )
  856. {
  857. unsigned int nTotalFiles = 0;
  858. unsigned int nTotalCompressed = 0;
  859. unsigned int nTotalUncompressed = 0;
  860. int i = GetDirectory().FirstInorder();
  861. while ( GetDirectory().IsValidIndex( i ) )
  862. {
  863. SaveFile_t &file = GetFile( i );
  864. i = GetDirectory().NextInorder( i );
  865. nTotalFiles++;
  866. nTotalCompressed += file.nCompressedSize;
  867. nTotalUncompressed += file.nSize;
  868. Msg("SIM: File: %s [c: %.02f KB / u: %.02f KB]\n", GetString( file.name ), (float)file.nCompressedSize/1024.0f, (float)file.nSize/1024.0f );
  869. }
  870. Msg("SIM: ------------------------------------------------------------");
  871. 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 );
  872. }
  873. CON_COMMAND( audit_save_in_memory, "Audit the memory usage and files in the save-to-memory system" )
  874. {
  875. if ( !IsX360() )
  876. return;
  877. g_pSaveRestoreFileSystem->AuditFiles();
  878. }
  879. CON_COMMAND( dump_x360_saves, "Dump X360 save games to disk" )
  880. {
  881. if ( !IsX360() )
  882. {
  883. Warning("dump_x360 only available on X360 platform!\n");
  884. return;
  885. }
  886. if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
  887. {
  888. Warning( "No storage device attached!\n" );
  889. return;
  890. }
  891. char szInName[MAX_PATH]; // Read path from the container
  892. char szOutName[MAX_PATH]; // Output path to the disk
  893. char szFileNameBase[MAX_PATH]; // Name of the file minus directories or extensions
  894. FileFindHandle_t findHandle;
  895. char szSearchPath[MAX_PATH];
  896. Q_snprintf( szSearchPath, sizeof( szSearchPath ), "%s:\\*.*", GetCurrentMod() );
  897. const char *pFileName = g_pFileSystem->FindFirst( szSearchPath, &findHandle );
  898. while (pFileName)
  899. {
  900. // Create the proper read path
  901. Q_snprintf( szInName, sizeof( szInName ), "%s:\\%s", GetCurrentMod(), pFileName );
  902. // Read the file and blat it out
  903. CUtlBuffer buf( 0, 0, 0 );
  904. if ( g_pFileSystem->ReadFile( szInName, NULL, buf ) )
  905. {
  906. // Strip us down to just our filename
  907. Q_FileBase( pFileName, szFileNameBase, sizeof ( szFileNameBase ) );
  908. Q_snprintf( szOutName, sizeof( szOutName ), "save/%s.sav", szFileNameBase );
  909. g_pFileSystem->WriteFile( szOutName, NULL, buf );
  910. Msg("Copied file: %s to %s\n", szInName, szOutName );
  911. }
  912. // Clean up
  913. buf.Clear();
  914. // Any more save files
  915. pFileName = g_pFileSystem->FindNext( findHandle );
  916. }
  917. g_pFileSystem->FindClose( findHandle );
  918. }
  919. CON_COMMAND( dump_x360_cfg, "Dump X360 config files to disk" )
  920. {
  921. if ( !IsX360() )
  922. {
  923. Warning("dump_x360 only available on X360 platform!\n");
  924. return;
  925. }
  926. if ( XBX_GetStorageDeviceId() == XBX_INVALID_STORAGE_ID || XBX_GetStorageDeviceId() == XBX_STORAGE_DECLINED )
  927. {
  928. Warning( "No storage device attached!\n" );
  929. return;
  930. }
  931. char szInName[MAX_PATH]; // Read path from the container
  932. char szOutName[MAX_PATH]; // Output path to the disk
  933. char szFileNameBase[MAX_PATH]; // Name of the file minus directories or extensions
  934. FileFindHandle_t findHandle;
  935. char szSearchPath[MAX_PATH];
  936. Q_snprintf( szSearchPath, sizeof( szSearchPath ), "cfg:\\*.*" );
  937. const char *pFileName = g_pFileSystem->FindFirst( szSearchPath, &findHandle );
  938. while (pFileName)
  939. {
  940. // Create the proper read path
  941. Q_snprintf( szInName, sizeof( szInName ), "cfg:\\%s", pFileName );
  942. // Read the file and blat it out
  943. CUtlBuffer buf( 0, 0, 0 );
  944. if ( g_pFileSystem->ReadFile( szInName, NULL, buf ) )
  945. {
  946. // Strip us down to just our filename
  947. Q_FileBase( pFileName, szFileNameBase, sizeof ( szFileNameBase ) );
  948. Q_snprintf( szOutName, sizeof( szOutName ), "%s.cfg", szFileNameBase );
  949. g_pFileSystem->WriteFile( szOutName, NULL, buf );
  950. Msg("Copied file: %s to %s\n", szInName, szOutName );
  951. }
  952. // Clean up
  953. buf.Clear();
  954. // Any more save files
  955. pFileName = g_pFileSystem->FindNext( findHandle );
  956. }
  957. g_pFileSystem->FindClose( findHandle );
  958. }
  959. #define FILECOPYBUFSIZE (1024 * 1024)
  960. //-----------------------------------------------------------------------------
  961. // Purpose: Copy one file to another file
  962. //-----------------------------------------------------------------------------
  963. static bool FileCopy( FileHandle_t pOutput, FileHandle_t pInput, int fileSize )
  964. {
  965. // allocate a reasonably large file copy buffer, since otherwise write performance under steam suffers
  966. char *buf = (char *)malloc(FILECOPYBUFSIZE);
  967. int size;
  968. int readSize;
  969. bool success = true;
  970. while ( fileSize > 0 )
  971. {
  972. if ( fileSize > FILECOPYBUFSIZE )
  973. size = FILECOPYBUFSIZE;
  974. else
  975. size = fileSize;
  976. if ( ( readSize = g_pSaveRestoreFileSystem->Read( buf, size, pInput ) ) < size )
  977. {
  978. Warning( "Unexpected end of file expanding save game\n" );
  979. fileSize = 0;
  980. success = false;
  981. break;
  982. }
  983. g_pSaveRestoreFileSystem->Write( buf, readSize, pOutput );
  984. fileSize -= size;
  985. }
  986. free(buf);
  987. return success;
  988. }
  989. struct filelistelem_t
  990. {
  991. char szFileName[MAX_PATH];
  992. };
  993. //-----------------------------------------------------------------------------
  994. // Purpose: Implementation to execute traditional save to disk behavior
  995. //-----------------------------------------------------------------------------
  996. class CSaveRestoreFileSystemPassthrough : public ISaveRestoreFileSystem
  997. {
  998. public:
  999. CSaveRestoreFileSystemPassthrough() : m_iContainerOpens( 0 ) {}
  1000. bool FileExists( const char *pFileName, const char *pPathID )
  1001. {
  1002. return g_pFileSystem->FileExists( pFileName, pPathID );
  1003. }
  1004. void RemoveFile( char const* pRelativePath, const char *pathID )
  1005. {
  1006. g_pFileSystem->RemoveFile( pRelativePath, pathID );
  1007. }
  1008. void RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID )
  1009. {
  1010. g_pFileSystem->RenameFile( pOldPath, pNewPath, pathID );
  1011. }
  1012. void AsyncFinishAllWrites( void )
  1013. {
  1014. g_pFileSystem->AsyncFinishAllWrites();
  1015. }
  1016. FileHandle_t Open( const char *pFullName, const char *pOptions, const char *pathID )
  1017. {
  1018. return g_pFileSystem->OpenEx( pFullName, pOptions, FSOPEN_NEVERINPACK, pathID );
  1019. }
  1020. void Close( FileHandle_t hSaveFile )
  1021. {
  1022. g_pFileSystem->Close( hSaveFile );
  1023. }
  1024. int Read( void *pOutput, int size, FileHandle_t hFile )
  1025. {
  1026. return g_pFileSystem->Read( pOutput, size, hFile );
  1027. }
  1028. int Write( void const* pInput, int size, FileHandle_t hFile )
  1029. {
  1030. return g_pFileSystem->Write( pInput, size, hFile );
  1031. }
  1032. FSAsyncStatus_t AsyncWrite( const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl )
  1033. {
  1034. SaveMsg( "AsyncWrite (%s/%d)...\n", pFileName, nSrcBytes );
  1035. return g_pFileSystem->AsyncWrite( pFileName, pSrc, nSrcBytes, bFreeMemory, bAppend, pControl );
  1036. }
  1037. void Seek( FileHandle_t hFile, int pos, FileSystemSeek_t method )
  1038. {
  1039. g_pFileSystem->Seek( hFile, pos, method );
  1040. }
  1041. unsigned int Tell( FileHandle_t hFile )
  1042. {
  1043. return g_pFileSystem->Tell( hFile );
  1044. }
  1045. unsigned int Size( FileHandle_t hFile )
  1046. {
  1047. return g_pFileSystem->Size( hFile );
  1048. }
  1049. unsigned int Size( const char *pFileName, const char *pPathID )
  1050. {
  1051. return g_pFileSystem->Size( pFileName, pPathID );
  1052. }
  1053. FSAsyncStatus_t AsyncFinish( FSAsyncControl_t hControl, bool wait )
  1054. {
  1055. return g_pFileSystem->AsyncFinish( hControl, wait );
  1056. }
  1057. void AsyncRelease( FSAsyncControl_t hControl )
  1058. {
  1059. g_pFileSystem->AsyncRelease( hControl );
  1060. }
  1061. FSAsyncStatus_t AsyncAppend(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl )
  1062. {
  1063. return g_pFileSystem->AsyncAppend( pFileName, pSrc, nSrcBytes, bFreeMemory, pControl );
  1064. }
  1065. FSAsyncStatus_t AsyncAppendFile(const char *pDestFileName, const char *pSrcFileName, FSAsyncControl_t *pControl )
  1066. {
  1067. return g_pFileSystem->AsyncAppendFile( pDestFileName, pSrcFileName, pControl );
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Purpose: Copies the contents of the save directory into a single file
  1071. //-----------------------------------------------------------------------------
  1072. void DirectoryCopy( const char *pPath, const char *pDestFileName, bool bIsXSave )
  1073. {
  1074. SaveMsg( "DirectoryCopy....\n");
  1075. CUtlVector<filelistelem_t> list;
  1076. // force the writes to finish before trying to get the size/existence of a file
  1077. // @TODO: don't need this if retain sizes for files written earlier in process
  1078. SaveMsg( "DirectoryCopy: AsyncFinishAllWrites\n");
  1079. g_pFileSystem->AsyncFinishAllWrites();
  1080. // build the directory list
  1081. char basefindfn[ MAX_PATH ];
  1082. const char *findfn = Sys_FindFirstEx(pPath, "DEFAULT_WRITE_PATH", basefindfn, sizeof( basefindfn ) );
  1083. while ( findfn )
  1084. {
  1085. int index = list.AddToTail();
  1086. memset( list[index].szFileName, 0, sizeof(list[index].szFileName) );
  1087. Q_strncpy( list[index].szFileName, findfn, sizeof(list[index].szFileName) );
  1088. findfn = Sys_FindNext( basefindfn, sizeof( basefindfn ) );
  1089. }
  1090. Sys_FindClose();
  1091. // write the list of files to the save file
  1092. char szName[MAX_PATH];
  1093. for ( int i = 0; i < list.Count(); i++ )
  1094. {
  1095. if ( !bIsXSave )
  1096. {
  1097. Q_snprintf( szName, sizeof( szName ), "%s%s", saverestore->GetSaveDir(), list[i].szFileName );
  1098. }
  1099. else
  1100. {
  1101. Q_snprintf( szName, sizeof( szName ), "%s:\\%s", GetCurrentMod(), list[i].szFileName );
  1102. }
  1103. Q_FixSlashes( szName );
  1104. int fileSize = g_pFileSystem->Size( szName );
  1105. if ( fileSize )
  1106. {
  1107. Assert( sizeof(list[i].szFileName) == MAX_PATH );
  1108. SaveMsg( "DirectoryCopy: AsyncAppend %s, %s\n", szName, pDestFileName );
  1109. 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
  1110. g_pFileSystem->AsyncAppend( pDestFileName, new int(fileSize), sizeof(int), true );
  1111. g_pFileSystem->AsyncAppendFile( pDestFileName, szName );
  1112. }
  1113. }
  1114. }
  1115. //-----------------------------------------------------------------------------
  1116. // Purpose: Extracts all the files contained within pFile
  1117. //-----------------------------------------------------------------------------
  1118. bool DirectoryExtract( FileHandle_t pFile, int fileCount, bool bIsXSave )
  1119. {
  1120. int fileSize;
  1121. FileHandle_t pCopy;
  1122. char szName[ MAX_PATH ], fileName[ MAX_PATH ];
  1123. bool success = true;
  1124. for ( int i = 0; i < fileCount && success; i++ )
  1125. {
  1126. // Filename can only be as long as a map name + extension
  1127. if ( g_pSaveRestoreFileSystem->Read( fileName, MAX_PATH, pFile ) != MAX_PATH )
  1128. return false;
  1129. if ( g_pSaveRestoreFileSystem->Read( &fileSize, sizeof(int), pFile ) != sizeof(int) )
  1130. return false;
  1131. if ( !fileSize )
  1132. return false;
  1133. if ( !bIsXSave )
  1134. {
  1135. Q_snprintf( szName, sizeof( szName ), "%s%s", saverestore->GetSaveDir(), fileName );
  1136. }
  1137. else
  1138. {
  1139. Q_snprintf( szName, sizeof( szName ), "%s:\\%s", GetCurrentMod(), fileName );
  1140. }
  1141. Q_FixSlashes( szName );
  1142. pCopy = g_pSaveRestoreFileSystem->Open( szName, "wb", "MOD" );
  1143. if ( !pCopy )
  1144. return false;
  1145. success = FileCopy( pCopy, pFile, fileSize );
  1146. g_pSaveRestoreFileSystem->Close( pCopy );
  1147. }
  1148. return success;
  1149. }
  1150. //-----------------------------------------------------------------------------
  1151. // Purpose: returns the number of files in the specified filter
  1152. //-----------------------------------------------------------------------------
  1153. int DirectoryCount( const char *pPath )
  1154. {
  1155. int count = 0;
  1156. const char *findfn = Sys_FindFirstEx( pPath, "DEFAULT_WRITE_PATH", NULL, 0 );
  1157. while ( findfn != NULL )
  1158. {
  1159. count++;
  1160. findfn = Sys_FindNext(NULL, 0 );
  1161. }
  1162. Sys_FindClose();
  1163. return count;
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. // Purpose: Clears the save directory of all temporary files (*.hl)
  1167. //-----------------------------------------------------------------------------
  1168. void DirectoryClear( const char *pPath, bool bIsXSave )
  1169. {
  1170. char const *findfn;
  1171. char szPath[ MAX_PATH ];
  1172. findfn = Sys_FindFirstEx( pPath, "DEFAULT_WRITE_PATH", NULL, 0 );
  1173. while ( findfn != NULL )
  1174. {
  1175. if ( !bIsXSave )
  1176. {
  1177. Q_snprintf( szPath, sizeof( szPath ), "%s%s", saverestore->GetSaveDir(), findfn );
  1178. }
  1179. else
  1180. {
  1181. Q_snprintf( szPath, sizeof( szPath ), "%s:\\%s", GetCurrentMod(), findfn );
  1182. }
  1183. // Delete the temporary save file
  1184. g_pFileSystem->RemoveFile( szPath, "MOD" );
  1185. // Any more save files
  1186. findfn = Sys_FindNext( NULL, 0 );
  1187. }
  1188. Sys_FindClose();
  1189. }
  1190. void AuditFiles( void )
  1191. {
  1192. Msg("Not using save-in-memory path!\n" );
  1193. }
  1194. bool LoadFileFromDisk( const char *pFilename )
  1195. {
  1196. Msg("Not using save-in-memory path!\n" );
  1197. return true;
  1198. }
  1199. private:
  1200. int m_iContainerOpens;
  1201. };
  1202. static CSaveRestoreFileSystem s_SaveRestoreFileSystem;
  1203. static CSaveRestoreFileSystemPassthrough s_SaveRestoreFileSystemPassthrough;
  1204. #ifdef _X360
  1205. ISaveRestoreFileSystem *g_pSaveRestoreFileSystem = &s_SaveRestoreFileSystem;
  1206. #else
  1207. ISaveRestoreFileSystem *g_pSaveRestoreFileSystem = &s_SaveRestoreFileSystemPassthrough;
  1208. #endif // _X360
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose: Called when switching between saving in memory and saving to disk.
  1211. //-----------------------------------------------------------------------------
  1212. void SaveInMemoryCallback( IConVar *pConVar, const char *pOldString, float flOldValue )
  1213. {
  1214. if ( !IsX360() )
  1215. {
  1216. Warning( "save_in_memory is compatible with only the Xbox 360!\n" );
  1217. return;
  1218. }
  1219. ConVarRef var( pConVar );
  1220. if ( var.GetFloat() == flOldValue )
  1221. return;
  1222. // *.hl? files are transferred between disk and memory when this cvar changes
  1223. char szPath[ MAX_PATH ];
  1224. Q_snprintf( szPath, sizeof( szPath ), "%s%s", saverestore->GetSaveDir(), "*.hl?" );
  1225. if ( var.GetBool() )
  1226. {
  1227. g_pSaveRestoreFileSystem = &s_SaveRestoreFileSystem;
  1228. // Clear memory and load
  1229. s_SaveRestoreFileSystem.DirectoryClear( "*.hl?", IsX360() );
  1230. s_SaveRestoreFileSystem.LoadSaveDirectoryFromDisk( szPath );
  1231. // Clear disk
  1232. s_SaveRestoreFileSystemPassthrough.DirectoryClear( szPath, IsX360() );
  1233. }
  1234. else
  1235. {
  1236. g_pSaveRestoreFileSystem = &s_SaveRestoreFileSystemPassthrough;
  1237. // Clear disk and write
  1238. s_SaveRestoreFileSystemPassthrough.DirectoryClear( szPath, IsX360() );
  1239. s_SaveRestoreFileSystem.WriteSaveDirectoryToDisk();
  1240. // Clear memory
  1241. s_SaveRestoreFileSystem.DirectoryClear( "*.hl?", IsX360() );
  1242. }
  1243. }
  1244. //-----------------------------------------------------------------------------
  1245. // Purpose: Dump a list of the save directory contents (in memory) to the console
  1246. //-----------------------------------------------------------------------------
  1247. void CSaveRestoreFileSystem::DumpSaveDirectory( void )
  1248. {
  1249. unsigned int totalCompressedSize = 0;
  1250. unsigned int totalUncompressedSize = 0;
  1251. for ( int i = GetDirectory().FirstInorder(); GetDirectory().IsValidIndex( i ); i = GetDirectory().NextInorder( i ) )
  1252. {
  1253. SaveFile_t &file = GetFile( i );
  1254. Msg( "File %d: %s Size:%d\n", i, GetString( file.name ), file.nCompressedSize );
  1255. totalUncompressedSize += file.nSize;
  1256. totalCompressedSize += file.nCompressedSize;
  1257. }
  1258. float percent = 0.f;
  1259. if ( totalUncompressedSize )
  1260. percent = 100.f - (totalCompressedSize / totalUncompressedSize * 100.f);
  1261. Msg( "Total Size: %.2f Mb (%d bytes)\n", totalCompressedSize / (1024.f*1024.f), totalCompressedSize );
  1262. Msg( "Compression: %.2f Mb to %.2f Mb (%.0f%%)\n", totalUncompressedSize/(1024.f*1024.f), totalCompressedSize/(1024.f*1024.f), percent );
  1263. }
  1264. CON_COMMAND( dumpsavedir, "List the contents of the save directory in memory" )
  1265. {
  1266. s_SaveRestoreFileSystem.DumpSaveDirectory();
  1267. }