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.

3369 lines
95 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Save game read and write. Any *.hl? files may be stored in memory, so use
  4. // g_pSaveRestoreFileSystem when accessing them. The .sav file is always stored
  5. // on disk, so use g_pFileSystem when accessing it.
  6. //
  7. // $Workfile: $
  8. // $Date: $
  9. // $NoKeywords: $
  10. //=============================================================================//
  11. // Save / Restore System
  12. #include <ctype.h>
  13. #ifdef _WIN32
  14. #include "winerror.h"
  15. #endif
  16. #include "client.h"
  17. #include "server.h"
  18. #include "vengineserver_impl.h"
  19. #include "host_cmd.h"
  20. #include "sys.h"
  21. #include "cdll_int.h"
  22. #include "tmessage.h"
  23. #include "screen.h"
  24. #include "decal.h"
  25. #include "zone.h"
  26. #include "sv_main.h"
  27. #include "host.h"
  28. #include "r_local.h"
  29. #include "filesystem.h"
  30. #include "filesystem_engine.h"
  31. #include "host_state.h"
  32. #include "datamap.h"
  33. #include "string_t.h"
  34. #include "PlayerState.h"
  35. #include "saverestoretypes.h"
  36. #include "demo.h"
  37. #include "icliententity.h"
  38. #include "r_efx.h"
  39. #include "icliententitylist.h"
  40. #include "cdll_int.h"
  41. #include "utldict.h"
  42. #include "decal_private.h"
  43. #include "engine/IEngineTrace.h"
  44. #include "enginetrace.h"
  45. #include "baseautocompletefilelist.h"
  46. #include "sound.h"
  47. #include "vgui_baseui_interface.h"
  48. #include "gl_matsysiface.h"
  49. #include "cl_main.h"
  50. #include "pr_edict.h"
  51. #include "tier0/vprof.h"
  52. #include <vgui/ILocalize.h>
  53. #include "vgui_controls/Controls.h"
  54. #include "tier0/icommandline.h"
  55. #include "testscriptmgr.h"
  56. #include "vengineserver_impl.h"
  57. #include "saverestore_filesystem.h"
  58. #include "tier1/callqueue.h"
  59. #include "vstdlib/jobthread.h"
  60. #include "enginebugreporter.h"
  61. #include "tier1/memstack.h"
  62. #include "vstdlib/jobthread.h"
  63. #if !defined( _X360 )
  64. #include "xbox/xboxstubs.h"
  65. #else
  66. #include "xbox/xbox_launch.h"
  67. #endif
  68. #include "ixboxsystem.h"
  69. extern IXboxSystem *g_pXboxSystem;
  70. // memdbgon must be the last include file in a .cpp file!!!
  71. #include "tier0/memdbgon.h"
  72. extern IBaseClientDLL *g_ClientDLL;
  73. extern ConVar deathmatch;
  74. extern ConVar skill;
  75. extern ConVar save_in_memory;
  76. extern CGlobalVars g_ServerGlobalVariables;
  77. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  78. // Keep the last 1 autosave / quick saves
  79. ConVar save_history_count("save_history_count", "1", 0, "Keep this many old copies in history of autosaves and quicksaves." );
  80. ConVar sv_autosave( "sv_autosave", "1", 0, "Set to 1 to autosave game on level transition. Does not affect autosave triggers." );
  81. ConVar save_async( "save_async", "1" );
  82. ConVar save_disable( "save_disable", "0" );
  83. ConVar save_noxsave( "save_noxsave", "0" );
  84. ConVar save_screenshot( "save_screenshot", "1", 0, "0 = none, 1 = non-autosave, 2 = always" );
  85. ConVar save_spew( "save_spew", "0" );
  86. #define SaveMsg if ( !save_spew.GetBool() ) ; else Msg
  87. // HACK HACK: Some hacking to keep the .sav file backward compatible on the client!!!
  88. #define SECTION_MAGIC_NUMBER 0x54541234
  89. #define SECTION_VERSION_NUMBER 2
  90. CCallQueue g_AsyncSaveCallQueue;
  91. static bool g_ConsoleInput = false;
  92. static char g_szMapLoadOverride[32];
  93. #define MOD_DIR ( IsX360() ? "DEFAULT_WRITE_PATH" : "MOD" )
  94. //-----------------------------------------------------------------------------
  95. IThreadPool *g_pSaveThread;
  96. static bool g_bSaveInProgress = false;
  97. //-----------------------------------------------------------------------------
  98. static bool HaveExactMap( const char *pszMapName )
  99. {
  100. char szCanonName[64] = { 0 };
  101. V_strncpy( szCanonName, pszMapName, sizeof( szCanonName ) );
  102. IVEngineServer::eFindMapResult eResult = g_pVEngineServer->FindMap( szCanonName, sizeof( szCanonName ) );
  103. switch ( eResult )
  104. {
  105. case IVEngineServer::eFindMap_Found:
  106. return true;
  107. case IVEngineServer::eFindMap_NonCanonical:
  108. case IVEngineServer::eFindMap_NotFound:
  109. case IVEngineServer::eFindMap_FuzzyMatch:
  110. case IVEngineServer::eFindMap_PossiblyAvailable:
  111. return false;
  112. }
  113. AssertMsg( false, "Unhandled engine->FindMap return value\n" );
  114. return false;
  115. }
  116. void FinishAsyncSave()
  117. {
  118. LOCAL_THREAD_LOCK();
  119. SaveMsg( "FinishAsyncSave() (%d/%d)\n", ThreadInMainThread(), ThreadGetCurrentId() );
  120. if ( g_AsyncSaveCallQueue.Count() )
  121. {
  122. g_AsyncSaveCallQueue.CallQueued();
  123. g_pFileSystem->AsyncFinishAllWrites();
  124. }
  125. g_bSaveInProgress = false;
  126. }
  127. void DispatchAsyncSave()
  128. {
  129. Assert( !g_bSaveInProgress );
  130. g_bSaveInProgress = true;
  131. if ( save_async.GetBool() )
  132. {
  133. g_pSaveThread->QueueCall( &FinishAsyncSave );
  134. }
  135. else
  136. {
  137. FinishAsyncSave();
  138. }
  139. }
  140. //-----------------------------------------------------------------------------
  141. inline void GetServerSaveCommentEx( char *comment, int maxlength, float flMinutes, float flSeconds )
  142. {
  143. if ( g_iServerGameDLLVersion >= 5 )
  144. {
  145. serverGameDLL->GetSaveComment( comment, maxlength, flMinutes, flSeconds );
  146. }
  147. else
  148. {
  149. Assert( 0 );
  150. }
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose: Alloc/free memory for save games
  154. // Input : num -
  155. // size -
  156. //-----------------------------------------------------------------------------
  157. class CSaveMemory : public CMemoryStack
  158. {
  159. public:
  160. CSaveMemory()
  161. {
  162. MEM_ALLOC_CREDIT();
  163. Init( 32*1024*1024, 64, 2*1024*1024 + 192*1024 );
  164. }
  165. int m_nSaveAllocs;
  166. };
  167. CSaveMemory &GetSaveMemory()
  168. {
  169. static CSaveMemory g_SaveMemory;
  170. return g_SaveMemory;
  171. }
  172. void *SaveAllocMemory( size_t num, size_t size, bool bClear )
  173. {
  174. MEM_ALLOC_CREDIT();
  175. ++GetSaveMemory().m_nSaveAllocs;
  176. size_t nBytes = num * size;
  177. return GetSaveMemory().Alloc( nBytes, bClear );
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. // Input : *pSaveMem -
  182. //-----------------------------------------------------------------------------
  183. void SaveFreeMemory( void *pSaveMem )
  184. {
  185. --GetSaveMemory().m_nSaveAllocs;
  186. if ( !GetSaveMemory().m_nSaveAllocs )
  187. {
  188. GetSaveMemory().FreeAll( false );
  189. }
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Reset save memory stack, as some failed save/load paths will leak
  193. //-----------------------------------------------------------------------------
  194. void SaveResetMemory()
  195. {
  196. GetSaveMemory().m_nSaveAllocs = 0;
  197. GetSaveMemory().FreeAll( false );
  198. }
  199. //-----------------------------------------------------------------------------
  200. //
  201. //-----------------------------------------------------------------------------
  202. struct GAME_HEADER
  203. {
  204. DECLARE_SIMPLE_DATADESC();
  205. char mapName[32];
  206. char comment[80];
  207. int mapCount; // the number of map state files in the save file. This is usually number of maps * 3 (.hl1, .hl2, .hl3 files)
  208. char originMapName[32];
  209. char landmark[256];
  210. };
  211. struct SAVE_HEADER
  212. {
  213. DECLARE_SIMPLE_DATADESC();
  214. int saveId;
  215. int version;
  216. int skillLevel;
  217. int connectionCount;
  218. int lightStyleCount;
  219. int mapVersion;
  220. float time__USE_VCR_MODE; // This is renamed to include the __USE_VCR_MODE prefix due to a #define on win32 from the VCR mode changes
  221. // The actual save games have the string "time__USE_VCR_MODE" in them
  222. char mapName[32];
  223. char skyName[32];
  224. };
  225. struct SAVELIGHTSTYLE
  226. {
  227. DECLARE_SIMPLE_DATADESC();
  228. int index;
  229. char style[64];
  230. };
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. //-----------------------------------------------------------------------------
  234. class CSaveRestore : public ISaveRestore
  235. {
  236. public:
  237. CSaveRestore()
  238. {
  239. m_bClearSaveDir = false;
  240. m_szSaveGameScreenshotFile[0] = 0;
  241. SetMostRecentElapsedMinutes( 0 );
  242. SetMostRecentElapsedSeconds( 0 );
  243. m_szMostRecentSaveLoadGame[0] = 0;
  244. m_szSaveGameName[ 0 ] = 0;
  245. m_bIsXSave = IsX360();
  246. }
  247. void Init( void );
  248. void Shutdown( void );
  249. void OnFrameRendered();
  250. virtual bool SaveFileExists( const char *pName );
  251. bool LoadGame( const char *pName );
  252. char *GetSaveDir(void);
  253. void ClearSaveDir( void );
  254. void DoClearSaveDir( bool bIsXSave );
  255. void RequestClearSaveDir( void );
  256. int LoadGameState( char const *level, bool createPlayers );
  257. void LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName );
  258. const char *FindRecentSave( char *pNameBuf, int nameBufLen );
  259. void ForgetRecentSave( void );
  260. int SaveGameSlot( const char *pSaveName, const char *pSaveComment, bool onlyThisLevel, bool bSetMostRecent, const char *pszDestMap = NULL, const char *pszLandmark = NULL );
  261. bool SaveGameState( bool bTransition, CSaveRestoreData ** = NULL, bool bOpenContainer = true, bool bIsAutosaveOrDangerous = false );
  262. void RestoreClientState( char const *fileName, bool adjacent );
  263. void RestoreAdjacenClientState( char const *map );
  264. int IsValidSave( void );
  265. void Finish( CSaveRestoreData *save );
  266. void ClearRestoredIndexTranslationTables();
  267. void OnFinishedClientRestore();
  268. void AutoSaveDangerousIsSafe();
  269. virtual void UpdateSaveGameScreenshots();
  270. virtual char const *GetMostRecentlyLoadedFileName();
  271. virtual char const *GetSaveFileName();
  272. virtual void SetIsXSave( bool bIsXSave ) { m_bIsXSave = bIsXSave; }
  273. virtual bool IsXSave() { return ( m_bIsXSave && !save_noxsave.GetBool() ); }
  274. virtual void FinishAsyncSave() { ::FinishAsyncSave(); }
  275. void AddDeferredCommand( char const *pchCommand );
  276. virtual bool StorageDeviceValid( void );
  277. virtual bool IsSaveInProgress();
  278. private:
  279. bool SaveClientState( const char *name );
  280. void EntityPatchWrite( CSaveRestoreData *pSaveData, const char *level, bool bAsync = false );
  281. void EntityPatchRead( CSaveRestoreData *pSaveData, const char *level );
  282. void DirectoryCount( const char *pPath, int *pResult );
  283. void DirectoryCopy( const char *pPath, const char *pDestFileName, bool bIsXSave );
  284. bool DirectoryExtract( FileHandle_t pFile, int mapCount );
  285. void DirectoryClear( const char *pPath );
  286. void AgeSaveList( const char *pName, int count, bool bIsXSave );
  287. void AgeSaveFile( const char *pName, const char *ext, int count, bool bIsXSave );
  288. int SaveReadHeader( FileHandle_t pFile, GAME_HEADER *pHeader, int readGlobalState, bool *pbOldSave );
  289. CSaveRestoreData *LoadSaveData( const char *level );
  290. void ParseSaveTables( CSaveRestoreData *pSaveData, SAVE_HEADER *pHeader, int updateGlobals );
  291. int FileSize( FileHandle_t pFile );
  292. bool CalcSaveGameName( const char *pName, char *output, int outputStringLength );
  293. CSaveRestoreData * SaveGameStateInit( void );
  294. void SaveGameStateGlobals( CSaveRestoreData *pSaveData );
  295. int SaveReadNameAndComment( FileHandle_t f, OUT_Z_CAP(nameSize) char *name, int nameSize, OUT_Z_CAP(commentSize) char *comment, int commentSize ) OVERRIDE;
  296. void BuildRestoredIndexTranslationTable( char const *mapname, CSaveRestoreData *pSaveData, bool verbose );
  297. char const *GetSaveGameMapName( char const *level );
  298. void SetMostRecentSaveGame( const char *pSaveName );
  299. int GetMostRecentElapsedMinutes( void );
  300. int GetMostRecentElapsedSeconds( void );
  301. int GetMostRecentElapsedTimeSet( void );
  302. void SetMostRecentElapsedMinutes( const int min );
  303. void SetMostRecentElapsedSeconds( const int sec );
  304. struct SaveRestoreTranslate
  305. {
  306. string_t classname;
  307. int savedindex;
  308. int restoredindex;
  309. };
  310. struct RestoreLookupTable
  311. {
  312. RestoreLookupTable() :
  313. m_vecLandMarkOffset( 0, 0, 0 )
  314. {
  315. }
  316. void Clear()
  317. {
  318. lookup.RemoveAll();
  319. m_vecLandMarkOffset.Init();
  320. }
  321. RestoreLookupTable( const RestoreLookupTable& src )
  322. {
  323. int c = src.lookup.Count();
  324. for ( int i = 0 ; i < c; i++ )
  325. {
  326. lookup.AddToTail( src.lookup[ i ] );
  327. }
  328. m_vecLandMarkOffset = src.m_vecLandMarkOffset;
  329. }
  330. RestoreLookupTable& operator=( const RestoreLookupTable& src )
  331. {
  332. if ( this == &src )
  333. return *this;
  334. int c = src.lookup.Count();
  335. for ( int i = 0 ; i < c; i++ )
  336. {
  337. lookup.AddToTail( src.lookup[ i ] );
  338. }
  339. m_vecLandMarkOffset = src.m_vecLandMarkOffset;
  340. return *this;
  341. }
  342. CUtlVector< SaveRestoreTranslate > lookup;
  343. Vector m_vecLandMarkOffset;
  344. };
  345. RestoreLookupTable *FindOrAddRestoreLookupTable( char const *mapname );
  346. int LookupRestoreSpotSaveIndex( RestoreLookupTable *table, int save );
  347. void ReapplyDecal( bool adjacent, RestoreLookupTable *table, decallist_t *entry );
  348. CUtlDict< RestoreLookupTable, int > m_RestoreLookup;
  349. bool m_bClearSaveDir;
  350. char m_szSaveGameScreenshotFile[MAX_OSPATH];
  351. float m_flClientSaveRestoreTime;
  352. char m_szMostRecentSaveLoadGame[MAX_OSPATH];
  353. char m_szSaveGameName[MAX_OSPATH];
  354. int m_MostRecentElapsedMinutes;
  355. int m_MostRecentElapsedSeconds;
  356. int m_MostRecentElapsedTimeSet;
  357. bool m_bWaitingForSafeDangerousSave;
  358. bool m_bIsXSave;
  359. int m_nDeferredCommandFrames;
  360. CUtlVector< CUtlSymbol > m_sDeferredCommands;
  361. };
  362. CSaveRestore g_SaveRestore;
  363. ISaveRestore *saverestore = (ISaveRestore *)&g_SaveRestore;
  364. BEGIN_SIMPLE_DATADESC( GAME_HEADER )
  365. DEFINE_FIELD( mapCount, FIELD_INTEGER ),
  366. DEFINE_ARRAY( mapName, FIELD_CHARACTER, 32 ),
  367. DEFINE_ARRAY( comment, FIELD_CHARACTER, 80 ),
  368. DEFINE_ARRAY( originMapName, FIELD_CHARACTER, 32 ),
  369. DEFINE_ARRAY( landmark, FIELD_CHARACTER, 256 ),
  370. END_DATADESC()
  371. // The proper way to extend the file format (add a new data chunk) is to add a field here, and use it to determine
  372. // whether your new data chunk is in the file or not. If the file was not saved with your new field, the chunk
  373. // won't be there either.
  374. // Structure members can be added/deleted without any problems, new structures must be reflected in an existing struct
  375. // and not read unless actually in the file. New structure members will be zeroed out when reading 'old' files.
  376. BEGIN_SIMPLE_DATADESC( SAVE_HEADER )
  377. // DEFINE_FIELD( saveId, FIELD_INTEGER ),
  378. // DEFINE_FIELD( version, FIELD_INTEGER ),
  379. DEFINE_FIELD( skillLevel, FIELD_INTEGER ),
  380. DEFINE_FIELD( connectionCount, FIELD_INTEGER ),
  381. DEFINE_FIELD( lightStyleCount, FIELD_INTEGER ),
  382. DEFINE_FIELD( mapVersion, FIELD_INTEGER ),
  383. DEFINE_FIELD( time__USE_VCR_MODE, FIELD_TIME ),
  384. DEFINE_ARRAY( mapName, FIELD_CHARACTER, 32 ),
  385. DEFINE_ARRAY( skyName, FIELD_CHARACTER, 32 ),
  386. END_DATADESC()
  387. BEGIN_SIMPLE_DATADESC( levellist_t )
  388. DEFINE_ARRAY( mapName, FIELD_CHARACTER, 32 ),
  389. DEFINE_ARRAY( landmarkName, FIELD_CHARACTER, 32 ),
  390. DEFINE_FIELD( pentLandmark, FIELD_EDICT ),
  391. DEFINE_FIELD( vecLandmarkOrigin, FIELD_VECTOR ),
  392. END_DATADESC()
  393. BEGIN_SIMPLE_DATADESC( SAVELIGHTSTYLE )
  394. DEFINE_FIELD( index, FIELD_INTEGER ),
  395. DEFINE_ARRAY( style, FIELD_CHARACTER, 64 ),
  396. END_DATADESC()
  397. //-----------------------------------------------------------------------------
  398. // Purpose:
  399. // Output : char const
  400. //-----------------------------------------------------------------------------
  401. char const *CSaveRestore::GetSaveGameMapName( char const *level )
  402. {
  403. Assert( level );
  404. static char mapname[ 256 ];
  405. Q_FileBase( level, mapname, sizeof( mapname ) );
  406. return mapname;
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose: returns the most recent save
  410. //-----------------------------------------------------------------------------
  411. const char *CSaveRestore::FindRecentSave( char *pNameBuf, int nameBufLen )
  412. {
  413. Q_strncpy( pNameBuf, m_szMostRecentSaveLoadGame, nameBufLen );
  414. if ( !m_szMostRecentSaveLoadGame[0] )
  415. return NULL;
  416. return pNameBuf;
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose: Forgets the most recent save game
  420. // this is so the current level will just restart if the player dies
  421. //-----------------------------------------------------------------------------
  422. void CSaveRestore::ForgetRecentSave()
  423. {
  424. m_szMostRecentSaveLoadGame[0] = 0;
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Purpose: Returns the save game directory for the current player profile
  428. //-----------------------------------------------------------------------------
  429. char *CSaveRestore::GetSaveDir(void)
  430. {
  431. static char szDirectory[MAX_OSPATH];
  432. Q_memset(szDirectory, 0, MAX_OSPATH);
  433. Q_strncpy(szDirectory, "save/", sizeof( szDirectory ) );
  434. return szDirectory;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: keeps the last few save files of the specified file around, renamed
  438. //-----------------------------------------------------------------------------
  439. void CSaveRestore::AgeSaveList( const char *pName, int count, bool bIsXSave )
  440. {
  441. // age all the previous save files (including screenshots)
  442. while ( count > 0 )
  443. {
  444. AgeSaveFile( pName, IsX360() ? "360.sav" : "sav", count, bIsXSave );
  445. if ( !IsX360() )
  446. {
  447. AgeSaveFile( pName, "tga", count, bIsXSave );
  448. }
  449. count--;
  450. }
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose: ages a single sav file
  454. //-----------------------------------------------------------------------------
  455. void CSaveRestore::AgeSaveFile( const char *pName, const char *ext, int count, bool bIsXSave )
  456. {
  457. char newName[MAX_OSPATH], oldName[MAX_OSPATH];
  458. if ( !IsXSave() )
  459. {
  460. if ( count == 1 )
  461. {
  462. Q_snprintf( oldName, sizeof( oldName ), "//%s/%s%s.%s", MOD_DIR, GetSaveDir(), pName, ext );// quick.sav. DON'T FixSlashes on this, it needs to be //MOD
  463. }
  464. else
  465. {
  466. Q_snprintf( oldName, sizeof( oldName ), "//%s/%s%s%02d.%s", MOD_DIR, GetSaveDir(), pName, count-1, ext ); // quick04.sav, etc. DON'T FixSlashes on this, it needs to be //MOD
  467. }
  468. Q_snprintf( newName, sizeof( newName ), "//%s/%s%s%02d.%s", MOD_DIR, GetSaveDir(), pName, count, ext ); // DON'T FixSlashes on this, it needs to be //MOD
  469. }
  470. else
  471. {
  472. if ( count == 1 )
  473. {
  474. Q_snprintf( oldName, sizeof( oldName ), "%s:\\%s.%s", GetCurrentMod(), pName, ext );
  475. }
  476. else
  477. {
  478. Q_snprintf( oldName, sizeof( oldName ), "%s:\\%s%02d.%s", GetCurrentMod(), pName, count-1, ext );
  479. }
  480. Q_snprintf( newName, sizeof( newName ), "%s:\\%s%02d.%s", GetCurrentMod(), pName, count, ext );
  481. }
  482. // Scroll the name list down (rename quick04.sav to quick05.sav)
  483. if ( g_pFileSystem->FileExists( oldName ) )
  484. {
  485. if ( count == save_history_count.GetInt() )
  486. {
  487. // there could be an old version, remove it
  488. if ( g_pFileSystem->FileExists( newName ) )
  489. {
  490. g_pFileSystem->RemoveFile( newName );
  491. }
  492. }
  493. g_pFileSystem->RenameFile( oldName, newName );
  494. }
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //-----------------------------------------------------------------------------
  499. int CSaveRestore::IsValidSave( void )
  500. {
  501. if (cmd_source != src_command)
  502. return 0;
  503. // Don't parse autosave/transition save/restores during playback!
  504. if ( demoplayer->IsPlayingBack() )
  505. {
  506. return 0;
  507. }
  508. if ( !sv.IsActive() )
  509. {
  510. ConMsg ("Not playing a local game.\n");
  511. return 0;
  512. }
  513. if ( !cl.IsActive() )
  514. {
  515. ConMsg ("Can't save if not active.\n");
  516. return 0;
  517. }
  518. if ( sv.IsMultiplayer() )
  519. {
  520. ConMsg ("Can't save multiplayer games.\n");
  521. return 0;
  522. }
  523. if ( sv.GetClientCount() > 0 && sv.GetClient(0)->IsActive() )
  524. {
  525. Assert( serverGameClients );
  526. CGameClient *pGameClient = sv.Client( 0 );
  527. CPlayerState *pl = serverGameClients->GetPlayerState( pGameClient->edict );
  528. if ( !pl )
  529. {
  530. ConMsg ("Can't savegame without a player!\n");
  531. return 0;
  532. }
  533. // we can't save if we're dead... unless we're reporting a bug.
  534. if ( pl->deadflag != false && !bugreporter->IsVisible() )
  535. {
  536. ConMsg ("Can't savegame with a dead player\n");
  537. return 0;
  538. }
  539. }
  540. // Passed all checks, it's ok to save
  541. return 1;
  542. }
  543. static ConVar save_asyncdelay( "save_asyncdelay", "0", 0, "For testing, adds this many milliseconds of delay to the save operation." );
  544. //-----------------------------------------------------------------------------
  545. // Purpose: save a game with the given name/comment
  546. // note: Added S_ExtraUpdate calls to fix audio pops in autosaves
  547. //-----------------------------------------------------------------------------
  548. int CSaveRestore::SaveGameSlot( const char *pSaveName, const char *pSaveComment, bool onlyThisLevel, bool bSetMostRecent, const char *pszDestMap, const char *pszLandmark )
  549. {
  550. if ( save_disable.GetBool() )
  551. {
  552. return 0;
  553. }
  554. if ( save_asyncdelay.GetInt() > 0 )
  555. {
  556. Sys_Sleep( clamp( save_asyncdelay.GetInt(), 0, 3000 ) );
  557. }
  558. SaveMsg( "Start save... (%d/%d)\n", ThreadInMainThread(), ThreadGetCurrentId() );
  559. VPROF_BUDGET( "SaveGameSlot", "Save" );
  560. char hlPath[256], name[256], *pTokenData;
  561. int tag, i, tokenSize;
  562. CSaveRestoreData *pSaveData;
  563. GAME_HEADER gameHeader;
  564. #if defined( _MEMTEST )
  565. Cbuf_AddText( "mem_dump\n" );
  566. #endif
  567. g_pSaveRestoreFileSystem->AsyncFinishAllWrites();
  568. S_ExtraUpdate();
  569. FinishAsyncSave();
  570. SaveResetMemory();
  571. S_ExtraUpdate();
  572. g_AsyncSaveCallQueue.DisableQueue( !save_async.GetBool() );
  573. // Figure out the name for this save game
  574. CalcSaveGameName( pSaveName, name, sizeof( name ) );
  575. ConDMsg( "Saving game to %s...\n", name );
  576. Q_strncpy( m_szSaveGameName, name, sizeof( m_szSaveGameName )) ;
  577. if ( m_bClearSaveDir )
  578. {
  579. m_bClearSaveDir = false;
  580. g_AsyncSaveCallQueue.QueueCall( this, &CSaveRestore::DoClearSaveDir, IsXSave() );
  581. }
  582. if ( !IsXSave() )
  583. {
  584. if ( onlyThisLevel )
  585. {
  586. Q_snprintf( hlPath, sizeof( hlPath ), "%s%s*.HL?", GetSaveDir(), sv.GetMapName() );
  587. }
  588. else
  589. {
  590. Q_snprintf( hlPath, sizeof( hlPath ), "%s*.HL?", GetSaveDir() );
  591. }
  592. }
  593. else
  594. {
  595. if ( onlyThisLevel )
  596. {
  597. Q_snprintf( hlPath, sizeof( hlPath ), "%s:\\%s*.HL?", GetCurrentMod(), sv.GetMapName() );
  598. }
  599. else
  600. {
  601. Q_snprintf( hlPath, sizeof( hlPath ), "%s:\\*.HL?", GetCurrentMod() );
  602. }
  603. }
  604. // Output to disk
  605. bool bClearFile = true;
  606. bool bIsQuick = ( stricmp(pSaveName, "quick") == 0 );
  607. bool bIsAutosave = ( !bIsQuick && stricmp(pSaveName,"autosave") == 0 );
  608. bool bIsAutosaveDangerous = ( !bIsAutosave && stricmp(pSaveName,"autosavedangerous") == 0 );
  609. if ( bIsQuick || bIsAutosave || bIsAutosaveDangerous )
  610. {
  611. bClearFile = false;
  612. SaveMsg( "Queue AgeSaveList\n");
  613. if ( StorageDeviceValid() )
  614. {
  615. g_AsyncSaveCallQueue.QueueCall( this, &CSaveRestore::AgeSaveList, CUtlEnvelope<const char *>(pSaveName), save_history_count.GetInt(), IsXSave() );
  616. }
  617. }
  618. S_ExtraUpdate();
  619. if (!SaveGameState( (pszDestMap != NULL ), NULL, false, ( bIsAutosave || bIsAutosaveDangerous ) ) )
  620. {
  621. m_szSaveGameName[ 0 ] = 0;
  622. return 0;
  623. }
  624. S_ExtraUpdate();
  625. //---------------------------------
  626. pSaveData = serverGameDLL->SaveInit( 0 );
  627. if ( !pSaveData )
  628. {
  629. m_szSaveGameName[ 0 ] = 0;
  630. return 0;
  631. }
  632. Q_FixSlashes( hlPath );
  633. Q_strncpy( gameHeader.comment, pSaveComment, sizeof( gameHeader.comment ) );
  634. if ( pszDestMap && pszLandmark && *pszDestMap && *pszLandmark )
  635. {
  636. Q_strncpy( gameHeader.mapName, pszDestMap, sizeof( gameHeader.mapName ) );
  637. Q_strncpy( gameHeader.originMapName, sv.GetMapName(), sizeof( gameHeader.originMapName ) );
  638. Q_strncpy( gameHeader.landmark, pszLandmark, sizeof( gameHeader.landmark ) );
  639. }
  640. else
  641. {
  642. Q_strncpy( gameHeader.mapName, sv.GetMapName(), sizeof( gameHeader.mapName ) );
  643. gameHeader.originMapName[0] = 0;
  644. gameHeader.landmark[0] = 0;
  645. }
  646. gameHeader.mapCount = 0; // No longer used. The map packer will place the map count at the head of the compound files (toml 7/18/2007)
  647. serverGameDLL->SaveWriteFields( pSaveData, "GameHeader", &gameHeader, NULL, GAME_HEADER::m_DataMap.dataDesc, GAME_HEADER::m_DataMap.dataNumFields );
  648. serverGameDLL->SaveGlobalState( pSaveData );
  649. // Write entity string token table
  650. pTokenData = pSaveData->AccessCurPos();
  651. for( i = 0; i < pSaveData->SizeSymbolTable(); i++ )
  652. {
  653. const char *pszToken = (pSaveData->StringFromSymbol( i )) ? pSaveData->StringFromSymbol( i ) : "";
  654. if ( !pSaveData->Write( pszToken, strlen(pszToken) + 1 ) )
  655. {
  656. ConMsg( "Token Table Save/Restore overflow!" );
  657. break;
  658. }
  659. }
  660. tokenSize = pSaveData->AccessCurPos() - pTokenData;
  661. pSaveData->Rewind( tokenSize );
  662. // open the file to validate it exists, and to clear it
  663. if ( bClearFile && !IsX360() )
  664. {
  665. FileHandle_t pSaveFile = g_pSaveRestoreFileSystem->Open( name, "wb" );
  666. if (!pSaveFile && g_pFileSystem->FileExists( name, "GAME" ) )
  667. {
  668. Msg("Save failed: invalid file name '%s'\n", pSaveName);
  669. m_szSaveGameName[ 0 ] = 0;
  670. return 0;
  671. }
  672. if ( pSaveFile )
  673. g_pSaveRestoreFileSystem->Close( pSaveFile );
  674. S_ExtraUpdate();
  675. }
  676. // If this isn't a dangerous auto save use it next
  677. if ( bSetMostRecent )
  678. {
  679. SetMostRecentSaveGame( pSaveName );
  680. }
  681. m_bWaitingForSafeDangerousSave = bIsAutosaveDangerous;
  682. int iHeaderBufferSize = 64 + tokenSize + pSaveData->GetCurPos();
  683. void *pMem = malloc(iHeaderBufferSize);
  684. CUtlBuffer saveHeader( pMem, iHeaderBufferSize );
  685. // Write the header -- THIS SHOULD NEVER CHANGE STRUCTURE, USE SAVE_HEADER FOR NEW HEADER INFORMATION
  686. // THIS IS ONLY HERE TO IDENTIFY THE FILE AND GET IT'S SIZE.
  687. tag = MAKEID('J','S','A','V');
  688. saveHeader.Put( &tag, sizeof(int) );
  689. tag = SAVEGAME_VERSION;
  690. saveHeader.Put( &tag, sizeof(int) );
  691. tag = pSaveData->GetCurPos();
  692. saveHeader.Put( &tag, sizeof(int) ); // Does not include token table
  693. // Write out the tokens first so we can load them before we load the entities
  694. tag = pSaveData->SizeSymbolTable();
  695. saveHeader.Put( &tag, sizeof(int) );
  696. saveHeader.Put( &tokenSize, sizeof(int) );
  697. saveHeader.Put( pTokenData, tokenSize );
  698. saveHeader.Put( pSaveData->GetBuffer(), pSaveData->GetCurPos() );
  699. // Create the save game container before the directory copy
  700. g_AsyncSaveCallQueue.QueueCall( g_pSaveRestoreFileSystem, &ISaveRestoreFileSystem::AsyncWrite, CUtlEnvelope<const char *>(name), saveHeader.Base(), saveHeader.TellPut(), true, false, (FSAsyncControl_t *) NULL );
  701. g_AsyncSaveCallQueue.QueueCall( this, &CSaveRestore::DirectoryCopy, CUtlEnvelope<const char *>(hlPath), CUtlEnvelope<const char *>(name), m_bIsXSave );
  702. // Finish all writes and close the save game container
  703. // @TODO: this async finish all writes has to go away, very expensive and will make game hitchy. switch to a wait on the last async op
  704. g_AsyncSaveCallQueue.QueueCall( g_pFileSystem, &IFileSystem::AsyncFinishAllWrites );
  705. if ( IsXSave() && StorageDeviceValid() )
  706. {
  707. // Finish all pending I/O to the storage devices
  708. g_AsyncSaveCallQueue.QueueCall( g_pXboxSystem, &IXboxSystem::FinishContainerWrites );
  709. }
  710. S_ExtraUpdate();
  711. Finish( pSaveData );
  712. S_ExtraUpdate();
  713. // queue up to save a matching screenshot
  714. if ( !IsX360() && save_screenshot.GetBool() ) // X360TBD: Faster savegame screenshots
  715. {
  716. if ( !( bIsAutosave || bIsAutosaveDangerous ) || save_screenshot.GetInt() == 2 )
  717. {
  718. Q_snprintf( m_szSaveGameScreenshotFile, sizeof( m_szSaveGameScreenshotFile ), "%s%s%s.tga", GetSaveDir(), pSaveName, GetPlatformExt() );
  719. }
  720. }
  721. S_ExtraUpdate();
  722. DispatchAsyncSave();
  723. m_szSaveGameName[ 0 ] = 0;
  724. return 1;
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose: Saves a screenshot for save game if necessary
  728. //-----------------------------------------------------------------------------
  729. void CSaveRestore::UpdateSaveGameScreenshots()
  730. {
  731. if ( IsPC() && g_LostVideoMemory )
  732. return;
  733. #ifndef SWDS
  734. if ( m_szSaveGameScreenshotFile[0] )
  735. {
  736. host_framecount++;
  737. g_ClientGlobalVariables.framecount = host_framecount;
  738. g_ClientDLL->WriteSaveGameScreenshot( m_szSaveGameScreenshotFile );
  739. m_szSaveGameScreenshotFile[0] = 0;
  740. }
  741. #endif
  742. }
  743. //-----------------------------------------------------------------------------
  744. // Purpose:
  745. //-----------------------------------------------------------------------------
  746. int CSaveRestore::SaveReadHeader( FileHandle_t pFile, GAME_HEADER *pHeader, int readGlobalState, bool *pbOldSave )
  747. {
  748. int i, tag, size, tokenCount, tokenSize;
  749. char *pszTokenList;
  750. CSaveRestoreData *pSaveData = NULL;
  751. if( g_pSaveRestoreFileSystem->Read( &tag, sizeof(int), pFile ) != sizeof(int) )
  752. return 0;
  753. if ( tag != MAKEID('J','S','A','V') )
  754. {
  755. Warning( "Can't load saved game, incorrect FILEID\n" );
  756. return 0;
  757. }
  758. if ( g_pSaveRestoreFileSystem->Read( &tag, sizeof(int), pFile ) != sizeof(int) )
  759. return 0;
  760. if ( tag != SAVEGAME_VERSION ) // Enforce version for now
  761. {
  762. Warning( "Can't load saved game, incorrect version (got %i expecting %i)\n", tag, SAVEGAME_VERSION );
  763. return 0;
  764. }
  765. if ( g_pSaveRestoreFileSystem->Read( &size, sizeof(int), pFile ) != sizeof(int) )
  766. return 0;
  767. if ( g_pSaveRestoreFileSystem->Read( &tokenCount, sizeof(int), pFile ) != sizeof(int) )
  768. return 0;
  769. if ( g_pSaveRestoreFileSystem->Read( &tokenSize, sizeof(int), pFile ) != sizeof(int) )
  770. return 0;
  771. // At this point we must clean this data up if we fail!
  772. void *pSaveMemory = SaveAllocMemory( sizeof(CSaveRestoreData) + tokenSize + size, sizeof(char) );
  773. if ( !pSaveMemory )
  774. {
  775. return 0;
  776. }
  777. pSaveData = MakeSaveRestoreData( pSaveMemory );
  778. pSaveData->levelInfo.connectionCount = 0;
  779. pszTokenList = (char *)(pSaveData + 1);
  780. if ( tokenSize > 0 )
  781. {
  782. if ( g_pSaveRestoreFileSystem->Read( pszTokenList, tokenSize, pFile ) != tokenSize )
  783. {
  784. Finish( pSaveData );
  785. return 0;
  786. }
  787. pSaveMemory = SaveAllocMemory( tokenCount, sizeof(char *), true );
  788. if ( !pSaveMemory )
  789. {
  790. Finish( pSaveData );
  791. return 0;
  792. }
  793. pSaveData->InitSymbolTable( (char**)pSaveMemory, tokenCount );
  794. // Make sure the token strings pointed to by the pToken hashtable.
  795. for( i=0; i<tokenCount; i++ )
  796. {
  797. if ( *pszTokenList )
  798. {
  799. Verify( pSaveData->DefineSymbol( pszTokenList, i ) );
  800. }
  801. while( *pszTokenList++ ); // Find next token (after next null)
  802. }
  803. }
  804. else
  805. {
  806. pSaveData->InitSymbolTable( NULL, 0 );
  807. }
  808. pSaveData->levelInfo.fUseLandmark = false;
  809. pSaveData->levelInfo.time = 0;
  810. // pszTokenList now points after token data
  811. pSaveData->Init( pszTokenList, size );
  812. if ( g_pSaveRestoreFileSystem->Read( pSaveData->GetBuffer(), size, pFile ) != size )
  813. {
  814. Finish( pSaveData );
  815. return 0;
  816. }
  817. serverGameDLL->SaveReadFields( pSaveData, "GameHeader", pHeader, NULL, GAME_HEADER::m_DataMap.dataDesc, GAME_HEADER::m_DataMap.dataNumFields );
  818. if ( g_szMapLoadOverride[0] )
  819. {
  820. V_strncpy( pHeader->mapName, g_szMapLoadOverride, sizeof( pHeader->mapName ) );
  821. g_szMapLoadOverride[0] = 0;
  822. }
  823. if ( pHeader->mapCount != 0 && pbOldSave)
  824. *pbOldSave = true;
  825. if ( readGlobalState && pHeader->mapCount == 0 ) // Alfred: Only load save games from the OB era engine where mapCount is forced to zero
  826. {
  827. serverGameDLL->RestoreGlobalState( pSaveData );
  828. }
  829. Finish( pSaveData );
  830. if ( pHeader->mapCount == 0 )
  831. {
  832. if ( g_pSaveRestoreFileSystem->Read( &pHeader->mapCount, sizeof(pHeader->mapCount), pFile ) != sizeof(pHeader->mapCount) )
  833. return 0;
  834. }
  835. return 1;
  836. }
  837. //-----------------------------------------------------------------------------
  838. // Purpose:
  839. // Input : *pName -
  840. // *output -
  841. // Output : Returns true on success, false on failure.
  842. //-----------------------------------------------------------------------------
  843. bool CSaveRestore::CalcSaveGameName( const char *pName, char *output, int outputStringLength )
  844. {
  845. if (!pName || !pName[0])
  846. return false;
  847. if ( IsXSave() )
  848. {
  849. Q_snprintf( output, outputStringLength, "%s:/%s", GetCurrentMod(), pName );
  850. }
  851. else
  852. {
  853. Q_snprintf( output, outputStringLength, "%s%s", GetSaveDir(), pName );
  854. }
  855. Q_DefaultExtension( output, IsX360() ? ".360.sav" : ".sav", outputStringLength );
  856. Q_FixSlashes( output );
  857. return true;
  858. }
  859. //-----------------------------------------------------------------------------
  860. // Does this save file exist?
  861. //-----------------------------------------------------------------------------
  862. bool CSaveRestore::SaveFileExists( const char *pName )
  863. {
  864. FinishAsyncSave();
  865. char name[256];
  866. if ( !CalcSaveGameName( pName, name, sizeof( name ) ) )
  867. return false;
  868. bool bExists = false;
  869. if ( IsXSave() )
  870. {
  871. if ( StorageDeviceValid() )
  872. {
  873. bExists = g_pFileSystem->FileExists( name );
  874. }
  875. else
  876. {
  877. bExists = g_pSaveRestoreFileSystem->FileExists( name );
  878. }
  879. }
  880. else
  881. {
  882. bExists = g_pFileSystem->FileExists( name );
  883. }
  884. return bExists;
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Purpose:
  888. // Input : *pName -
  889. // Output : int
  890. //-----------------------------------------------------------------------------
  891. bool CL_HL2Demo_MapCheck( const char *name ); // in host_cmd.cpp
  892. bool CL_PortalDemo_MapCheck( const char *name ); // in host_cmd.cpp
  893. bool CSaveRestore::LoadGame( const char *pName )
  894. {
  895. FileHandle_t pFile;
  896. GAME_HEADER gameHeader;
  897. char name[ MAX_PATH ];
  898. bool validload = false;
  899. FinishAsyncSave();
  900. SaveResetMemory();
  901. if ( !CalcSaveGameName( pName, name, sizeof( name ) ) )
  902. {
  903. DevWarning("Loaded bad game %s\n", pName);
  904. Assert(0);
  905. return false;
  906. }
  907. // store off the most recent save
  908. SetMostRecentSaveGame( pName );
  909. ConMsg( "Loading game from %s...\n", name );
  910. m_bClearSaveDir = false;
  911. DoClearSaveDir( IsXSave() );
  912. bool bLoadedToMemory = false;
  913. if ( IsX360() )
  914. {
  915. bool bValidStorageDevice = StorageDeviceValid();
  916. if ( bValidStorageDevice )
  917. {
  918. // Load the file into memory, whole hog
  919. bLoadedToMemory = g_pSaveRestoreFileSystem->LoadFileFromDisk( name );
  920. if ( bLoadedToMemory == false )
  921. return false;
  922. }
  923. }
  924. int iElapsedMinutes = 0;
  925. int iElapsedSeconds = 0;
  926. bool bOldSave = false;
  927. pFile = g_pSaveRestoreFileSystem->Open( name, "rb", MOD_DIR );
  928. if ( pFile )
  929. {
  930. char szDummyName[ MAX_PATH ];
  931. char szComment[ MAX_PATH ];
  932. char szElapsedTime[ MAX_PATH ];
  933. if ( SaveReadNameAndComment( pFile, szDummyName, sizeof(szDummyName), szComment, sizeof(szComment) ) )
  934. {
  935. // Elapsed time is the last 6 characters in comment. (mmm:ss)
  936. int i;
  937. i = strlen( szComment );
  938. Q_strncpy( szElapsedTime, "??", sizeof( szElapsedTime ) );
  939. if (i >= 6)
  940. {
  941. Q_strncpy( szElapsedTime, (char *)&szComment[i - 6], 7 );
  942. szElapsedTime[6] = '\0';
  943. // parse out
  944. iElapsedMinutes = atoi( szElapsedTime );
  945. iElapsedSeconds = atoi( szElapsedTime + 4);
  946. }
  947. }
  948. else
  949. {
  950. g_pSaveRestoreFileSystem->Close( pFile );
  951. if ( bLoadedToMemory )
  952. {
  953. g_pSaveRestoreFileSystem->RemoveFile( name );
  954. }
  955. return NULL;
  956. }
  957. // Reset the file pointer to the start of the file
  958. g_pSaveRestoreFileSystem->Seek( pFile, 0, FILESYSTEM_SEEK_HEAD );
  959. if ( SaveReadHeader( pFile, &gameHeader, 1, &bOldSave ) )
  960. {
  961. validload = DirectoryExtract( pFile, gameHeader.mapCount );
  962. }
  963. if ( !HaveExactMap( gameHeader.mapName ) )
  964. {
  965. Msg( "Map '%s' missing or invalid\n", gameHeader.mapName );
  966. validload = false;
  967. }
  968. g_pSaveRestoreFileSystem->Close( pFile );
  969. if ( bLoadedToMemory )
  970. {
  971. g_pSaveRestoreFileSystem->RemoveFile( name );
  972. }
  973. }
  974. else
  975. {
  976. ConMsg( "File not found or failed to open.\n" );
  977. return false;
  978. }
  979. if ( !validload )
  980. {
  981. Msg("Save file %s is not valid\n", name );
  982. return false;
  983. }
  984. // stop demo loop in case this fails
  985. cl.demonum = -1;
  986. deathmatch.SetValue( 0 );
  987. coop.SetValue( 0 );
  988. if ( !CL_HL2Demo_MapCheck( gameHeader.mapName ) )
  989. {
  990. Warning( "Save file %s is not valid\n", name );
  991. return false;
  992. }
  993. if ( !CL_PortalDemo_MapCheck( gameHeader.mapName ) )
  994. {
  995. Warning( "Save file %s is not valid\n", name );
  996. return false;
  997. }
  998. bool bIsTransitionSave = ( gameHeader.originMapName[0] != 0 );
  999. bool retval = Host_NewGame( gameHeader.mapName, true, false, ( bIsTransitionSave ) ? gameHeader.originMapName : NULL, ( bIsTransitionSave ) ? gameHeader.landmark : NULL, bOldSave );
  1000. SetMostRecentElapsedMinutes( iElapsedMinutes );
  1001. SetMostRecentElapsedSeconds( iElapsedSeconds );
  1002. return retval;
  1003. }
  1004. //-----------------------------------------------------------------------------
  1005. // Purpose: Remebers the most recent save game
  1006. //-----------------------------------------------------------------------------
  1007. void CSaveRestore::SetMostRecentSaveGame( const char *pSaveName )
  1008. {
  1009. // Only remember xsaves in the x360 case
  1010. if ( IsX360() && IsXSave() == false )
  1011. return;
  1012. if ( pSaveName )
  1013. {
  1014. Q_strncpy( m_szMostRecentSaveLoadGame, pSaveName, sizeof(m_szMostRecentSaveLoadGame) );
  1015. }
  1016. else
  1017. {
  1018. m_szMostRecentSaveLoadGame[0] = 0;
  1019. }
  1020. if ( !m_szMostRecentSaveLoadGame[0] )
  1021. {
  1022. DevWarning("Cleared most recent save!\n");
  1023. Assert(0);
  1024. }
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. // Purpose: Returns the last recored elapsed minutes
  1028. //-----------------------------------------------------------------------------
  1029. int CSaveRestore::GetMostRecentElapsedMinutes( void )
  1030. {
  1031. return m_MostRecentElapsedMinutes;
  1032. }
  1033. //-----------------------------------------------------------------------------
  1034. // Purpose: Returns the last recored elapsed seconds
  1035. //-----------------------------------------------------------------------------
  1036. int CSaveRestore::GetMostRecentElapsedSeconds( void )
  1037. {
  1038. return m_MostRecentElapsedSeconds;
  1039. }
  1040. int CSaveRestore::GetMostRecentElapsedTimeSet( void )
  1041. {
  1042. return m_MostRecentElapsedTimeSet;
  1043. }
  1044. //-----------------------------------------------------------------------------
  1045. // Purpose: Sets the last recored elapsed minutes
  1046. //-----------------------------------------------------------------------------
  1047. void CSaveRestore::SetMostRecentElapsedMinutes( const int min )
  1048. {
  1049. m_MostRecentElapsedMinutes = min;
  1050. m_MostRecentElapsedTimeSet = g_ServerGlobalVariables.curtime;
  1051. }
  1052. //-----------------------------------------------------------------------------
  1053. // Purpose: Sets the last recored elapsed seconds
  1054. //-----------------------------------------------------------------------------
  1055. void CSaveRestore::SetMostRecentElapsedSeconds( const int sec )
  1056. {
  1057. m_MostRecentElapsedSeconds = sec;
  1058. m_MostRecentElapsedTimeSet = g_ServerGlobalVariables.curtime;
  1059. }
  1060. //-----------------------------------------------------------------------------
  1061. // Purpose:
  1062. // Output : CSaveRestoreData
  1063. //-----------------------------------------------------------------------------
  1064. struct SaveFileHeaderTag_t
  1065. {
  1066. int id;
  1067. int version;
  1068. bool operator==(const SaveFileHeaderTag_t &rhs) const { return ( memcmp( this, &rhs, sizeof(SaveFileHeaderTag_t) ) == 0 ); }
  1069. bool operator!=(const SaveFileHeaderTag_t &rhs) const { return ( memcmp( this, &rhs, sizeof(SaveFileHeaderTag_t) ) != 0 ); }
  1070. };
  1071. #define MAKEID(d,c,b,a) ( ((int)(a) << 24) | ((int)(b) << 16) | ((int)(c) << 8) | ((int)(d)) )
  1072. const struct SaveFileHeaderTag_t CURRENT_SAVEFILE_HEADER_TAG = { MAKEID('V','A','L','V'), SAVEGAME_VERSION };
  1073. struct SaveFileSectionsInfo_t
  1074. {
  1075. int nBytesSymbols;
  1076. int nSymbols;
  1077. int nBytesDataHeaders;
  1078. int nBytesData;
  1079. int SumBytes() const
  1080. {
  1081. return ( nBytesSymbols + nBytesDataHeaders + nBytesData );
  1082. }
  1083. };
  1084. struct SaveFileSections_t
  1085. {
  1086. char *pSymbols;
  1087. char *pDataHeaders;
  1088. char *pData;
  1089. };
  1090. void CSaveRestore::SaveGameStateGlobals( CSaveRestoreData *pSaveData )
  1091. {
  1092. SAVE_HEADER header;
  1093. INetworkStringTable * table = sv.GetLightStyleTable();
  1094. Assert( table );
  1095. // Write global data
  1096. header.version = build_number( );
  1097. header.skillLevel = skill.GetInt(); // This is created from an int even though it's a float
  1098. header.connectionCount = pSaveData->levelInfo.connectionCount;
  1099. header.time__USE_VCR_MODE = sv.GetTime();
  1100. ConVarRef skyname( "sv_skyname" );
  1101. if ( skyname.IsValid() )
  1102. {
  1103. Q_strncpy( header.skyName, skyname.GetString(), sizeof( header.skyName ) );
  1104. }
  1105. else
  1106. {
  1107. Q_strncpy( header.skyName, "unknown", sizeof( header.skyName ) );
  1108. }
  1109. Q_strncpy( header.mapName, sv.GetMapName(), sizeof( header.mapName ) );
  1110. header.lightStyleCount = 0;
  1111. header.mapVersion = g_ServerGlobalVariables.mapversion;
  1112. int i;
  1113. for ( i = 0; i < MAX_LIGHTSTYLES; i++ )
  1114. {
  1115. const char * ligthStyle = (const char*) table->GetStringUserData( i, NULL );
  1116. if ( ligthStyle && ligthStyle[0] )
  1117. header.lightStyleCount++;
  1118. }
  1119. pSaveData->levelInfo.time = 0; // prohibits rebase of header.time (why not just save time as a field_float and ditch this hack?)
  1120. serverGameDLL->SaveWriteFields( pSaveData, "Save Header", &header, NULL, SAVE_HEADER::m_DataMap.dataDesc, SAVE_HEADER::m_DataMap.dataNumFields );
  1121. pSaveData->levelInfo.time = header.time__USE_VCR_MODE;
  1122. // Write adjacency list
  1123. for ( i = 0; i < pSaveData->levelInfo.connectionCount; i++ )
  1124. serverGameDLL->SaveWriteFields( pSaveData, "ADJACENCY", pSaveData->levelInfo.levelList + i, NULL, levellist_t::m_DataMap.dataDesc, levellist_t::m_DataMap.dataNumFields );
  1125. // Write the lightstyles
  1126. SAVELIGHTSTYLE light;
  1127. for ( i = 0; i < MAX_LIGHTSTYLES; i++ )
  1128. {
  1129. const char * ligthStyle = (const char*) table->GetStringUserData( i, NULL );
  1130. if ( ligthStyle && ligthStyle[0] )
  1131. {
  1132. light.index = i;
  1133. Q_strncpy( light.style, ligthStyle, sizeof( light.style ) );
  1134. serverGameDLL->SaveWriteFields( pSaveData, "LIGHTSTYLE", &light, NULL, SAVELIGHTSTYLE::m_DataMap.dataDesc, SAVELIGHTSTYLE::m_DataMap.dataNumFields );
  1135. }
  1136. }
  1137. }
  1138. CSaveRestoreData *CSaveRestore::SaveGameStateInit( void )
  1139. {
  1140. CSaveRestoreData *pSaveData = serverGameDLL->SaveInit( 0 );
  1141. return pSaveData;
  1142. }
  1143. bool CSaveRestore::SaveGameState( bool bTransition, CSaveRestoreData **ppReturnSaveData, bool bOpenContainer, bool bIsAutosaveOrDangerous )
  1144. {
  1145. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  1146. SaveMsg( "SaveGameState...\n" );
  1147. int i;
  1148. SaveFileSectionsInfo_t sectionsInfo;
  1149. SaveFileSections_t sections;
  1150. if ( ppReturnSaveData )
  1151. {
  1152. *ppReturnSaveData = NULL;
  1153. }
  1154. if ( bTransition )
  1155. {
  1156. if ( m_bClearSaveDir )
  1157. {
  1158. m_bClearSaveDir = false;
  1159. DoClearSaveDir( IsXSave() );
  1160. }
  1161. }
  1162. S_ExtraUpdate();
  1163. CSaveRestoreData *pSaveData = SaveGameStateInit();
  1164. if ( !pSaveData )
  1165. {
  1166. return false;
  1167. }
  1168. pSaveData->bAsync = bIsAutosaveOrDangerous;
  1169. //---------------------------------
  1170. // Save the data
  1171. sections.pData = pSaveData->AccessCurPos();
  1172. //---------------------------------
  1173. // Pre-save
  1174. serverGameDLL->PreSave( pSaveData );
  1175. // Build the adjacent map list (after entity table build by game in presave)
  1176. if ( bTransition )
  1177. {
  1178. serverGameDLL->BuildAdjacentMapList();
  1179. }
  1180. else
  1181. {
  1182. pSaveData->levelInfo.connectionCount = 0;
  1183. }
  1184. S_ExtraUpdate();
  1185. //---------------------------------
  1186. SaveGameStateGlobals( pSaveData );
  1187. S_ExtraUpdate();
  1188. serverGameDLL->Save( pSaveData );
  1189. S_ExtraUpdate();
  1190. sectionsInfo.nBytesData = pSaveData->AccessCurPos() - sections.pData;
  1191. //---------------------------------
  1192. // Save necessary tables/dictionaries/directories
  1193. sections.pDataHeaders = pSaveData->AccessCurPos();
  1194. serverGameDLL->WriteSaveHeaders( pSaveData );
  1195. sectionsInfo.nBytesDataHeaders = pSaveData->AccessCurPos() - sections.pDataHeaders;
  1196. //---------------------------------
  1197. // Write the save file symbol table
  1198. sections.pSymbols = pSaveData->AccessCurPos();
  1199. for( i = 0; i < pSaveData->SizeSymbolTable(); i++ )
  1200. {
  1201. const char *pszToken = ( pSaveData->StringFromSymbol( i ) ) ? pSaveData->StringFromSymbol( i ) : "";
  1202. if ( !pSaveData->Write( pszToken, strlen(pszToken) + 1 ) )
  1203. {
  1204. break;
  1205. }
  1206. }
  1207. sectionsInfo.nBytesSymbols = pSaveData->AccessCurPos() - sections.pSymbols;
  1208. sectionsInfo.nSymbols = pSaveData->SizeSymbolTable();
  1209. //---------------------------------
  1210. // Output to disk
  1211. char name[256];
  1212. int nBytesStateFile = sizeof(CURRENT_SAVEFILE_HEADER_TAG) +
  1213. sizeof(sectionsInfo) +
  1214. sectionsInfo.nBytesSymbols +
  1215. sectionsInfo.nBytesDataHeaders +
  1216. sectionsInfo.nBytesData;
  1217. void *pBuffer = new byte[nBytesStateFile];
  1218. CUtlBuffer buffer( pBuffer, nBytesStateFile );
  1219. // Write the header -- THIS SHOULD NEVER CHANGE STRUCTURE, USE SAVE_HEADER FOR NEW HEADER INFORMATION
  1220. // THIS IS ONLY HERE TO IDENTIFY THE FILE AND GET IT'S SIZE.
  1221. buffer.Put( &CURRENT_SAVEFILE_HEADER_TAG, sizeof(CURRENT_SAVEFILE_HEADER_TAG) );
  1222. // Write out the tokens and table FIRST so they are loaded in the right order, then write out the rest of the data in the file.
  1223. buffer.Put( &sectionsInfo, sizeof(sectionsInfo) );
  1224. buffer.Put( sections.pSymbols, sectionsInfo.nBytesSymbols );
  1225. buffer.Put( sections.pDataHeaders, sectionsInfo.nBytesDataHeaders );
  1226. buffer.Put( sections.pData, sectionsInfo.nBytesData );
  1227. if ( !IsXSave() )
  1228. {
  1229. Q_snprintf( name, 256, "//%s/%s%s.HL1", MOD_DIR, GetSaveDir(), GetSaveGameMapName( sv.GetMapName() ) ); // DON'T FixSlashes on this, it needs to be //MOD
  1230. SaveMsg( "Queue COM_CreatePath\n" );
  1231. g_AsyncSaveCallQueue.QueueCall( &COM_CreatePath, CUtlEnvelope<const char *>(name) );
  1232. }
  1233. else
  1234. {
  1235. Q_snprintf( name, 256, "%s:/%s.HL1", GetCurrentMod(), GetSaveGameMapName( sv.GetMapName() ) ); // DON'T FixSlashes on this, it needs to be //MOD
  1236. }
  1237. S_ExtraUpdate();
  1238. SaveMsg( "Queue AsyncWrite (%s)\n", name );
  1239. g_AsyncSaveCallQueue.QueueCall( g_pSaveRestoreFileSystem, &ISaveRestoreFileSystem::AsyncWrite, CUtlEnvelope<const char *>(name), pBuffer, nBytesStateFile, true, false, (FSAsyncControl_t *)NULL );
  1240. pBuffer = NULL;
  1241. //---------------------------------
  1242. EntityPatchWrite( pSaveData, GetSaveGameMapName( sv.GetMapName() ), true );
  1243. if ( !ppReturnSaveData )
  1244. {
  1245. Finish( pSaveData );
  1246. }
  1247. else
  1248. {
  1249. *ppReturnSaveData = pSaveData;
  1250. }
  1251. if ( !IsXSave() )
  1252. {
  1253. Q_snprintf(name, sizeof( name ), "//%s/%s%s.HL2", MOD_DIR, GetSaveDir(), GetSaveGameMapName( sv.GetMapName() ) );// DON'T FixSlashes on this, it needs to be //MOD
  1254. }
  1255. else
  1256. {
  1257. Q_snprintf(name, sizeof( name ), "%s:/%s.HL2", GetCurrentMod(), GetSaveGameMapName( sv.GetMapName() ) );// DON'T FixSlashes on this, it needs to be //MOD
  1258. }
  1259. // Let the client see the server entity to id lookup tables, etc.
  1260. S_ExtraUpdate();
  1261. bool bSuccess = SaveClientState( name );
  1262. S_ExtraUpdate();
  1263. //---------------------------------
  1264. if ( bTransition )
  1265. {
  1266. FinishAsyncSave();
  1267. }
  1268. S_ExtraUpdate();
  1269. return bSuccess;
  1270. }
  1271. //-----------------------------------------------------------------------------
  1272. // Purpose:
  1273. // Input : *save -
  1274. //-----------------------------------------------------------------------------
  1275. void CSaveRestore::Finish( CSaveRestoreData *save )
  1276. {
  1277. char **pTokens = save->DetachSymbolTable();
  1278. if ( pTokens )
  1279. SaveFreeMemory( pTokens );
  1280. entitytable_t *pEntityTable = save->DetachEntityTable();
  1281. if ( pEntityTable )
  1282. SaveFreeMemory( pEntityTable );
  1283. save->PurgeEntityHash();
  1284. SaveFreeMemory( save );
  1285. g_ServerGlobalVariables.pSaveData = NULL;
  1286. }
  1287. BEGIN_SIMPLE_DATADESC( musicsave_t )
  1288. DEFINE_ARRAY( songname, FIELD_CHARACTER, 128 ),
  1289. DEFINE_FIELD( sampleposition, FIELD_INTEGER ),
  1290. DEFINE_FIELD( master_volume, FIELD_SHORT ),
  1291. END_DATADESC()
  1292. BEGIN_SIMPLE_DATADESC( decallist_t )
  1293. DEFINE_FIELD( position, FIELD_POSITION_VECTOR ),
  1294. DEFINE_ARRAY( name, FIELD_CHARACTER, 128 ),
  1295. DEFINE_FIELD( entityIndex, FIELD_SHORT ),
  1296. // DEFINE_FIELD( depth, FIELD_CHARACTER ),
  1297. DEFINE_FIELD( flags, FIELD_CHARACTER ),
  1298. DEFINE_FIELD( impactPlaneNormal, FIELD_VECTOR ),
  1299. END_DATADESC()
  1300. struct baseclientsectionsold_t
  1301. {
  1302. int entitysize;
  1303. int headersize;
  1304. int decalsize;
  1305. int symbolsize;
  1306. int decalcount;
  1307. int symbolcount;
  1308. int SumBytes()
  1309. {
  1310. return entitysize + headersize + decalsize + symbolsize;
  1311. }
  1312. };
  1313. struct clientsectionsold_t : public baseclientsectionsold_t
  1314. {
  1315. char *symboldata;
  1316. char *entitydata;
  1317. char *headerdata;
  1318. char *decaldata;
  1319. };
  1320. // FIXME: Remove the above and replace with this once we update the save format!!
  1321. struct baseclientsections_t
  1322. {
  1323. int entitysize;
  1324. int headersize;
  1325. int decalsize;
  1326. int musicsize;
  1327. int symbolsize;
  1328. int decalcount;
  1329. int musiccount;
  1330. int symbolcount;
  1331. int SumBytes()
  1332. {
  1333. return entitysize + headersize + decalsize + symbolsize + musicsize;
  1334. }
  1335. };
  1336. struct clientsections_t : public baseclientsections_t
  1337. {
  1338. char *symboldata;
  1339. char *entitydata;
  1340. char *headerdata;
  1341. char *decaldata;
  1342. char *musicdata;
  1343. };
  1344. int CSaveRestore::LookupRestoreSpotSaveIndex( RestoreLookupTable *table, int save )
  1345. {
  1346. int c = table->lookup.Count();
  1347. for ( int i = 0; i < c; i++ )
  1348. {
  1349. SaveRestoreTranslate *slot = &table->lookup[ i ];
  1350. if ( slot->savedindex == save )
  1351. return slot->restoredindex;
  1352. }
  1353. return -1;
  1354. }
  1355. void CSaveRestore::ReapplyDecal( bool adjacent, RestoreLookupTable *table, decallist_t *entry )
  1356. {
  1357. int flags = entry->flags;
  1358. if ( adjacent )
  1359. {
  1360. flags |= FDECAL_DONTSAVE;
  1361. }
  1362. // unlock sting tables to allow changes, helps to find unwanted changes (bebug build only)
  1363. bool oldlock = networkStringTableContainerServer->Lock( false );
  1364. if ( adjacent )
  1365. {
  1366. // These entities might not exist over transitions, so we'll use the saved plane and do a traceline instead
  1367. Vector testspot = entry->position;
  1368. VectorMA( testspot, 5.0f, entry->impactPlaneNormal, testspot );
  1369. Vector testend = entry->position;
  1370. VectorMA( testend, -5.0f, entry->impactPlaneNormal, testend );
  1371. CTraceFilterHitAll traceFilter;
  1372. trace_t tr;
  1373. Ray_t ray;
  1374. ray.Init( testspot, testend );
  1375. g_pEngineTraceServer->TraceRay( ray, MASK_OPAQUE, &traceFilter, &tr );
  1376. if ( tr.fraction != 1.0f && !tr.allsolid )
  1377. {
  1378. // Check impact plane normal
  1379. float dot = entry->impactPlaneNormal.Dot( tr.plane.normal );
  1380. if ( dot >= 0.99 )
  1381. {
  1382. // Hack, have to use server traceline stuff to get at an actuall index here
  1383. edict_t *hit = tr.GetEdict();
  1384. if ( hit != NULL )
  1385. {
  1386. // Looks like a good match for original splat plane, reapply the decal
  1387. int entityToHit = NUM_FOR_EDICT( hit );
  1388. if ( entityToHit >= 0 )
  1389. {
  1390. IClientEntity *clientEntity = entitylist->GetClientEntity( entityToHit );
  1391. if ( !clientEntity )
  1392. return;
  1393. bool found = false;
  1394. int decalIndex = Draw_DecalIndexFromName( entry->name, &found );
  1395. if ( !found )
  1396. {
  1397. // This is a serious HACK because we're grabbing the index that the server hasn't networked down to us and forcing
  1398. // the decal name directly. However, the message should eventually arrive and set the decal back to the same
  1399. // name on the server's index...we can live with that I suppose.
  1400. decalIndex = sv.PrecacheDecal( entry->name, RES_FATALIFMISSING );
  1401. Draw_DecalSetName( decalIndex, entry->name );
  1402. }
  1403. g_pEfx->DecalShoot(
  1404. decalIndex,
  1405. entityToHit,
  1406. clientEntity->GetModel(),
  1407. clientEntity->GetAbsOrigin(),
  1408. clientEntity->GetAbsAngles(),
  1409. entry->position, 0, flags );
  1410. }
  1411. }
  1412. }
  1413. }
  1414. }
  1415. else
  1416. {
  1417. int entityToHit = entry->entityIndex != 0 ? LookupRestoreSpotSaveIndex( table, entry->entityIndex ) : entry->entityIndex;
  1418. if ( entityToHit >= 0 )
  1419. {
  1420. // NOTE: I re-initialized the origin and angles as the decals pos/angle are saved in local space (ie. relative to
  1421. // the entity they are attached to.
  1422. Vector vecOrigin( 0.0f, 0.0f, 0.0f );
  1423. QAngle vecAngle( 0.0f, 0.0f, 0.0f );
  1424. const model_t *pModel = NULL;
  1425. IClientEntity *clientEntity = entitylist->GetClientEntity( entityToHit );
  1426. if ( clientEntity )
  1427. {
  1428. pModel = clientEntity->GetModel();
  1429. }
  1430. else
  1431. {
  1432. // This breaks client/server. However, non-world entities are not in your PVS potentially.
  1433. edict_t *pEdict = EDICT_NUM( entityToHit );
  1434. if ( pEdict )
  1435. {
  1436. IServerEntity *pServerEntity = pEdict->GetIServerEntity();
  1437. if ( pServerEntity )
  1438. {
  1439. pModel = sv.GetModel( pServerEntity->GetModelIndex() );
  1440. }
  1441. }
  1442. }
  1443. if ( pModel )
  1444. {
  1445. bool found = false;
  1446. int decalIndex = Draw_DecalIndexFromName( entry->name, &found );
  1447. if ( !found )
  1448. {
  1449. // This is a serious HACK because we're grabbing the index that the server hasn't networked down to us and forcing
  1450. // the decal name directly. However, the message should eventually arrive and set the decal back to the same
  1451. // name on the server's index...we can live with that I suppose.
  1452. decalIndex = sv.PrecacheDecal( entry->name, RES_FATALIFMISSING );
  1453. Draw_DecalSetName( decalIndex, entry->name );
  1454. }
  1455. g_pEfx->DecalShoot( decalIndex, entityToHit, pModel, vecOrigin, vecAngle, entry->position, 0, flags );
  1456. }
  1457. }
  1458. }
  1459. // unlock sting tables to allow changes, helps to find unwanted changes (bebug build only)
  1460. networkStringTableContainerServer->Lock( oldlock );
  1461. }
  1462. void CSaveRestore::RestoreClientState( char const *fileName, bool adjacent )
  1463. {
  1464. FileHandle_t pFile;
  1465. pFile = g_pSaveRestoreFileSystem->Open( fileName, "rb" );
  1466. if ( !pFile )
  1467. {
  1468. DevMsg( "Failed to open client state file %s\n", fileName );
  1469. return;
  1470. }
  1471. SaveFileHeaderTag_t tag;
  1472. g_pSaveRestoreFileSystem->Read( &tag, sizeof(tag), pFile );
  1473. if ( tag != CURRENT_SAVEFILE_HEADER_TAG )
  1474. {
  1475. g_pSaveRestoreFileSystem->Close( pFile );
  1476. return;
  1477. }
  1478. // Check for magic number
  1479. int savePos = g_pSaveRestoreFileSystem->Tell( pFile );
  1480. int sectionheaderversion = 1;
  1481. int magicnumber = 0;
  1482. baseclientsections_t sections;
  1483. g_pSaveRestoreFileSystem->Read( &magicnumber, sizeof( magicnumber ), pFile );
  1484. if ( magicnumber == SECTION_MAGIC_NUMBER )
  1485. {
  1486. g_pSaveRestoreFileSystem->Read( &sectionheaderversion, sizeof( sectionheaderversion ), pFile );
  1487. if ( sectionheaderversion != SECTION_VERSION_NUMBER )
  1488. {
  1489. g_pSaveRestoreFileSystem->Close( pFile );
  1490. return;
  1491. }
  1492. g_pSaveRestoreFileSystem->Read( &sections, sizeof(baseclientsections_t), pFile );
  1493. }
  1494. else
  1495. {
  1496. // Rewind
  1497. g_pSaveRestoreFileSystem->Seek( pFile, savePos, FILESYSTEM_SEEK_HEAD );
  1498. baseclientsectionsold_t oldsections;
  1499. g_pSaveRestoreFileSystem->Read( &oldsections, sizeof(baseclientsectionsold_t), pFile );
  1500. Q_memset( &sections, 0, sizeof( sections ) );
  1501. sections.entitysize = oldsections.entitysize;
  1502. sections.headersize = oldsections.headersize;
  1503. sections.decalsize = oldsections.decalsize;
  1504. sections.symbolsize = oldsections.symbolsize;
  1505. sections.decalcount = oldsections.decalcount;
  1506. sections.symbolcount = oldsections.symbolcount;
  1507. }
  1508. void *pSaveMemory = SaveAllocMemory( sizeof(CSaveRestoreData) + sections.SumBytes(), sizeof(char) );
  1509. if ( !pSaveMemory )
  1510. {
  1511. return;
  1512. }
  1513. CSaveRestoreData *pSaveData = MakeSaveRestoreData( pSaveMemory );
  1514. // Needed?
  1515. Q_strncpy( pSaveData->levelInfo.szCurrentMapName, fileName, sizeof( pSaveData->levelInfo.szCurrentMapName ) );
  1516. g_pSaveRestoreFileSystem->Read( (char *)(pSaveData + 1), sections.SumBytes(), pFile );
  1517. g_pSaveRestoreFileSystem->Close( pFile );
  1518. char *pszTokenList = (char *)(pSaveData + 1);
  1519. if ( sections.symbolsize > 0 )
  1520. {
  1521. pSaveMemory = SaveAllocMemory( sections.symbolcount, sizeof(char *), true );
  1522. if ( !pSaveMemory )
  1523. {
  1524. SaveFreeMemory( pSaveData );
  1525. return;
  1526. }
  1527. pSaveData->InitSymbolTable( (char**)pSaveMemory, sections.symbolcount );
  1528. // Make sure the token strings pointed to by the pToken hashtable.
  1529. for( int i=0; i<sections.symbolcount; i++ )
  1530. {
  1531. if ( *pszTokenList )
  1532. {
  1533. Verify( pSaveData->DefineSymbol( pszTokenList, i ) );
  1534. }
  1535. while( *pszTokenList++ ); // Find next token (after next null)
  1536. }
  1537. }
  1538. else
  1539. {
  1540. pSaveData->InitSymbolTable( NULL, 0 );
  1541. }
  1542. Assert( pszTokenList - (char *)(pSaveData + 1) == sections.symbolsize );
  1543. //---------------------------------
  1544. // Set up the restore basis
  1545. int size = sections.SumBytes() - sections.symbolsize;
  1546. pSaveData->Init( (char *)(pszTokenList), size ); // The point pszTokenList was incremented to the end of the tokens
  1547. g_ClientDLL->ReadRestoreHeaders( pSaveData );
  1548. pSaveData->Rebase();
  1549. //HACKHACK
  1550. pSaveData->levelInfo.time = m_flClientSaveRestoreTime;
  1551. char name[256];
  1552. Q_FileBase( fileName, name, sizeof( name ) );
  1553. Q_strlower( name );
  1554. RestoreLookupTable *table = FindOrAddRestoreLookupTable( name );
  1555. pSaveData->levelInfo.fUseLandmark = adjacent;
  1556. if ( adjacent )
  1557. {
  1558. pSaveData->levelInfo.vecLandmarkOffset = table->m_vecLandMarkOffset;
  1559. }
  1560. bool bFixTable = false;
  1561. // Fixup restore indices based on what server re-created for us
  1562. int c = pSaveData->NumEntities();
  1563. for ( int i = 0 ; i < c; i++ )
  1564. {
  1565. entitytable_t *entry = pSaveData->GetEntityInfo( i );
  1566. entry->restoreentityindex = LookupRestoreSpotSaveIndex( table, entry->saveentityindex );
  1567. //Adrian: This means we are a client entity with no index to restore and we need our model precached.
  1568. if ( entry->restoreentityindex == -1 && entry->classname != NULL_STRING && entry->modelname != NULL_STRING )
  1569. {
  1570. sv.PrecacheModel( STRING( entry->modelname ), RES_FATALIFMISSING | RES_PRELOAD );
  1571. bFixTable = true;
  1572. }
  1573. }
  1574. //Adrian: Fix up model string tables to make sure they match on both sides.
  1575. if ( bFixTable == true )
  1576. {
  1577. int iCount = cl.m_pModelPrecacheTable->GetNumStrings();
  1578. while ( iCount < sv.GetModelPrecacheTable()->GetNumStrings() )
  1579. {
  1580. string_t szString = MAKE_STRING( sv.GetModelPrecacheTable()->GetString( iCount ) );
  1581. cl.m_pModelPrecacheTable->AddString( true, STRING( szString ) );
  1582. iCount++;
  1583. }
  1584. }
  1585. g_ClientDLL->Restore( pSaveData, false );
  1586. if ( r_decals.GetInt() )
  1587. {
  1588. for ( int i = 0; i < sections.decalcount; i++ )
  1589. {
  1590. decallist_t entry;
  1591. g_ClientDLL->SaveReadFields( pSaveData, "DECALLIST", &entry, NULL, decallist_t::m_DataMap.dataDesc, decallist_t::m_DataMap.dataNumFields );
  1592. ReapplyDecal( adjacent, table, &entry );
  1593. }
  1594. }
  1595. for ( int i = 0; i < sections.musiccount; i++ )
  1596. {
  1597. musicsave_t song;
  1598. g_ClientDLL->SaveReadFields( pSaveData, "MUSICLIST", &song, NULL, musicsave_t::m_DataMap.dataDesc, musicsave_t::m_DataMap.dataNumFields );
  1599. // Tell sound system to restart the music
  1600. S_RestartSong( &song );
  1601. }
  1602. Finish( pSaveData );
  1603. }
  1604. void CSaveRestore::RestoreAdjacenClientState( char const *map )
  1605. {
  1606. char name[256];
  1607. if ( !IsXSave() )
  1608. {
  1609. Q_snprintf( name, sizeof( name ), "//%s/%s%s.HL2", MOD_DIR, GetSaveDir(), GetSaveGameMapName( map ) );// DON'T FixSlashes on this, it needs to be //MOD
  1610. }
  1611. else
  1612. {
  1613. Q_snprintf( name, sizeof( name ), "%s:/%s.HL2", GetCurrentMod(), GetSaveGameMapName( map ) );// DON'T FixSlashes on this, it needs to be //MOD
  1614. }
  1615. COM_CreatePath( name );
  1616. RestoreClientState( name, true );
  1617. }
  1618. //-----------------------------------------------------------------------------
  1619. // Purpose:
  1620. // Input : *name -
  1621. //-----------------------------------------------------------------------------
  1622. bool CSaveRestore::SaveClientState( const char *name )
  1623. {
  1624. #ifndef SWDS
  1625. decallist_t *decalList;
  1626. int i;
  1627. clientsections_t sections;
  1628. CSaveRestoreData *pSaveData = g_ClientDLL->SaveInit( 0 );
  1629. if ( !pSaveData )
  1630. {
  1631. return false;
  1632. }
  1633. sections.entitydata = pSaveData->AccessCurPos();
  1634. // Now write out the client .dll entities to the save file, too
  1635. g_ClientDLL->PreSave( pSaveData );
  1636. g_ClientDLL->Save( pSaveData );
  1637. sections.entitysize = pSaveData->AccessCurPos() - sections.entitydata;
  1638. sections.headerdata = pSaveData->AccessCurPos();
  1639. g_ClientDLL->WriteSaveHeaders( pSaveData );
  1640. sections.headersize = pSaveData->AccessCurPos() - sections.headerdata;
  1641. sections.decaldata = pSaveData->AccessCurPos();
  1642. decalList = (decallist_t*)malloc( sizeof(decallist_t) * Draw_DecalMax() );
  1643. sections.decalcount = DecalListCreate( decalList );
  1644. for ( i = 0; i < sections.decalcount; i++ )
  1645. {
  1646. decallist_t *entry = &decalList[ i ];
  1647. g_ClientDLL->SaveWriteFields( pSaveData, "DECALLIST", entry, NULL, decallist_t::m_DataMap.dataDesc, decallist_t::m_DataMap.dataNumFields );
  1648. }
  1649. sections.decalsize = pSaveData->AccessCurPos() - sections.decaldata;
  1650. sections.musicdata = pSaveData->AccessCurPos();
  1651. CUtlVector< musicsave_t > music;
  1652. // Ask sound system for current music tracks
  1653. S_GetCurrentlyPlayingMusic( music );
  1654. sections.musiccount = music.Count();
  1655. for ( i = 0; i < sections.musiccount; ++i )
  1656. {
  1657. musicsave_t *song = &music[ i ];
  1658. g_ClientDLL->SaveWriteFields( pSaveData, "MUSICLIST", song, NULL, musicsave_t::m_DataMap.dataDesc, musicsave_t::m_DataMap.dataNumFields );
  1659. }
  1660. sections.musicsize = pSaveData->AccessCurPos() - sections.musicdata;
  1661. // Write string token table
  1662. sections.symboldata = pSaveData->AccessCurPos();
  1663. for( i = 0; i < pSaveData->SizeSymbolTable(); i++ )
  1664. {
  1665. const char *pszToken = (pSaveData->StringFromSymbol( i )) ? pSaveData->StringFromSymbol( i ) : "";
  1666. if ( !pSaveData->Write( pszToken, strlen(pszToken) + 1 ) )
  1667. {
  1668. ConMsg( "Token Table Save/Restore overflow!" );
  1669. break;
  1670. }
  1671. }
  1672. sections.symbolcount = pSaveData->SizeSymbolTable();
  1673. sections.symbolsize = pSaveData->AccessCurPos() - sections.symboldata;
  1674. int magicnumber = SECTION_MAGIC_NUMBER;
  1675. int sectionheaderversion = SECTION_VERSION_NUMBER;
  1676. unsigned nBytes = sizeof(CURRENT_SAVEFILE_HEADER_TAG) +
  1677. sizeof( magicnumber ) +
  1678. sizeof( sectionheaderversion ) +
  1679. sizeof( baseclientsections_t ) +
  1680. sections.symbolsize +
  1681. sections.headersize +
  1682. sections.entitysize +
  1683. sections.decalsize +
  1684. sections.musicsize;
  1685. void *pBuffer = new byte[nBytes];
  1686. CUtlBuffer buffer( pBuffer, nBytes );
  1687. buffer.Put( &CURRENT_SAVEFILE_HEADER_TAG, sizeof(CURRENT_SAVEFILE_HEADER_TAG) );
  1688. buffer.Put( &magicnumber, sizeof( magicnumber ) );
  1689. buffer.Put( &sectionheaderversion, sizeof( sectionheaderversion ) );
  1690. buffer.Put( (baseclientsections_t * )&sections, sizeof( baseclientsections_t ) );
  1691. buffer.Put( sections.symboldata, sections.symbolsize );
  1692. buffer.Put( sections.headerdata, sections.headersize );
  1693. buffer.Put( sections.entitydata, sections.entitysize );
  1694. buffer.Put( sections.decaldata, sections.decalsize );
  1695. buffer.Put( sections.musicdata, sections.musicsize );
  1696. SaveMsg( "Queue AsyncWrite (%s)\n", name );
  1697. g_AsyncSaveCallQueue.QueueCall( g_pSaveRestoreFileSystem, &ISaveRestoreFileSystem::AsyncWrite, CUtlEnvelope<const char *>(name), pBuffer, nBytes, true, false, (FSAsyncControl_t *)NULL );
  1698. Finish( pSaveData );
  1699. free( decalList );
  1700. return true;
  1701. #endif
  1702. }
  1703. //-----------------------------------------------------------------------------
  1704. // Purpose: Parses and confirms save information. Pulled from PC UI
  1705. //-----------------------------------------------------------------------------
  1706. int CSaveRestore::SaveReadNameAndComment( FileHandle_t f, OUT_Z_CAP(nameSize) char *name, int nameSize, OUT_Z_CAP(commentSize) char *comment, int commentSize )
  1707. {
  1708. int i, tag, size, tokenSize, tokenCount;
  1709. char *pSaveData = NULL;
  1710. char *pFieldName = NULL;
  1711. char **pTokenList = NULL;
  1712. name[0] = '\0';
  1713. comment[0] = '\0';
  1714. // Make sure we can at least read in the first five fields
  1715. unsigned int tagsize = sizeof(int) * 5;
  1716. if ( g_pSaveRestoreFileSystem->Size( f ) < tagsize )
  1717. return 0;
  1718. int nRead = g_pSaveRestoreFileSystem->Read( &tag, sizeof(int), f );
  1719. if ( ( nRead != sizeof(int) ) || tag != MAKEID('J','S','A','V') )
  1720. return 0;
  1721. if ( g_pSaveRestoreFileSystem->Read( &tag, sizeof(int), f ) != sizeof(int) )
  1722. return 0;
  1723. if ( g_pSaveRestoreFileSystem->Read( &size, sizeof(int), f ) != sizeof(int) )
  1724. return 0;
  1725. if ( g_pSaveRestoreFileSystem->Read( &tokenCount, sizeof(int), f ) != sizeof(int) ) // These two ints are the token list
  1726. return 0;
  1727. if ( g_pSaveRestoreFileSystem->Read( &tokenSize, sizeof(int), f ) != sizeof(int) )
  1728. return 0;
  1729. size += tokenSize;
  1730. // Sanity Check.
  1731. if ( tokenCount < 0 || tokenCount > 1024 * 1024 * 32 )
  1732. {
  1733. return 0;
  1734. }
  1735. if ( tokenSize < 0 || tokenSize > 1024*1024*10 )
  1736. {
  1737. return 0;
  1738. }
  1739. pSaveData = (char *)new char[size];
  1740. if ( g_pSaveRestoreFileSystem->Read(pSaveData, size, f) != size )
  1741. {
  1742. delete[] pSaveData;
  1743. return 0;
  1744. }
  1745. int nNumberOfFields;
  1746. char *pData;
  1747. int nFieldSize;
  1748. pData = pSaveData;
  1749. // Allocate a table for the strings, and parse the table
  1750. if ( tokenSize > 0 )
  1751. {
  1752. pTokenList = new char *[tokenCount];
  1753. // Make sure the token strings pointed to by the pToken hashtable.
  1754. for( i=0; i<tokenCount; i++ )
  1755. {
  1756. pTokenList[i] = *pData ? pData : NULL; // Point to each string in the pToken table
  1757. while( *pData++ ); // Find next token (after next null)
  1758. }
  1759. }
  1760. else
  1761. pTokenList = NULL;
  1762. // short, short (size, index of field name)
  1763. nFieldSize = *(short *)pData;
  1764. pData += sizeof(short);
  1765. pFieldName = pTokenList[ *(short *)pData ];
  1766. if ( !pFieldName || Q_stricmp( pFieldName, "GameHeader" ) )
  1767. {
  1768. delete[] pSaveData;
  1769. delete[] pTokenList;
  1770. return 0;
  1771. };
  1772. // int (fieldcount)
  1773. pData += sizeof(short);
  1774. nNumberOfFields = *(int*)pData;
  1775. pData += nFieldSize;
  1776. // Each field is a short (size), short (index of name), binary string of "size" bytes (data)
  1777. for ( i = 0; i < nNumberOfFields; ++i )
  1778. {
  1779. // Data order is:
  1780. // Size
  1781. // szName
  1782. // Actual Data
  1783. nFieldSize = *(short *)pData;
  1784. pData += sizeof(short);
  1785. pFieldName = pTokenList[ *(short *)pData ];
  1786. pData += sizeof(short);
  1787. if ( !Q_stricmp( pFieldName, "comment" ) )
  1788. {
  1789. int copySize = MAX( commentSize, nFieldSize );
  1790. Q_strncpy( comment, pData, copySize );
  1791. }
  1792. else if ( !Q_stricmp( pFieldName, "mapName" ) )
  1793. {
  1794. int copySize = MAX( commentSize, nFieldSize );
  1795. Q_strncpy( name, pData, copySize );
  1796. };
  1797. // Move to Start of next field.
  1798. pData += nFieldSize;
  1799. }
  1800. // Delete the string table we allocated
  1801. delete[] pTokenList;
  1802. delete[] pSaveData;
  1803. if ( strlen( name ) > 0 && strlen( comment ) > 0 )
  1804. return 1;
  1805. return 0;
  1806. }
  1807. //-----------------------------------------------------------------------------
  1808. // Purpose:
  1809. // Input : *level -
  1810. // Output : CSaveRestoreData
  1811. //-----------------------------------------------------------------------------
  1812. CSaveRestoreData *CSaveRestore::LoadSaveData( const char *level )
  1813. {
  1814. char name[MAX_OSPATH];
  1815. FileHandle_t pFile;
  1816. if ( !IsXSave() )
  1817. {
  1818. Q_snprintf( name, sizeof( name ), "//%s/%s%s.HL1", MOD_DIR, GetSaveDir(), level);// DON'T FixSlashes on this, it needs to be //MOD
  1819. }
  1820. else
  1821. {
  1822. Q_snprintf( name, sizeof( name ), "%s:/%s.HL1", GetCurrentMod(), level);// DON'T FixSlashes on this, it needs to be //MOD
  1823. }
  1824. ConMsg ("Loading game from %s...\n", name);
  1825. pFile = g_pSaveRestoreFileSystem->Open( name, "rb" );
  1826. if (!pFile)
  1827. {
  1828. ConMsg ("ERROR: couldn't open.\n");
  1829. return NULL;
  1830. }
  1831. //---------------------------------
  1832. // Read the header
  1833. SaveFileHeaderTag_t tag;
  1834. if ( g_pSaveRestoreFileSystem->Read( &tag, sizeof(tag), pFile ) != sizeof(tag) )
  1835. return NULL;
  1836. // Is this a valid save?
  1837. if ( tag != CURRENT_SAVEFILE_HEADER_TAG )
  1838. return NULL;
  1839. //---------------------------------
  1840. // Read the sections info and the data
  1841. //
  1842. SaveFileSectionsInfo_t sectionsInfo;
  1843. if ( g_pSaveRestoreFileSystem->Read( &sectionsInfo, sizeof(sectionsInfo), pFile ) != sizeof(sectionsInfo) )
  1844. return NULL;
  1845. void *pSaveMemory = SaveAllocMemory( sizeof(CSaveRestoreData) + sectionsInfo.SumBytes(), sizeof(char) );
  1846. if ( !pSaveMemory )
  1847. {
  1848. return 0;
  1849. }
  1850. CSaveRestoreData *pSaveData = MakeSaveRestoreData( pSaveMemory );
  1851. Q_strncpy( pSaveData->levelInfo.szCurrentMapName, level, sizeof( pSaveData->levelInfo.szCurrentMapName ) );
  1852. if ( g_pSaveRestoreFileSystem->Read( (char *)(pSaveData + 1), sectionsInfo.SumBytes(), pFile ) != sectionsInfo.SumBytes() )
  1853. {
  1854. // Free the memory and give up
  1855. Finish( pSaveData );
  1856. return NULL;
  1857. }
  1858. g_pSaveRestoreFileSystem->Close( pFile );
  1859. //---------------------------------
  1860. // Parse the symbol table
  1861. char *pszTokenList = (char *)(pSaveData + 1);// Skip past the CSaveRestoreData structure
  1862. if ( sectionsInfo.nBytesSymbols > 0 )
  1863. {
  1864. pSaveMemory = SaveAllocMemory( sectionsInfo.nSymbols, sizeof(char *), true );
  1865. if ( !pSaveMemory )
  1866. {
  1867. SaveFreeMemory( pSaveData );
  1868. return 0;
  1869. }
  1870. pSaveData->InitSymbolTable( (char**)pSaveMemory, sectionsInfo.nSymbols );
  1871. // Make sure the token strings pointed to by the pToken hashtable.
  1872. for( int i = 0; i<sectionsInfo.nSymbols; i++ )
  1873. {
  1874. if ( *pszTokenList )
  1875. {
  1876. Verify( pSaveData->DefineSymbol( pszTokenList, i ) );
  1877. }
  1878. while( *pszTokenList++ ); // Find next token (after next null)
  1879. }
  1880. }
  1881. else
  1882. {
  1883. pSaveData->InitSymbolTable( NULL, 0 );
  1884. }
  1885. Assert( pszTokenList - (char *)(pSaveData + 1) == sectionsInfo.nBytesSymbols );
  1886. //---------------------------------
  1887. // Set up the restore basis
  1888. int size = sectionsInfo.SumBytes() - sectionsInfo.nBytesSymbols;
  1889. pSaveData->levelInfo.connectionCount = 0;
  1890. pSaveData->Init( (char *)(pszTokenList), size ); // The point pszTokenList was incremented to the end of the tokens
  1891. pSaveData->levelInfo.fUseLandmark = true;
  1892. pSaveData->levelInfo.time = 0;
  1893. VectorCopy( vec3_origin, pSaveData->levelInfo.vecLandmarkOffset );
  1894. g_ServerGlobalVariables.pSaveData = (CSaveRestoreData*)pSaveData;
  1895. return pSaveData;
  1896. }
  1897. //-----------------------------------------------------------------------------
  1898. // Purpose:
  1899. // Input : *pSaveData -
  1900. // *pHeader -
  1901. // updateGlobals -
  1902. //-----------------------------------------------------------------------------
  1903. void CSaveRestore::ParseSaveTables( CSaveRestoreData *pSaveData, SAVE_HEADER *pHeader, int updateGlobals )
  1904. {
  1905. int i;
  1906. SAVELIGHTSTYLE light;
  1907. INetworkStringTable * table = sv.GetLightStyleTable();
  1908. // Re-base the savedata since we re-ordered the entity/table / restore fields
  1909. pSaveData->Rebase();
  1910. // Process SAVE_HEADER
  1911. serverGameDLL->SaveReadFields( pSaveData, "Save Header", pHeader, NULL, SAVE_HEADER::m_DataMap.dataDesc, SAVE_HEADER::m_DataMap.dataNumFields );
  1912. // header.version = ENGINE_VERSION;
  1913. pSaveData->levelInfo.mapVersion = pHeader->mapVersion;
  1914. pSaveData->levelInfo.connectionCount = pHeader->connectionCount;
  1915. pSaveData->levelInfo.time = pHeader->time__USE_VCR_MODE;
  1916. pSaveData->levelInfo.fUseLandmark = true;
  1917. VectorCopy( vec3_origin, pSaveData->levelInfo.vecLandmarkOffset );
  1918. // Read adjacency list
  1919. for ( i = 0; i < pSaveData->levelInfo.connectionCount; i++ )
  1920. serverGameDLL->SaveReadFields( pSaveData, "ADJACENCY", pSaveData->levelInfo.levelList + i, NULL, levellist_t::m_DataMap.dataDesc, levellist_t::m_DataMap.dataNumFields );
  1921. if ( updateGlobals )
  1922. {
  1923. for ( i = 0; i < MAX_LIGHTSTYLES; i++ )
  1924. table->SetStringUserData( i, 1, "" );
  1925. }
  1926. for ( i = 0; i < pHeader->lightStyleCount; i++ )
  1927. {
  1928. serverGameDLL->SaveReadFields( pSaveData, "LIGHTSTYLE", &light, NULL, SAVELIGHTSTYLE::m_DataMap.dataDesc, SAVELIGHTSTYLE::m_DataMap.dataNumFields );
  1929. if ( updateGlobals )
  1930. {
  1931. table->SetStringUserData( light.index, Q_strlen(light.style)+1, light.style );
  1932. }
  1933. }
  1934. }
  1935. //-----------------------------------------------------------------------------
  1936. // Purpose: Write out the list of entities that are no longer in the save file for this level
  1937. // (they've been moved to another level)
  1938. // Input : *pSaveData -
  1939. // *level -
  1940. //-----------------------------------------------------------------------------
  1941. void CSaveRestore::EntityPatchWrite( CSaveRestoreData *pSaveData, const char *level, bool bAsync )
  1942. {
  1943. char name[MAX_OSPATH];
  1944. int i, size;
  1945. if ( !IsXSave() )
  1946. {
  1947. Q_snprintf( name, sizeof( name ), "//%s/%s%s.HL3", MOD_DIR, GetSaveDir(), level);// DON'T FixSlashes on this, it needs to be //MOD
  1948. }
  1949. else
  1950. {
  1951. Q_snprintf( name, sizeof( name ), "%s:/%s.HL3", GetCurrentMod(), level);// DON'T FixSlashes on this, it needs to be //MOD
  1952. }
  1953. size = 0;
  1954. for ( i = 0; i < pSaveData->NumEntities(); i++ )
  1955. {
  1956. if ( pSaveData->GetEntityInfo(i)->flags & FENTTABLE_REMOVED )
  1957. size++;
  1958. }
  1959. int nBytesEntityPatch = sizeof(int) + size * sizeof(int);
  1960. void *pBuffer = new byte[nBytesEntityPatch];
  1961. CUtlBuffer buffer( pBuffer, nBytesEntityPatch );
  1962. // Patch count
  1963. buffer.Put( &size, sizeof(int) );
  1964. for ( i = 0; i < pSaveData->NumEntities(); i++ )
  1965. {
  1966. if ( pSaveData->GetEntityInfo(i)->flags & FENTTABLE_REMOVED )
  1967. buffer.Put( &i, sizeof(int) );
  1968. }
  1969. if ( !bAsync )
  1970. {
  1971. g_pSaveRestoreFileSystem->AsyncWrite( name, pBuffer, nBytesEntityPatch, true, false );
  1972. g_pSaveRestoreFileSystem->AsyncFinishAllWrites();
  1973. }
  1974. else
  1975. {
  1976. SaveMsg( "Queue AsyncWrite (%s)\n", name );
  1977. g_AsyncSaveCallQueue.QueueCall( g_pSaveRestoreFileSystem, &ISaveRestoreFileSystem::AsyncWrite, CUtlEnvelope<const char *>(name), pBuffer, nBytesEntityPatch, true, false, (FSAsyncControl_t *)NULL );
  1978. }
  1979. }
  1980. //-----------------------------------------------------------------------------
  1981. // Purpose: Read the list of entities that are no longer in the save file for this level (they've been moved to another level)
  1982. // and correct the table
  1983. // Input : *pSaveData -
  1984. // *level -
  1985. //-----------------------------------------------------------------------------
  1986. void CSaveRestore::EntityPatchRead( CSaveRestoreData *pSaveData, const char *level )
  1987. {
  1988. char name[MAX_OSPATH];
  1989. FileHandle_t pFile;
  1990. int i, size, entityId;
  1991. if ( !IsXSave() )
  1992. {
  1993. Q_snprintf(name, sizeof( name ), "//%s/%s%s.HL3", MOD_DIR, GetSaveDir(), GetSaveGameMapName( level ) );// DON'T FixSlashes on this, it needs to be //MOD
  1994. }
  1995. else
  1996. {
  1997. Q_snprintf(name, sizeof( name ), "%s:/%s.HL3", GetCurrentMod(), GetSaveGameMapName( level ) );// DON'T FixSlashes on this, it needs to be //MOD
  1998. }
  1999. pFile = g_pSaveRestoreFileSystem->Open( name, "rb" );
  2000. if ( pFile )
  2001. {
  2002. // Patch count
  2003. g_pSaveRestoreFileSystem->Read( &size, sizeof(int), pFile );
  2004. for ( i = 0; i < size; i++ )
  2005. {
  2006. g_pSaveRestoreFileSystem->Read( &entityId, sizeof(int), pFile );
  2007. pSaveData->GetEntityInfo(entityId)->flags = FENTTABLE_REMOVED;
  2008. }
  2009. g_pSaveRestoreFileSystem->Close( pFile );
  2010. }
  2011. }
  2012. //-----------------------------------------------------------------------------
  2013. // Purpose:
  2014. // Input : *level -
  2015. // createPlayers -
  2016. // Output : int
  2017. //-----------------------------------------------------------------------------
  2018. int CSaveRestore::LoadGameState( char const *level, bool createPlayers )
  2019. {
  2020. VPROF("CSaveRestore::LoadGameState");
  2021. SAVE_HEADER header;
  2022. CSaveRestoreData *pSaveData;
  2023. pSaveData = LoadSaveData( GetSaveGameMapName( level ) );
  2024. if ( !pSaveData ) // Couldn't load the file
  2025. return 0;
  2026. serverGameDLL->ReadRestoreHeaders( pSaveData );
  2027. ParseSaveTables( pSaveData, &header, 1 );
  2028. EntityPatchRead( pSaveData, level );
  2029. if ( !IsX360() )
  2030. {
  2031. skill.SetValue( header.skillLevel );
  2032. }
  2033. Q_strncpy( sv.m_szMapname, header.mapName, sizeof( sv.m_szMapname ) );
  2034. ConVarRef skyname( "sv_skyname" );
  2035. if ( skyname.IsValid() )
  2036. {
  2037. skyname.SetValue( header.skyName );
  2038. }
  2039. // Create entity list
  2040. serverGameDLL->Restore( pSaveData, createPlayers );
  2041. BuildRestoredIndexTranslationTable( level, pSaveData, false );
  2042. m_flClientSaveRestoreTime = pSaveData->levelInfo.time;
  2043. Finish( pSaveData );
  2044. sv.m_nTickCount = (int)( header.time__USE_VCR_MODE / host_state.interval_per_tick );
  2045. // SUCCESS!
  2046. return 1;
  2047. }
  2048. CSaveRestore::RestoreLookupTable *CSaveRestore::FindOrAddRestoreLookupTable( char const *mapname )
  2049. {
  2050. int idx = m_RestoreLookup.Find( mapname );
  2051. if ( idx == m_RestoreLookup.InvalidIndex() )
  2052. {
  2053. idx = m_RestoreLookup.Insert( mapname );
  2054. }
  2055. return &m_RestoreLookup[ idx ];
  2056. }
  2057. //-----------------------------------------------------------------------------
  2058. // Purpose:
  2059. // Input : *pSaveData -
  2060. // Output : int
  2061. //-----------------------------------------------------------------------------
  2062. void CSaveRestore::BuildRestoredIndexTranslationTable( char const *mapname, CSaveRestoreData *pSaveData, bool verbose )
  2063. {
  2064. char name[ 256 ];
  2065. Q_FileBase( mapname, name, sizeof( name ) );
  2066. Q_strlower( name );
  2067. // Build Translation Lookup
  2068. RestoreLookupTable *table = FindOrAddRestoreLookupTable( name );
  2069. table->Clear();
  2070. int c = pSaveData->NumEntities();
  2071. for ( int i = 0; i < c; i++ )
  2072. {
  2073. entitytable_t *entry = pSaveData->GetEntityInfo( i );
  2074. SaveRestoreTranslate slot;
  2075. slot.classname = entry->classname;
  2076. slot.savedindex = entry->saveentityindex;
  2077. slot.restoredindex = entry->restoreentityindex;
  2078. table->lookup.AddToTail( slot );
  2079. }
  2080. table->m_vecLandMarkOffset = pSaveData->levelInfo.vecLandmarkOffset;
  2081. }
  2082. void CSaveRestore::ClearRestoredIndexTranslationTables()
  2083. {
  2084. m_RestoreLookup.RemoveAll();
  2085. }
  2086. //-----------------------------------------------------------------------------
  2087. // Purpose: Find all occurances of the map in the adjacency table
  2088. // Input : *pSaveData -
  2089. // *pMapName -
  2090. // index -
  2091. // Output : int
  2092. //-----------------------------------------------------------------------------
  2093. int EntryInTable( CSaveRestoreData *pSaveData, const char *pMapName, int index )
  2094. {
  2095. int i;
  2096. index++;
  2097. for ( i = index; i < pSaveData->levelInfo.connectionCount; i++ )
  2098. {
  2099. if ( !stricmp( pSaveData->levelInfo.levelList[i].mapName, pMapName ) )
  2100. return i;
  2101. }
  2102. return -1;
  2103. }
  2104. //-----------------------------------------------------------------------------
  2105. // Purpose:
  2106. // Input : *pSaveData -
  2107. // output -
  2108. // *pLandmarkName -
  2109. //-----------------------------------------------------------------------------
  2110. void LandmarkOrigin( CSaveRestoreData *pSaveData, Vector& output, const char *pLandmarkName )
  2111. {
  2112. int i;
  2113. for ( i = 0; i < pSaveData->levelInfo.connectionCount; i++ )
  2114. {
  2115. if ( !stricmp( pSaveData->levelInfo.levelList[i].landmarkName, pLandmarkName ) )
  2116. {
  2117. VectorCopy( pSaveData->levelInfo.levelList[i].vecLandmarkOrigin, output );
  2118. return;
  2119. }
  2120. }
  2121. VectorCopy( vec3_origin, output );
  2122. }
  2123. //-----------------------------------------------------------------------------
  2124. // Purpose:
  2125. // Input : *pOldLevel -
  2126. // *pLandmarkName -
  2127. //-----------------------------------------------------------------------------
  2128. void CSaveRestore::LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName )
  2129. {
  2130. FinishAsyncSave();
  2131. CSaveRestoreData currentLevelData, *pSaveData;
  2132. int i, test, flags, index, movedCount = 0;
  2133. SAVE_HEADER header;
  2134. Vector landmarkOrigin;
  2135. memset( &currentLevelData, 0, sizeof(CSaveRestoreData) );
  2136. g_ServerGlobalVariables.pSaveData = &currentLevelData;
  2137. // Build the adjacent map list
  2138. serverGameDLL->BuildAdjacentMapList();
  2139. bool foundprevious = false;
  2140. for ( i = 0; i < currentLevelData.levelInfo.connectionCount; i++ )
  2141. {
  2142. // make sure the previous level is in the connection list so we can
  2143. // bring over the player.
  2144. if ( !strcmpi( currentLevelData.levelInfo.levelList[i].mapName, pOldLevel ) )
  2145. {
  2146. foundprevious = true;
  2147. }
  2148. for ( test = 0; test < i; test++ )
  2149. {
  2150. // Only do maps once
  2151. if ( !stricmp( currentLevelData.levelInfo.levelList[i].mapName, currentLevelData.levelInfo.levelList[test].mapName ) )
  2152. break;
  2153. }
  2154. // Map was already in the list
  2155. if ( test < i )
  2156. continue;
  2157. // ConMsg("Merging entities from %s ( at %s )\n", currentLevelData.levelInfo.levelList[i].mapName, currentLevelData.levelInfo.levelList[i].landmarkName );
  2158. pSaveData = LoadSaveData( GetSaveGameMapName( currentLevelData.levelInfo.levelList[i].mapName ) );
  2159. if ( pSaveData )
  2160. {
  2161. serverGameDLL->ReadRestoreHeaders( pSaveData );
  2162. ParseSaveTables( pSaveData, &header, 0 );
  2163. EntityPatchRead( pSaveData, currentLevelData.levelInfo.levelList[i].mapName );
  2164. pSaveData->levelInfo.time = sv.GetTime();// - header.time;
  2165. pSaveData->levelInfo.fUseLandmark = true;
  2166. flags = 0;
  2167. LandmarkOrigin( &currentLevelData, landmarkOrigin, pLandmarkName );
  2168. LandmarkOrigin( pSaveData, pSaveData->levelInfo.vecLandmarkOffset, pLandmarkName );
  2169. VectorSubtract( landmarkOrigin, pSaveData->levelInfo.vecLandmarkOffset, pSaveData->levelInfo.vecLandmarkOffset );
  2170. if ( !stricmp( currentLevelData.levelInfo.levelList[i].mapName, pOldLevel ) )
  2171. flags |= FENTTABLE_PLAYER;
  2172. index = -1;
  2173. while ( 1 )
  2174. {
  2175. index = EntryInTable( pSaveData, sv.GetMapName(), index );
  2176. if ( index < 0 )
  2177. break;
  2178. flags |= 1<<index;
  2179. }
  2180. if ( flags )
  2181. movedCount = serverGameDLL->CreateEntityTransitionList( pSaveData, flags );
  2182. // If ents were moved, rewrite entity table to save file
  2183. if ( movedCount )
  2184. EntityPatchWrite( pSaveData, GetSaveGameMapName( currentLevelData.levelInfo.levelList[i].mapName ) );
  2185. BuildRestoredIndexTranslationTable( currentLevelData.levelInfo.levelList[i].mapName, pSaveData, true );
  2186. Finish( pSaveData );
  2187. }
  2188. }
  2189. g_ServerGlobalVariables.pSaveData = NULL;
  2190. if ( !foundprevious )
  2191. {
  2192. // Host_Error( "Level transition ERROR\nCan't find connection to %s from %s\n", pOldLevel, sv.GetMapName() );
  2193. Warning( "Level transition ERROR\nCan't find connection to %s from %s\n", pOldLevel, sv.GetMapName() );
  2194. Cbuf_AddText( "disconnect\n" );
  2195. }
  2196. }
  2197. //-----------------------------------------------------------------------------
  2198. // Purpose:
  2199. // Input : *pFile -
  2200. // Output : int
  2201. //-----------------------------------------------------------------------------
  2202. int CSaveRestore::FileSize( FileHandle_t pFile )
  2203. {
  2204. if ( !pFile )
  2205. return 0;
  2206. return g_pSaveRestoreFileSystem->Size(pFile);
  2207. }
  2208. //-----------------------------------------------------------------------------
  2209. // Purpose: Copies the contents of the save directory into a single file
  2210. //-----------------------------------------------------------------------------
  2211. void CSaveRestore::DirectoryCopy( const char *pPath, const char *pDestFileName, bool bIsXSave )
  2212. {
  2213. SaveMsg( "Directory copy (%s)\n", pPath );
  2214. g_pSaveRestoreFileSystem->AsyncFinishAllWrites();
  2215. int nMaps = g_pSaveRestoreFileSystem->DirectoryCount( pPath );
  2216. FileHandle_t hFile = g_pSaveRestoreFileSystem->Open( pDestFileName, "ab+" );
  2217. if ( hFile )
  2218. {
  2219. g_pSaveRestoreFileSystem->Write( &nMaps, sizeof(nMaps), hFile );
  2220. g_pSaveRestoreFileSystem->Close( hFile );
  2221. g_pSaveRestoreFileSystem->DirectoryCopy( pPath, pDestFileName, bIsXSave );
  2222. }
  2223. else
  2224. {
  2225. Warning( "Invalid save, failed to open file\n" );
  2226. }
  2227. }
  2228. //-----------------------------------------------------------------------------
  2229. // Purpose: Extracts all the files contained within pFile
  2230. //-----------------------------------------------------------------------------
  2231. bool CSaveRestore::DirectoryExtract( FileHandle_t pFile, int fileCount )
  2232. {
  2233. return g_pSaveRestoreFileSystem->DirectoryExtract( pFile, fileCount, IsXSave() );
  2234. }
  2235. //-----------------------------------------------------------------------------
  2236. // Purpose: returns the number of save files in the specified filter
  2237. //-----------------------------------------------------------------------------
  2238. void CSaveRestore::DirectoryCount( const char *pPath, int *pResult )
  2239. {
  2240. LOCAL_THREAD_LOCK();
  2241. if ( *pResult == -1 )
  2242. *pResult = g_pSaveRestoreFileSystem->DirectoryCount( pPath );
  2243. // else already set by worker thread
  2244. }
  2245. //-----------------------------------------------------------------------------
  2246. // Purpose:
  2247. // Input : *pPath -
  2248. //-----------------------------------------------------------------------------
  2249. void CSaveRestore::DirectoryClear( const char *pPath )
  2250. {
  2251. g_pSaveRestoreFileSystem->DirectoryClear( pPath, IsXSave() );
  2252. }
  2253. //-----------------------------------------------------------------------------
  2254. // Purpose: deletes all the partial save files from the save game directory
  2255. //-----------------------------------------------------------------------------
  2256. void CSaveRestore::ClearSaveDir( void )
  2257. {
  2258. m_bClearSaveDir = true;
  2259. }
  2260. //-----------------------------------------------------------------------------
  2261. //
  2262. //-----------------------------------------------------------------------------
  2263. void CSaveRestore::DoClearSaveDir( bool bIsXSave )
  2264. {
  2265. // before we clear the save dir, we need to make sure that
  2266. // any async-written save games have finished writing,
  2267. // since we still may need these temp files to write the save game
  2268. char szName[MAX_OSPATH];
  2269. if ( !bIsXSave )
  2270. {
  2271. Q_snprintf(szName, sizeof( szName ), "%s", GetSaveDir() );
  2272. Q_FixSlashes( szName );
  2273. // Create save directory if it doesn't exist
  2274. Sys_mkdir( szName );
  2275. }
  2276. else
  2277. {
  2278. Q_snprintf( szName, sizeof( szName ), "%s:\\", GetCurrentMod() );
  2279. }
  2280. Q_strncat( szName, "*.HL?", sizeof( szName ), COPY_ALL_CHARACTERS );
  2281. DirectoryClear( szName );
  2282. }
  2283. void CSaveRestore::RequestClearSaveDir( void )
  2284. {
  2285. m_bClearSaveDir = true;
  2286. }
  2287. void CSaveRestore::OnFinishedClientRestore()
  2288. {
  2289. g_ClientDLL->DispatchOnRestore();
  2290. ClearRestoredIndexTranslationTables();
  2291. if ( m_bClearSaveDir )
  2292. {
  2293. m_bClearSaveDir = false;
  2294. FinishAsyncSave();
  2295. DoClearSaveDir( IsXSave() );
  2296. }
  2297. }
  2298. void CSaveRestore::AutoSaveDangerousIsSafe()
  2299. {
  2300. if ( save_async.GetBool() && ThreadInMainThread() && g_pSaveThread )
  2301. {
  2302. g_pSaveThread->QueueCall( this, &CSaveRestore::FinishAsyncSave );
  2303. g_pSaveThread->QueueCall( this, &CSaveRestore::AutoSaveDangerousIsSafe );
  2304. return;
  2305. }
  2306. if ( !m_bWaitingForSafeDangerousSave )
  2307. return;
  2308. m_bWaitingForSafeDangerousSave = false;
  2309. ConDMsg( "Committing autosavedangerous...\n" );
  2310. char szOldName[MAX_PATH];
  2311. char szNewName[MAX_PATH];
  2312. // Back up the old autosaves
  2313. if ( StorageDeviceValid() )
  2314. {
  2315. AgeSaveList( "autosave", save_history_count.GetInt(), IsXSave() );
  2316. }
  2317. // Rename the screenshot
  2318. if ( !IsX360() )
  2319. {
  2320. Q_snprintf( szOldName, sizeof( szOldName ), "//%s/%sautosavedangerous%s.tga", MOD_DIR, GetSaveDir(), GetPlatformExt() );
  2321. Q_snprintf( szNewName, sizeof( szNewName ), "//%s/%sautosave%s.tga", MOD_DIR, GetSaveDir(), GetPlatformExt() );
  2322. // there could be an old version, remove it
  2323. if ( g_pFileSystem->FileExists( szNewName ) )
  2324. {
  2325. g_pFileSystem->RemoveFile( szNewName );
  2326. }
  2327. if ( g_pFileSystem->FileExists( szOldName ) )
  2328. {
  2329. if ( !g_pFileSystem->RenameFile( szOldName, szNewName ) )
  2330. {
  2331. SetMostRecentSaveGame( "autosavedangerous" );
  2332. return;
  2333. }
  2334. }
  2335. }
  2336. // Rename the dangerous auto save as a normal auto save
  2337. if ( !IsXSave() )
  2338. {
  2339. Q_snprintf( szOldName, sizeof( szOldName ), "//%s/%sautosavedangerous%s.sav", MOD_DIR, GetSaveDir(), GetPlatformExt() );
  2340. Q_snprintf( szNewName, sizeof( szNewName ), "//%s/%sautosave%s.sav", MOD_DIR, GetSaveDir(), GetPlatformExt() );
  2341. }
  2342. else
  2343. {
  2344. Q_snprintf( szOldName, sizeof( szOldName ), "%s:\\autosavedangerous%s.sav", GetCurrentMod(), GetPlatformExt() );
  2345. Q_snprintf( szNewName, sizeof( szNewName ), "%s:\\autosave%s.sav", GetCurrentMod(), GetPlatformExt() );
  2346. }
  2347. // there could be an old version, remove it
  2348. if ( g_pFileSystem->FileExists( szNewName ) )
  2349. {
  2350. g_pFileSystem->RemoveFile( szNewName );
  2351. }
  2352. if ( !g_pFileSystem->RenameFile( szOldName, szNewName ) )
  2353. {
  2354. SetMostRecentSaveGame( "autosavedangerous" );
  2355. return;
  2356. }
  2357. // Use this as the most recent now that it's safe
  2358. SetMostRecentSaveGame( "autosave" );
  2359. // Finish off all writes
  2360. if ( IsXSave() )
  2361. {
  2362. g_pXboxSystem->FinishContainerWrites();
  2363. }
  2364. }
  2365. static void SaveGame( const CCommand &args )
  2366. {
  2367. bool bFinishAsync = false;
  2368. bool bSetMostRecent = true;
  2369. bool bRenameMap = false;
  2370. if ( args.ArgC() > 2 )
  2371. {
  2372. for ( int i = 2; i < args.ArgC(); i++ )
  2373. {
  2374. if ( !Q_stricmp( args[i], "wait" ) )
  2375. {
  2376. bFinishAsync = true;
  2377. }
  2378. else if ( !Q_stricmp(args[i], "notmostrecent"))
  2379. {
  2380. bSetMostRecent = false;
  2381. }
  2382. else if ( !Q_stricmp( args[i], "copymap" ) )
  2383. {
  2384. bRenameMap = true;
  2385. }
  2386. }
  2387. }
  2388. char szMapName[MAX_PATH];
  2389. if ( bRenameMap )
  2390. {
  2391. // HACK: The bug is going to make a copy of this map, so replace the global state to
  2392. // fool the system
  2393. Q_strncpy( szMapName, sv.m_szMapname, sizeof(szMapName) );
  2394. Q_strncpy( sv.m_szMapname, args[1], sizeof(sv.m_szMapname) );
  2395. }
  2396. int iAdditionalSeconds = g_ServerGlobalVariables.curtime - saverestore->GetMostRecentElapsedTimeSet();
  2397. int iAdditionalMinutes = iAdditionalSeconds / 60;
  2398. iAdditionalSeconds -= iAdditionalMinutes * 60;
  2399. char comment[80];
  2400. GetServerSaveCommentEx(
  2401. comment,
  2402. sizeof( comment ),
  2403. saverestore->GetMostRecentElapsedMinutes() + iAdditionalMinutes,
  2404. saverestore->GetMostRecentElapsedSeconds() + iAdditionalSeconds );
  2405. saverestore->SaveGameSlot( args[1], comment, false, bSetMostRecent );
  2406. if ( bFinishAsync )
  2407. {
  2408. FinishAsyncSave();
  2409. }
  2410. if ( bRenameMap )
  2411. {
  2412. // HACK: Put the original name back
  2413. Q_strncpy( sv.m_szMapname, szMapName, sizeof(sv.m_szMapname) );
  2414. }
  2415. #if !defined (SWDS)
  2416. CL_HudMessage( IsX360() ? "GAMESAVED_360" : "GAMESAVED" );
  2417. #endif
  2418. }
  2419. //-----------------------------------------------------------------------------
  2420. // Purpose:
  2421. // Output : void Host_Savegame_f
  2422. //-----------------------------------------------------------------------------
  2423. CON_COMMAND_F( save, "Saves current game.", FCVAR_DONTRECORD )
  2424. {
  2425. // Can we save at this point?
  2426. if ( !saverestore->IsValidSave() )
  2427. return;
  2428. if ( args.ArgC() < 2 )
  2429. {
  2430. ConDMsg("save <savename> [wait]: save a game\n");
  2431. return;
  2432. }
  2433. if ( strstr(args[1], ".." ) )
  2434. {
  2435. ConDMsg ("Relative pathnames are not allowed.\n");
  2436. return;
  2437. }
  2438. if ( strstr(sv.m_szMapname, "background" ) )
  2439. {
  2440. ConDMsg ("\"background\" is a reserved map name and cannot be saved or loaded.\n");
  2441. return;
  2442. }
  2443. g_SaveRestore.SetIsXSave( false );
  2444. SaveGame( args );
  2445. }
  2446. //-----------------------------------------------------------------------------
  2447. // Purpose:
  2448. // Output : void Host_Savegame_f
  2449. //-----------------------------------------------------------------------------
  2450. CON_COMMAND_F( xsave, "Saves current game to a 360 storage device.", FCVAR_DONTRECORD )
  2451. {
  2452. // Can we save at this point?
  2453. if ( !saverestore->IsValidSave() )
  2454. return;
  2455. if ( args.ArgC() < 2 )
  2456. {
  2457. ConDMsg("save <savename> [wait]: save a game\n");
  2458. return;
  2459. }
  2460. if ( strstr(args[1], ".." ) )
  2461. {
  2462. ConDMsg ("Relative pathnames are not allowed.\n");
  2463. return;
  2464. }
  2465. if ( strstr(sv.m_szMapname, "background" ) )
  2466. {
  2467. ConDMsg ("\"background\" is a reserved map name and cannot be saved or loaded.\n");
  2468. return;
  2469. }
  2470. g_SaveRestore.SetIsXSave( IsX360() );
  2471. SaveGame( args );
  2472. }
  2473. //-----------------------------------------------------------------------------
  2474. // Purpose: saves the game, but only includes the state for the current level
  2475. // useful for bug reporting.
  2476. // Output :
  2477. //-----------------------------------------------------------------------------
  2478. CON_COMMAND_F( minisave, "Saves game (for current level only!)", FCVAR_DONTRECORD )
  2479. {
  2480. // Can we save at this point?
  2481. if ( !saverestore->IsValidSave() )
  2482. return;
  2483. if (args.ArgC() != 2 || strstr(args[1], ".."))
  2484. return;
  2485. int iAdditionalSeconds = g_ServerGlobalVariables.curtime - saverestore->GetMostRecentElapsedTimeSet();
  2486. int iAdditionalMinutes = iAdditionalSeconds / 60;
  2487. iAdditionalSeconds -= iAdditionalMinutes * 60;
  2488. char comment[80];
  2489. GetServerSaveCommentEx(
  2490. comment,
  2491. sizeof( comment ),
  2492. saverestore->GetMostRecentElapsedMinutes() + iAdditionalMinutes,
  2493. saverestore->GetMostRecentElapsedSeconds() + iAdditionalSeconds );
  2494. saverestore->SaveGameSlot( args[1], comment, true, true );
  2495. }
  2496. static void AutoSave_Silent( bool bDangerous )
  2497. {
  2498. // Can we save at this point?
  2499. if ( !saverestore->IsValidSave() )
  2500. return;
  2501. int iAdditionalSeconds = g_ServerGlobalVariables.curtime - saverestore->GetMostRecentElapsedTimeSet();
  2502. int iAdditionalMinutes = iAdditionalSeconds / 60;
  2503. iAdditionalSeconds -= iAdditionalMinutes * 60;
  2504. char comment[80];
  2505. GetServerSaveCommentEx(
  2506. comment,
  2507. sizeof( comment ),
  2508. saverestore->GetMostRecentElapsedMinutes() + iAdditionalMinutes,
  2509. saverestore->GetMostRecentElapsedSeconds() + iAdditionalSeconds );
  2510. g_SaveRestore.SetIsXSave( IsX360() );
  2511. if ( !bDangerous )
  2512. {
  2513. saverestore->SaveGameSlot( "autosave", comment, false, true );
  2514. }
  2515. else
  2516. {
  2517. saverestore->SaveGameSlot( "autosavedangerous", comment, false, false );
  2518. }
  2519. }
  2520. static ConVar save_console( "save_console", "0", 0, "Autosave on the PC behaves like it does on the consoles." );
  2521. static ConVar save_huddelayframes( "save_huddelayframes", "1", 0, "Number of frames to defer for drawing the Saving message." );
  2522. CON_COMMAND( _autosave, "Autosave" )
  2523. {
  2524. AutoSave_Silent( false );
  2525. bool bConsole = save_console.GetBool();
  2526. #if defined ( _X360 )
  2527. bConsole = true;
  2528. #endif
  2529. if ( bConsole )
  2530. {
  2531. #if !defined (SWDS)
  2532. CL_HudMessage( IsX360() ? "GAMESAVED_360" : "GAMESAVED" );
  2533. #endif
  2534. }
  2535. }
  2536. CON_COMMAND( _autosavedangerous, "AutoSaveDangerous" )
  2537. {
  2538. // Don't even bother if we've got an invalid save
  2539. if ( saverestore->StorageDeviceValid() == false )
  2540. return;
  2541. AutoSave_Silent( true );
  2542. bool bConsole = save_console.GetBool();
  2543. #if defined ( _X360 )
  2544. bConsole = true;
  2545. #endif
  2546. if ( bConsole )
  2547. {
  2548. #if !defined (SWDS)
  2549. CL_HudMessage( IsX360() ? "GAMESAVED_360" : "GAMESAVED" );
  2550. #endif
  2551. }
  2552. }
  2553. //-----------------------------------------------------------------------------
  2554. // Purpose:
  2555. // Output : void Host_AutoSave_f
  2556. //-----------------------------------------------------------------------------
  2557. CON_COMMAND( autosave, "Autosave" )
  2558. {
  2559. // Can we save at this point?
  2560. if ( !saverestore->IsValidSave() || !sv_autosave.GetBool() )
  2561. return;
  2562. bool bConsole = save_console.GetBool();
  2563. char const *pchSaving = IsX360() ? "GAMESAVING_360" : "GAMESAVING";
  2564. #if defined ( _X360 )
  2565. bConsole = true;
  2566. #endif
  2567. if ( bConsole )
  2568. {
  2569. #if !defined (SWDS)
  2570. CL_HudMessage( pchSaving );
  2571. #endif
  2572. g_SaveRestore.AddDeferredCommand( "_autosave" );
  2573. }
  2574. else
  2575. {
  2576. AutoSave_Silent( false );
  2577. }
  2578. }
  2579. //-----------------------------------------------------------------------------
  2580. // Purpose:
  2581. // Output : void Host_AutoSaveDangerous_f
  2582. //-----------------------------------------------------------------------------
  2583. CON_COMMAND( autosavedangerous, "AutoSaveDangerous" )
  2584. {
  2585. // Can we save at this point?
  2586. if ( !saverestore->IsValidSave() || !sv_autosave.GetBool() )
  2587. return;
  2588. // Don't even bother if we've got an invalid save
  2589. if ( saverestore->StorageDeviceValid() == false )
  2590. return;
  2591. //Don't print out "SAVED" unless we're running on an Xbox (in which case it prints "CHECKPOINT").
  2592. bool bConsole = save_console.GetBool();
  2593. char const *pchSaving = IsX360() ? "GAMESAVING_360" : "GAMESAVING";
  2594. #if defined ( _X360 )
  2595. bConsole = true;
  2596. #endif
  2597. if ( bConsole )
  2598. {
  2599. #if !defined (SWDS)
  2600. CL_HudMessage( pchSaving );
  2601. #endif
  2602. g_SaveRestore.AddDeferredCommand( "_autosavedangerous" );
  2603. }
  2604. else
  2605. {
  2606. AutoSave_Silent( true );
  2607. }
  2608. }
  2609. //-----------------------------------------------------------------------------
  2610. // Purpose:
  2611. // Output : void Host_AutoSaveSafe_f
  2612. //-----------------------------------------------------------------------------
  2613. CON_COMMAND( autosavedangerousissafe, "" )
  2614. {
  2615. saverestore->AutoSaveDangerousIsSafe();
  2616. }
  2617. //-----------------------------------------------------------------------------
  2618. // Purpose: Load a save game in response to a console command (load or xload)
  2619. //-----------------------------------------------------------------------------
  2620. static void LoadSaveGame( const char *savename )
  2621. {
  2622. // Make sure the freaking save file exists....
  2623. if ( !saverestore->SaveFileExists( savename ) )
  2624. {
  2625. Warning( "Can't load '%s', file missing!\n", savename );
  2626. return;
  2627. }
  2628. GetTestScriptMgr()->SetWaitCheckPoint( "load_game" );
  2629. // if we're not currently in a game, show progress
  2630. if ( !sv.IsActive() || sv.IsLevelMainMenuBackground() )
  2631. {
  2632. EngineVGui()->EnabledProgressBarForNextLoad();
  2633. }
  2634. // Put up loading plaque
  2635. SCR_BeginLoadingPlaque();
  2636. Host_Disconnect( false ); // stop old game
  2637. HostState_LoadGame( savename, false );
  2638. }
  2639. //-----------------------------------------------------------------------------
  2640. // Purpose:
  2641. // Output : void Host_Loadgame_f
  2642. //-----------------------------------------------------------------------------
  2643. void Host_Loadgame_f( const CCommand &args )
  2644. {
  2645. if ( cmd_source != src_command )
  2646. return;
  2647. if ( sv.IsMultiplayer() )
  2648. {
  2649. ConMsg ("Can't load in multiplayer games.\n");
  2650. return;
  2651. }
  2652. if (args.ArgC() < 2)
  2653. {
  2654. ConMsg ("load <savename> : load a game\n");
  2655. return;
  2656. }
  2657. g_szMapLoadOverride[0] = 0;
  2658. if ( args.ArgC() > 2)
  2659. {
  2660. V_strncpy( g_szMapLoadOverride, args[2], sizeof( g_szMapLoadOverride ) );
  2661. }
  2662. g_SaveRestore.SetIsXSave( false );
  2663. LoadSaveGame( args[1] );
  2664. }
  2665. // Always loads saves from DEFAULT_WRITE_PATH, regardless of platform
  2666. CON_COMMAND_AUTOCOMPLETEFILE( load, Host_Loadgame_f, "Load a saved game.", "save", sav );
  2667. // Loads saves from the 360 storage device
  2668. CON_COMMAND( xload, "Load a saved game from a 360 storage device." )
  2669. {
  2670. if ( sv.IsMultiplayer() )
  2671. {
  2672. ConMsg ("Can't load in multiplayer games.\n");
  2673. return;
  2674. }
  2675. if (args.ArgC() != 2)
  2676. {
  2677. ConMsg ("xload <savename>\n");
  2678. return;
  2679. }
  2680. g_SaveRestore.SetIsXSave( IsX360() );
  2681. LoadSaveGame( args[1] );
  2682. }
  2683. CON_COMMAND( save_finish_async, "" )
  2684. {
  2685. FinishAsyncSave();
  2686. }
  2687. //-----------------------------------------------------------------------------
  2688. // Purpose:
  2689. //-----------------------------------------------------------------------------
  2690. void CSaveRestore::Init( void )
  2691. {
  2692. int minplayers = 1;
  2693. // serverGameClients should have been initialized by the CModAppSystemGroup Create method (so it's before the Host_Init stuff which calls this)
  2694. Assert( serverGameClients );
  2695. if ( serverGameClients )
  2696. {
  2697. int dummy = 1;
  2698. int dummy2 = 1;
  2699. serverGameClients->GetPlayerLimits( minplayers, dummy, dummy2 );
  2700. }
  2701. if ( !serverGameClients ||
  2702. ( minplayers == 1 ) )
  2703. {
  2704. GetSaveMemory();
  2705. Assert( !g_pSaveThread );
  2706. ThreadPoolStartParams_t threadPoolStartParams;
  2707. threadPoolStartParams.nThreads = 1;
  2708. if ( !IsX360() )
  2709. {
  2710. threadPoolStartParams.fDistribute = TRS_FALSE;
  2711. }
  2712. else
  2713. {
  2714. threadPoolStartParams.iAffinityTable[0] = XBOX_PROCESSOR_1;
  2715. threadPoolStartParams.bUseAffinityTable = true;
  2716. }
  2717. g_pSaveThread = CreateThreadPool();
  2718. g_pSaveThread->Start( threadPoolStartParams, "SaveJob" );
  2719. }
  2720. m_nDeferredCommandFrames = 0;
  2721. m_szSaveGameScreenshotFile[0] = 0;
  2722. if ( !IsX360() && !CommandLine()->FindParm( "-noclearsave" ) )
  2723. {
  2724. ClearSaveDir();
  2725. }
  2726. }
  2727. //-----------------------------------------------------------------------------
  2728. // Purpose:
  2729. //-----------------------------------------------------------------------------
  2730. void CSaveRestore::Shutdown( void )
  2731. {
  2732. FinishAsyncSave();
  2733. if ( g_pSaveThread )
  2734. {
  2735. g_pSaveThread->Stop();
  2736. g_pSaveThread->Release();
  2737. g_pSaveThread = NULL;
  2738. }
  2739. m_szSaveGameScreenshotFile[0] = 0;
  2740. }
  2741. char const *CSaveRestore::GetMostRecentlyLoadedFileName()
  2742. {
  2743. return m_szMostRecentSaveLoadGame;
  2744. }
  2745. char const *CSaveRestore::GetSaveFileName()
  2746. {
  2747. return m_szSaveGameName;
  2748. }
  2749. void CSaveRestore::AddDeferredCommand( char const *pchCommand )
  2750. {
  2751. m_nDeferredCommandFrames = clamp( save_huddelayframes.GetInt(), 0, 10 );
  2752. CUtlSymbol sym;
  2753. sym = pchCommand;
  2754. m_sDeferredCommands.AddToTail( sym );
  2755. }
  2756. void CSaveRestore::OnFrameRendered()
  2757. {
  2758. if ( m_nDeferredCommandFrames > 0 )
  2759. {
  2760. --m_nDeferredCommandFrames;
  2761. if ( m_nDeferredCommandFrames == 0 )
  2762. {
  2763. // Dispatch deferred command
  2764. for ( int i = 0; i < m_sDeferredCommands.Count(); ++i )
  2765. {
  2766. Cbuf_AddText( m_sDeferredCommands[ i ].String() );
  2767. }
  2768. m_sDeferredCommands.Purge();
  2769. }
  2770. }
  2771. }
  2772. bool CSaveRestore::StorageDeviceValid( void )
  2773. {
  2774. // PC is always valid
  2775. if ( !IsX360() )
  2776. return true;
  2777. // Non-XSaves are always valid
  2778. if ( !IsXSave() )
  2779. return true;
  2780. #ifdef _X360
  2781. // Otherwise, we must have a real storage device
  2782. int nStorageDeviceID = XBX_GetStorageDeviceId();
  2783. return ( nStorageDeviceID != XBX_INVALID_STORAGE_ID && nStorageDeviceID != XBX_STORAGE_DECLINED );
  2784. #endif
  2785. return true;
  2786. }
  2787. bool CSaveRestore::IsSaveInProgress()
  2788. {
  2789. return g_bSaveInProgress;
  2790. }