sys_session_savegames.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "sys_savegame.h"
  23. #include "sys_session_local.h"
  24. #include "sys_session_savegames.h"
  25. extern idCVar saveGame_verbose;
  26. idCVar savegame_error( "savegame_error", "0", CVAR_INTEGER, "Combination of bits that will simulate and error, see 'savegamePrintErrors'. 0 = no error" );
  27. void OutputDetailList( const saveGameDetailsList_t & savegameList );
  28. #pragma region PROCESSORS
  29. /*
  30. ================================================================================================
  31. idSaveGameProcessorLoadFiles
  32. ================================================================================================
  33. */
  34. /*
  35. ========================
  36. idSaveGameProcessorLoadFiles::InitLoadFiles
  37. ========================
  38. */
  39. bool idSaveGameProcessorLoadFiles::InitLoadFiles( const char * folder_, const saveFileEntryList_t & files, idSaveGameManager::packageType_t type ) {
  40. if ( !idSaveGameProcessor::Init() ) {
  41. return false;
  42. }
  43. parms.directory = AddSaveFolderPrefix( folder_, type );
  44. parms.description.slotName = folder_;
  45. parms.mode = SAVEGAME_MBF_LOAD;
  46. for ( int i = 0; i < files.Num(); ++i ) {
  47. parms.files.Append( files[i] );
  48. }
  49. return true;
  50. }
  51. /*
  52. ========================
  53. idSaveGameProcessorLoadFiles::Process
  54. ========================
  55. */
  56. bool idSaveGameProcessorLoadFiles::Process() {
  57. // Platform-specific impl
  58. // This will populate an idFile_Memory with the contents of the save game
  59. // This will not initialize the game, only load the file from the file-system
  60. Sys_ExecuteSavegameCommandAsync( &parms );
  61. return false;
  62. }
  63. /*
  64. ================================================================================================
  65. idSaveGameProcessorDelete
  66. ================================================================================================
  67. */
  68. /*
  69. ========================
  70. idSaveGameProcessorDelete::Init
  71. ========================
  72. */
  73. bool idSaveGameProcessorDelete::InitDelete( const char * folder_, idSaveGameManager::packageType_t type ) {
  74. if ( !idSaveGameProcessor::Init() ) {
  75. return false;
  76. }
  77. parms.description.slotName = folder_;
  78. parms.directory = AddSaveFolderPrefix( folder_, type );
  79. parms.mode = SAVEGAME_MBF_DELETE_FOLDER;
  80. return true;
  81. }
  82. /*
  83. ========================
  84. idSaveGameProcessorDelete::Process
  85. ========================
  86. */
  87. bool idSaveGameProcessorDelete::Process() {
  88. // Platform-specific impl
  89. // This will populate an idFile_Memory with the contents of the save game
  90. // This will not initialize the game, only load the file from the file-system
  91. Sys_ExecuteSavegameCommandAsync( &parms );
  92. return false;
  93. }
  94. /*
  95. ================================================================================================
  96. idSaveGameProcessorSaveFiles
  97. ================================================================================================
  98. */
  99. /*
  100. ========================
  101. idSaveGameProcessorSaveFiles::InitSave
  102. ========================
  103. */
  104. bool idSaveGameProcessorSaveFiles::InitSave( const char * folder, const saveFileEntryList_t & files, const idSaveGameDetails & descriptionForPS3, idSaveGameManager::packageType_t type ) {
  105. if ( !idSaveGameProcessor::Init() ) {
  106. return false;
  107. }
  108. if ( files.Num() == 0 ) {
  109. idLib::Warning( "No files to save." );
  110. return false;
  111. }
  112. // Setup save system
  113. parms.directory = AddSaveFolderPrefix( folder, type );
  114. parms.mode = SAVEGAME_MBF_SAVE; // do NOT delete the existing files
  115. for ( int i = 0; i < files.Num(); ++i ) {
  116. parms.files.Append( files[i] );
  117. }
  118. this->parms.description = descriptionForPS3;
  119. parms.description.slotName = folder;
  120. return true;
  121. }
  122. /*
  123. ========================
  124. idSaveGameProcessorSaveFiles::Process
  125. ========================
  126. */
  127. bool idSaveGameProcessorSaveFiles::Process() {
  128. // Platform-specific implementation
  129. // This will start a worker thread for async operation.
  130. // It will always signal when it's completed.
  131. Sys_ExecuteSavegameCommandAsync( &parms );
  132. return false;
  133. }
  134. /*
  135. ================================================================================================
  136. idSaveGameProcessorEnumerateGames
  137. ================================================================================================
  138. */
  139. /*
  140. ========================
  141. idSaveGameProcessorEnumerateGames::Process
  142. ========================
  143. */
  144. bool idSaveGameProcessorEnumerateGames::Process() {
  145. parms.mode = SAVEGAME_MBF_ENUMERATE | SAVEGAME_MBF_READ_DETAILS;
  146. // Platform-specific implementation
  147. // This will start a worker thread for async operation.
  148. // It will always signal when it's completed.
  149. Sys_ExecuteSavegameCommandAsync( &parms );
  150. return false;
  151. }
  152. #pragma endregion
  153. /*
  154. ========================
  155. idSessionLocal::SaveGameSync
  156. ========================
  157. */
  158. saveGameHandle_t idSessionLocal::SaveGameSync( const char * name, const saveFileEntryList_t & files, const idSaveGameDetails & description ) {
  159. saveGameHandle_t handle = 0;
  160. // serialize the description file behind their back...
  161. saveFileEntryList_t filesWithDetails( files );
  162. idFile_SaveGame * gameDetailsFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_DETAILS_FILENAME, SAVEGAMEFILE_TEXT | SAVEGAMEFILE_AUTO_DELETE );
  163. gameDetailsFile->MakeWritable();
  164. description.descriptors.WriteToIniFile( gameDetailsFile );
  165. filesWithDetails.Append( gameDetailsFile );
  166. if ( processorSaveFiles->InitSave( name, filesWithDetails, description ) ) {
  167. processorSaveFiles->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnSaveCompleted, &processorSaveFiles->GetParmsNonConst() ) );
  168. handle = GetSaveGameManager().ExecuteProcessorAndWait( processorSaveFiles );
  169. }
  170. // Errors within the process of saving are handled in OnSaveCompleted()
  171. // so that asynchronous save errors are handled the same was as synchronous.
  172. if ( handle == 0 ) {
  173. idSaveLoadParms & parms = processorSaveFiles->GetParmsNonConst();
  174. parms.errorCode = SAVEGAME_E_UNKNOWN;
  175. // Uniform error handling
  176. OnSaveCompleted( &parms );
  177. }
  178. return handle;
  179. }
  180. /*
  181. ========================
  182. idSessionLocal::SaveGameAsync
  183. ========================
  184. */
  185. saveGameHandle_t idSessionLocal::SaveGameAsync( const char * name, const saveFileEntryList_t & files, const idSaveGameDetails & description ) {
  186. saveGameHandle_t handle = 0;
  187. // Done this way so we know it will be shutdown properly on early exit or exception
  188. struct local_t {
  189. local_t( idSaveLoadParms * localparms ) : parms( localparms ) {
  190. // Prepare background renderer
  191. }
  192. ~local_t() {
  193. // Shutdown background renderer
  194. }
  195. idSaveLoadParms * parms;
  196. } local( &processorSaveFiles->GetParmsNonConst() );
  197. // serialize the description file behind their back...
  198. saveFileEntryList_t filesWithDetails( files );
  199. idFile_SaveGame * gameDetailsFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_DETAILS_FILENAME, SAVEGAMEFILE_TEXT | SAVEGAMEFILE_AUTO_DELETE );
  200. gameDetailsFile->MakeWritable();
  201. description.descriptors.WriteToIniFile( gameDetailsFile );
  202. filesWithDetails.Append( gameDetailsFile );
  203. if ( processorSaveFiles->InitSave( name, filesWithDetails, description ) ) {
  204. processorSaveFiles->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnSaveCompleted, &processorSaveFiles->GetParmsNonConst() ) );
  205. handle = GetSaveGameManager().ExecuteProcessor( processorSaveFiles );
  206. }
  207. // Errors within the process of saving are handled in OnSaveCompleted()
  208. // so that asynchronous save errors are handled the same was as synchronous.
  209. if ( handle == 0 ) {
  210. idSaveLoadParms & parms = processorSaveFiles->GetParmsNonConst();
  211. parms.errorCode = SAVEGAME_E_UNKNOWN;
  212. common->Dialog().ShowSaveIndicator( false );
  213. // Uniform error handling
  214. OnSaveCompleted( &parms );
  215. }
  216. return handle;
  217. }
  218. /*
  219. ========================
  220. idSessionLocal::OnSaveCompleted
  221. ========================
  222. */
  223. void idSessionLocal::OnSaveCompleted( idSaveLoadParms * parms ) {
  224. idLocalUser * master = session->GetSignInManager().GetMasterLocalUser();
  225. if ( parms->GetError() != SAVEGAME_E_INSUFFICIENT_ROOM ) {
  226. // if savegame completeed we can clear retry info
  227. GetSaveGameManager().ClearRetryInfo();
  228. }
  229. // Only turn off the indicator if we're not also going to save the profile settings
  230. if ( master != NULL && master->GetProfile() != NULL && !master->GetProfile()->IsDirty() ) {
  231. common->Dialog().ShowSaveIndicator( false );
  232. }
  233. if ( parms->GetError() == SAVEGAME_E_NONE ) {
  234. // Save the profile any time we save the game
  235. if ( master != NULL && master->GetProfile() != NULL ) {
  236. master->GetProfile()->SaveSettings( false );
  237. }
  238. // Update the enumerated savegames
  239. saveGameDetailsList_t & detailList = session->GetSaveGameManager().GetEnumeratedSavegamesNonConst();
  240. idSaveGameDetails * details = detailList.Find( parms->description );
  241. if ( details == NULL ) {
  242. // add it
  243. detailList.Append( parms->description );
  244. } else {
  245. // replace it
  246. *details = parms->description;
  247. }
  248. }
  249. // Error handling and additional processing
  250. common->OnSaveCompleted( *parms );
  251. }
  252. /*
  253. ========================
  254. idSessionLocal::LoadGameSync
  255. We still want to use the savegame manager because we could have file system operations in flight and need to
  256. ========================
  257. */
  258. saveGameHandle_t idSessionLocal::LoadGameSync( const char * name, saveFileEntryList_t & files ) {
  259. idSaveLoadParms & parms = processorLoadFiles->GetParmsNonConst();
  260. saveGameHandle_t handle = 0;
  261. {
  262. // Put in a local block so everything will go in the global heap before the map change, but the heap is
  263. // automatically popped out on early return or exception
  264. // You cannot be in the global heap during a map change...
  265. //idScopedGlobalHeap everythingGoesInTheGlobalHeap;
  266. // Done this way so we know it will be shutdown properly on early exit or exception
  267. struct local_t {
  268. local_t( idSaveLoadParms * parms_ ) : parms( parms_ ) {
  269. // Prepare background renderer or loadscreen with what you want to show
  270. {
  271. // with mode: SAVE_GAME_MODE_LOAD
  272. }
  273. }
  274. ~local_t() {
  275. // Shutdown background renderer or loadscreen
  276. {
  277. }
  278. common->OnLoadCompleted( *parms );
  279. }
  280. idSaveLoadParms * parms;
  281. } local( &parms );
  282. // Read the details file when loading games
  283. saveFileEntryList_t filesWithDetails( files );
  284. std::auto_ptr< idFile_SaveGame > gameDetailsFile( new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_DETAILS_FILENAME, SAVEGAMEFILE_TEXT ) );
  285. filesWithDetails.Append( gameDetailsFile.get() );
  286. // Check the cached save details from the enumeration and make sure we don't load a save from a newer version of the game!
  287. const saveGameDetailsList_t details = GetSaveGameManager().GetEnumeratedSavegames();
  288. for ( int i = 0; i < details.Num(); ++i ) {
  289. if ( idStr::Cmp( name, details[i].slotName ) == 0 ) {
  290. if ( details[i].GetSaveVersion() > BUILD_NUMBER ) {
  291. parms.errorCode = SAVEGAME_E_INCOMPATIBLE_NEWER_VERSION;
  292. return 0;
  293. }
  294. }
  295. }
  296. // Synchronous load
  297. if ( processorLoadFiles->InitLoadFiles( name, filesWithDetails ) ) {
  298. handle = GetSaveGameManager().ExecuteProcessorAndWait( processorLoadFiles );
  299. }
  300. if ( handle == 0 ) {
  301. parms.errorCode = SAVEGAME_E_UNKNOWN;
  302. }
  303. if ( parms.GetError() != SAVEGAME_E_NONE ) {
  304. return 0;
  305. }
  306. // Checks the description file to see if corrupted or if it's from a newer savegame
  307. if ( !LoadGameCheckDescriptionFile( parms ) ) {
  308. return 0;
  309. }
  310. // Checks to see if loaded map is from a DLC map and if that DLC is active
  311. if ( !IsDLCAvailable( parms.description.GetMapName() ) ) {
  312. parms.errorCode = SAVEGAME_E_DLC_NOT_FOUND;
  313. return 0;
  314. }
  315. }
  316. common->OnLoadFilesCompleted( parms );
  317. return handle;
  318. }
  319. /*
  320. ========================
  321. idSessionLocal::OnLoadCompleted
  322. ========================
  323. */
  324. void idSessionLocal::OnLoadCompleted( idSaveLoadParms * parms ) {
  325. }
  326. /*
  327. ========================
  328. idSessionLocal::EnumerateSaveGamesSync
  329. ========================
  330. */
  331. saveGameHandle_t idSessionLocal::EnumerateSaveGamesSync() {
  332. saveGameHandle_t handle = 0;
  333. // Done this way so we know it will be shutdown properly on early exit or exception
  334. struct local_t {
  335. local_t() {
  336. // Prepare background renderer or loadscreen with what you want to show
  337. {
  338. // with mode: SAVE_GAME_MODE_ENUMERATE
  339. }
  340. }
  341. ~local_t() {
  342. // Shutdown background renderer or loadscreen
  343. {
  344. }
  345. }
  346. } local;
  347. // flush the old enumerated list
  348. GetSaveGameManager().GetEnumeratedSavegamesNonConst().Clear();
  349. if ( processorEnumerate->Init() ) {
  350. processorEnumerate->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnEnumerationCompleted, &processorEnumerate->GetParmsNonConst() ) );
  351. handle = GetSaveGameManager().ExecuteProcessorAndWait( processorEnumerate );
  352. }
  353. // Errors within the process of saving are handled in OnEnumerationCompleted()
  354. // so that asynchronous save errors are handled the same was as synchronous.
  355. if ( handle == 0 ) {
  356. idSaveLoadParms & parms = processorEnumerate->GetParmsNonConst();
  357. parms.errorCode = SAVEGAME_E_UNKNOWN;
  358. // Uniform error handling
  359. OnEnumerationCompleted( &parms );
  360. }
  361. return handle;
  362. }
  363. /*
  364. ========================
  365. idSessionLocal::EnumerateSaveGamesAsync
  366. ========================
  367. */
  368. saveGameHandle_t idSessionLocal::EnumerateSaveGamesAsync() {
  369. saveGameHandle_t handle = 0;
  370. // flush the old enumerated list
  371. GetSaveGameManager().GetEnumeratedSavegamesNonConst().Clear();
  372. if ( processorEnumerate->Init() ) {
  373. processorEnumerate->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnEnumerationCompleted, &processorEnumerate->GetParmsNonConst() ) );
  374. handle = GetSaveGameManager().ExecuteProcessor( processorEnumerate );
  375. }
  376. // Errors within the process of saving are handled in OnEnumerationCompleted()
  377. // so that asynchronous save errors are handled the same was as synchronous.
  378. if ( handle == 0 ) {
  379. idSaveLoadParms & parms = processorEnumerate->GetParmsNonConst();
  380. parms.errorCode = SAVEGAME_E_UNKNOWN;
  381. // Uniform error handling
  382. OnEnumerationCompleted( &parms );
  383. }
  384. return handle;
  385. }
  386. int idSort_EnumeratedSavegames( const idSaveGameDetails * a, const idSaveGameDetails * b ) {
  387. return b->date - a->date;
  388. }
  389. /*
  390. ========================
  391. idSessionLocal::OnEnumerationCompleted
  392. ========================
  393. */
  394. void idSessionLocal::OnEnumerationCompleted( idSaveLoadParms * parms ) {
  395. // idTech4 idList::sort is just a qsort wrapper, which doesn't deal with
  396. // idStrStatic properly!
  397. // parms->detailList.Sort( idSort_EnumeratedSavegames );
  398. std::sort( parms->detailList.Ptr(), parms->detailList.Ptr() + parms->detailList.Num() );
  399. if ( parms->GetError() == SAVEGAME_E_NONE ) {
  400. // Copy into the maintained list
  401. saveGameDetailsList_t & detailsList = session->GetSaveGameManager().GetEnumeratedSavegamesNonConst();
  402. //mem.PushHeap();
  403. detailsList = parms->detailList; // copies new list into the savegame manager's reference
  404. //mem.PopHeap();
  405. // The platform-specific implementations don't know about the prefixes
  406. // If we don't do this here, we will end up with slots like: GAME-GAME-GAME-GAME-AUTOSAVE...
  407. for ( int i = 0; i < detailsList.Num(); i++ ) {
  408. idSaveGameDetails & details = detailsList[i];
  409. const idStr original = details.slotName;
  410. const idStr stripped = RemoveSaveFolderPrefix( original, idSaveGameManager::PACKAGE_GAME );
  411. details.slotName = stripped;
  412. }
  413. if ( saveGame_verbose.GetBool() ) {
  414. OutputDetailList( detailsList );
  415. }
  416. }
  417. common->OnEnumerationCompleted( *parms );
  418. }
  419. /*
  420. ========================
  421. idSessionLocal::DeleteSaveGameSync
  422. ========================
  423. */
  424. saveGameHandle_t idSessionLocal::DeleteSaveGameSync( const char * name ) {
  425. saveGameHandle_t handle = 0;
  426. // Done this way so we know it will be shutdown properly on early exit or exception
  427. struct local_t {
  428. local_t() {
  429. // Prepare background renderer or loadscreen with what you want to show
  430. {
  431. // with mode: SAVE_GAME_MODE_DELETE
  432. }
  433. }
  434. ~local_t() {
  435. // Shutdown background renderer or loadscreen
  436. {
  437. }
  438. }
  439. } local;
  440. if ( processorDelete->InitDelete( name ) ) {
  441. processorDelete->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnDeleteCompleted, &processorDelete->GetParmsNonConst() ) );
  442. handle = GetSaveGameManager().ExecuteProcessorAndWait( processorDelete );
  443. }
  444. // Errors within the process of saving are handled in OnDeleteCompleted()
  445. // so that asynchronous save errors are handled the same was as synchronous.
  446. if ( handle == 0 ) {
  447. idSaveLoadParms & parms = processorDelete->GetParmsNonConst();
  448. parms.errorCode = SAVEGAME_E_UNKNOWN;
  449. // Uniform error handling
  450. OnDeleteCompleted( &parms );
  451. }
  452. return handle;
  453. }
  454. /*
  455. ========================
  456. idSessionLocal::DeleteSaveGameAsync
  457. ========================
  458. */
  459. saveGameHandle_t idSessionLocal::DeleteSaveGameAsync( const char * name ) {
  460. saveGameHandle_t handle = 0;
  461. if ( processorDelete->InitDelete( name ) ) {
  462. processorDelete->AddCompletedCallback( MakeCallback( this, &idSessionLocal::OnDeleteCompleted, &processorDelete->GetParmsNonConst() ) );
  463. common->Dialog().ShowSaveIndicator( true );
  464. handle = GetSaveGameManager().ExecuteProcessor( processorDelete );
  465. }
  466. // Errors within the process of saving are handled in OnDeleteCompleted()
  467. // so that asynchronous save errors are handled the same was as synchronous.
  468. if ( handle == 0 ) {
  469. idSaveLoadParms & parms = processorDelete->GetParmsNonConst();
  470. parms.errorCode = SAVEGAME_E_UNKNOWN;
  471. // Uniform error handling
  472. OnDeleteCompleted( &parms );
  473. }
  474. return handle;
  475. }
  476. /*
  477. ========================
  478. idSessionLocal::OnDeleteCompleted
  479. ========================
  480. */
  481. void idSessionLocal::OnDeleteCompleted( idSaveLoadParms * parms ) {
  482. common->Dialog().ShowSaveIndicator( false );
  483. if ( parms->GetError() == SAVEGAME_E_NONE ) {
  484. // Update the enumerated list
  485. saveGameDetailsList_t & details = session->GetSaveGameManager().GetEnumeratedSavegamesNonConst();
  486. details.Remove( parms->description );
  487. }
  488. common->OnDeleteCompleted( *parms );
  489. }
  490. /*
  491. ========================
  492. idSessionLocal::IsEnumerating
  493. ========================
  494. */
  495. bool idSessionLocal::IsEnumerating() const {
  496. return !session->IsSaveGameCompletedFromHandle( processorEnumerate->GetHandle() );
  497. }
  498. /*
  499. ========================
  500. idSessionLocal::GetEnumerationHandle
  501. ========================
  502. */
  503. saveGameHandle_t idSessionLocal::GetEnumerationHandle() const {
  504. return processorEnumerate->GetHandle();
  505. }
  506. /*
  507. ========================
  508. idSessionLocal::IsDLCAvailable
  509. ========================
  510. */
  511. bool idSessionLocal::IsDLCAvailable( const char * mapName ) {
  512. bool hasContentPackage = true;
  513. return hasContentPackage;
  514. }
  515. /*
  516. ========================
  517. idSessionLocal::LoadGameCheckDiscNumber
  518. ========================
  519. */
  520. bool idSessionLocal::LoadGameCheckDiscNumber( idSaveLoadParms & parms ) {
  521. #if 0
  522. idStr mapName = parms.description.GetMapName();
  523. assert( !discSwapStateMgr->IsWorking() );
  524. discSwapStateMgr->Init( &parms.callbackSignal, idDiscSwapStateManager::DISC_SWAP_COMMAND_LOAD );
  525. //// TODO_KC this is probably broken now...
  526. //discSwapStateMgr->folder = folder;
  527. //discSwapStateMgr->spawnInfo = newSpawnInfo;
  528. //discSwapStateMgr->spawnSpot = newSpawnPoint;
  529. //discSwapStateMgr->instanceFileName = instanceFileName;
  530. discSwapStateMgr->user = session->GetSignInManager().GetMasterLocalUser();
  531. discSwapStateMgr->map = mapName;
  532. discSwapStateMgr->Pump();
  533. while ( discSwapStateMgr->IsWorking() ) {
  534. Sys_Sleep( 15 );
  535. // process input and render
  536. discSwapStateMgr->Pump();
  537. }
  538. idDiscSwapStateManager::discSwapStateError_t discSwapError = discSwapStateMgr->GetError();
  539. if ( discSwapError == idDiscSwapStateManager::DSSE_CANCEL ) {
  540. parms.errorCode = SAVEGAME_E_CANCELLED;
  541. } else if ( discSwapError == idDiscSwapStateManager::DSSE_INSUFFICIENT_ROOM ) {
  542. parms.errorCode = SAVEGAME_E_INSUFFICIENT_ROOM;
  543. parms.requiredSpaceInBytes = discSwapStateMgr->GetRequiredStorageBytes();
  544. } else if ( discSwapError == idDiscSwapStateManager::DSSE_CORRECT_DISC_ALREADY ) {
  545. parms.errorCode = SAVEGAME_E_NONE;
  546. } else if ( discSwapError != idDiscSwapStateManager::DSSE_OK ) {
  547. parms.errorCode = SAVEGAME_E_DISC_SWAP;
  548. }
  549. if ( parms.errorCode == SAVEGAME_E_UNKNOWN ) {
  550. parms.errorCode = SAVEGAME_E_DISC_SWAP;
  551. }
  552. #endif
  553. return ( parms.GetError() == SAVEGAME_E_NONE );
  554. }
  555. /*
  556. ========================
  557. idSessionLocal::LoadGameCheckDescriptionFile
  558. ========================
  559. */
  560. bool idSessionLocal::LoadGameCheckDescriptionFile( idSaveLoadParms & parms ) {
  561. idFile_SaveGame ** detailsFile = FindFromGenericPtr( parms.files, SAVEGAME_DETAILS_FILENAME );
  562. if ( detailsFile == NULL ) {
  563. parms.errorCode = SAVEGAME_E_FILE_NOT_FOUND;
  564. return false;
  565. }
  566. assert( *detailsFile != NULL );
  567. (*detailsFile)->MakeReadOnly();
  568. if ( !SavegameReadDetailsFromFile( *detailsFile, parms.description ) ) {
  569. parms.errorCode = SAVEGAME_E_CORRUPTED;
  570. } else {
  571. if ( parms.description.GetSaveVersion() > BUILD_NUMBER ) {
  572. parms.errorCode = SAVEGAME_E_INCOMPATIBLE_NEWER_VERSION;
  573. }
  574. }
  575. return ( parms.GetError() == SAVEGAME_E_NONE );
  576. }
  577. #pragma region COMMANDS
  578. /*
  579. ================================================================================================
  580. COMMANDS
  581. ================================================================================================
  582. */
  583. CONSOLE_COMMAND( testSavegameDeleteAll, "delete all savegames without confirmation", 0 ) {
  584. if ( session == NULL ) {
  585. idLib::Printf( "Invalid session.\n" );
  586. return;
  587. }
  588. idSaveLoadParms parms;
  589. parms.SetDefaults();
  590. parms.mode = SAVEGAME_MBF_DELETE_ALL_FOLDERS | SAVEGAME_MBF_NO_COMPRESS;
  591. Sys_ExecuteSavegameCommandAsync( &parms );
  592. parms.callbackSignal.Wait();
  593. idLib::Printf( "Completed process.\n" );
  594. idLib::Printf( "Error = 0x%08X, %s\n", parms.GetError(), GetSaveGameErrorString( parms.GetError() ).c_str() );
  595. }
  596. CONSOLE_COMMAND( testSavegameDelete, "deletes a savegames without confirmation", 0 ) {
  597. if ( session == NULL ) {
  598. idLib::Printf( "Invalid session.\n" );
  599. return;
  600. }
  601. if ( args.Argc() != 2 ) {
  602. idLib::Printf( "Usage: testSavegameDelete <folder (without 'GAMES-')>\n" );
  603. return;
  604. }
  605. idStr folder = args.Argv( 1 );
  606. idSaveGameProcessorDelete testDeleteSaveGamesProc;
  607. if ( testDeleteSaveGamesProc.InitDelete( folder ) ) {
  608. session->GetSaveGameManager().ExecuteProcessorAndWait( &testDeleteSaveGamesProc );
  609. }
  610. idLib::Printf( "Completed process.\n" );
  611. idLib::Printf( "Error = 0x%08X, %s\n", testDeleteSaveGamesProc.GetParms().GetError(), GetSaveGameErrorString( testDeleteSaveGamesProc.GetParms().GetError() ).c_str() );
  612. }
  613. CONSOLE_COMMAND( testSavegameEnumerateFiles, "enumerates all the files in a folder (blank for 'current slot' folder, use 'autosave' for the autosave slot)", 0 ) {
  614. if ( session == NULL ) {
  615. idLib::Printf( "Invalid session.\n" );
  616. return;
  617. }
  618. idStr folder = session->GetCurrentSaveSlot();
  619. if ( args.Argc() > 1 ) {
  620. folder = args.Argv( 1 );
  621. }
  622. idLib::Printf( "Testing folder: %s\n\n", folder.c_str() );
  623. idSaveLoadParms parms;
  624. parms.SetDefaults();
  625. parms.mode = SAVEGAME_MBF_ENUMERATE_FILES;
  626. // Platform-specific implementation
  627. // This will start a worker thread for async operation.
  628. // It will always signal when it's completed.
  629. Sys_ExecuteSavegameCommandAsync( &parms );
  630. parms.callbackSignal.Wait();
  631. for ( int i = 0; i < parms.files.Num(); i++ ) {
  632. idLib::Printf( S_COLOR_YELLOW "\t%d: %s\n" S_COLOR_DEFAULT, i, parms.files[i]->GetName() );
  633. }
  634. }
  635. /*
  636. ========================
  637. OutputDetailList
  638. ========================
  639. */
  640. void OutputDetailList( const saveGameDetailsList_t & savegameList ) {
  641. for ( int i = 0; i < savegameList.Num(); ++i ) {
  642. idLib::Printf( S_COLOR_YELLOW "\t%s - %s\n" S_COLOR_DEFAULT
  643. "\t\tMap: %s\n"
  644. "\t\tTime: %s\n",
  645. savegameList[i].slotName.c_str(),
  646. savegameList[i].damaged ? S_COLOR_RED "CORRUPT" : S_COLOR_GREEN "OK",
  647. savegameList[i].damaged ? "?" : savegameList[i].descriptors.GetString( SAVEGAME_DETAIL_FIELD_MAP, "" ),
  648. Sys_TimeStampToStr( savegameList[i].date )
  649. );
  650. }
  651. }
  652. CONSOLE_COMMAND( testSavegameEnumerate, "enumerates the savegames available", 0 ) {
  653. if ( session == NULL ) {
  654. idLib::Printf( "Invalid session.\n" );
  655. return;
  656. }
  657. saveGameHandle_t handle = session->EnumerateSaveGamesSync();
  658. if ( handle == 0 ) {
  659. idLib::Printf( "Error enumerating.\n" );
  660. return;
  661. }
  662. const saveGameDetailsList_t gameList = session->GetSaveGameManager().GetEnumeratedSavegames();
  663. idLib::Printf( "Savegames found: %d\n\n", gameList.Num() );
  664. OutputDetailList( gameList );
  665. }
  666. CONSOLE_COMMAND( testSaveGameCheck, "tests existence of savegame", 0 ) {
  667. bool exists;
  668. bool autosaveExists;
  669. Sys_SaveGameCheck( exists, autosaveExists );
  670. idLib::Printf( "Savegame check: exists = %d, autosaveExists = %d\n", exists, autosaveExists );
  671. }
  672. CONSOLE_COMMAND( testSaveGameOutputEnumeratedSavegames, "outputs the list of savegames already enumerated, this does not re-enumerate", 0 ) {
  673. if ( session == NULL ) {
  674. idLib::Printf( "Invalid session.\n" );
  675. return;
  676. }
  677. const saveGameDetailsList_t & savegames = session->GetSaveGameManager().GetEnumeratedSavegames();
  678. OutputDetailList( savegames );
  679. }
  680. CONSOLE_COMMAND( testSavegameGetCurrentSlot, "returns the current slot in use", 0 ) {
  681. if ( session == NULL ) {
  682. idLib::Printf( "Invalid session.\n" );
  683. return;
  684. }
  685. idLib::Printf( "Current slot: %s\n", session->GetCurrentSaveSlot() );
  686. }
  687. CONSOLE_COMMAND( testSavegameSetCurrentSlot, "returns the current slot in use", 0 ) {
  688. if ( session == NULL ) {
  689. idLib::Printf( "Invalid session.\n" );
  690. return;
  691. }
  692. if ( args.Argc() != 2 ) {
  693. idLib::Printf( "Usage: testSavegameSetCurrentSlot name\n" );
  694. return;
  695. }
  696. const char * slot = args.Argv( 1 );
  697. session->SetCurrentSaveSlot( slot );
  698. idLib::Printf( "Current slot: %s\n", session->GetCurrentSaveSlot() );
  699. }
  700. CONSOLE_COMMAND( savegameSetErrorBit, "Allows you to set savegame_error by bit instead of integer value", 0 ) {
  701. int bit = atoi( args.Argv( 1 ) );
  702. savegame_error.SetInteger( savegame_error.GetInteger() | ( 1 << bit ) );
  703. }
  704. #pragma endregion