Counter Strike : Global Offensive Source Code
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.

3538 lines
101 KiB

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