sys_profile.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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. #define SAVEGAME_PROFILE_FILENAME "profile.bin"
  23. idCVar profile_verbose( "profile_verbose", "0", CVAR_BOOL, "Turns on debug spam for profiles" );
  24. /*
  25. ================================================
  26. idProfileMgr
  27. ================================================
  28. */
  29. /*
  30. ========================
  31. idProfileMgr
  32. ========================
  33. */
  34. idProfileMgr::idProfileMgr() :
  35. profileSaveProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorSaveProfile ),
  36. profileLoadProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorLoadProfile ),
  37. profile( NULL ),
  38. handle( 0 ) {
  39. }
  40. /*
  41. ================================================
  42. ~idProfileMgr
  43. ================================================
  44. */
  45. idProfileMgr::~idProfileMgr() {
  46. }
  47. /*
  48. ========================
  49. idProfileMgr::Init
  50. ========================
  51. */
  52. void idProfileMgr::Init( idLocalUser * user_ ) {
  53. user = user_;
  54. handle = 0;
  55. }
  56. /*
  57. ========================
  58. idProfileMgr::Pump
  59. ========================
  60. */
  61. void idProfileMgr::Pump() {
  62. // profile can be NULL if we forced the user to register as in the case of map-ing into a level from the press start screen
  63. if ( profile == NULL ) {
  64. return;
  65. }
  66. // See if we are done with saving/loading the profile
  67. bool saving = profile->GetState() == idPlayerProfile::SAVING;
  68. bool loading = profile->GetState() == idPlayerProfile::LOADING;
  69. if ( ( saving || loading ) && session->IsSaveGameCompletedFromHandle( handle ) ) {
  70. profile->SetState( idPlayerProfile::IDLE );
  71. if ( saving ) {
  72. // Done saving
  73. } else if ( loading ) {
  74. // Done loading
  75. const idSaveLoadParms & parms = profileLoadProcessor->GetParms();
  76. if ( parms.GetError() == SAVEGAME_E_FOLDER_NOT_FOUND || parms.GetError() == SAVEGAME_E_FILE_NOT_FOUND ) {
  77. profile->SaveSettings( true );
  78. } else if ( parms.GetError() == SAVEGAME_E_CORRUPTED ) {
  79. idLib::Warning( "Profile corrupt, creating a new one..." );
  80. common->Dialog().AddDialog( GDM_CORRUPT_PROFILE, DIALOG_CONTINUE, NULL, NULL, false );
  81. profile->SetDefaults();
  82. profile->SaveSettings( true );
  83. } else if ( parms.GetError() != SAVEGAME_E_NONE ) {
  84. profile->SetState( idPlayerProfile::ERR );
  85. }
  86. session->OnLocalUserProfileLoaded( user );
  87. }
  88. } else if ( saving || loading ) {
  89. return;
  90. }
  91. // See if we need to save/load the profile
  92. if ( profile->GetRequestedState() == idPlayerProfile::SAVE_REQUESTED && profile->IsDirty() ) {
  93. profile->MarkDirty( false );
  94. SaveSettingsAsync();
  95. // Syncs the steam data
  96. //session->StoreStats();
  97. profile->SetRequestedState( idPlayerProfile::IDLE );
  98. } else if ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED ) {
  99. LoadSettingsAsync();
  100. profile->SetRequestedState( idPlayerProfile::IDLE );
  101. }
  102. }
  103. /*
  104. ========================
  105. idProfileMgr::GetProfile
  106. ========================
  107. */
  108. idPlayerProfile * idProfileMgr::GetProfile() {
  109. assert( user != NULL );
  110. if ( profile == NULL ) {
  111. // Lazy instantiation
  112. // Create a new profile
  113. profile = idPlayerProfile::CreatePlayerProfile( user->GetInputDevice() );
  114. if ( profile == NULL ) {
  115. return NULL;
  116. }
  117. }
  118. bool loading = ( profile->GetState() == idPlayerProfile::LOADING ) || ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED );
  119. if ( loading ) {
  120. return NULL;
  121. }
  122. return profile;
  123. }
  124. /*
  125. ========================
  126. idProfileMgr::SaveSettingsAsync
  127. ========================
  128. */
  129. void idProfileMgr::SaveSettingsAsync() {
  130. if ( !saveGame_enable.GetBool() ) {
  131. idLib::Warning( "Skipping profile save because saveGame_enable = 0" );
  132. }
  133. if ( GetProfile() != NULL ) {
  134. // Issue the async save...
  135. if ( profileSaveProcessor->InitSaveProfile( profile, "" ) ) {
  136. profileSaveProcessor->AddCompletedCallback( MakeCallback( this, &idProfileMgr::OnSaveSettingsCompleted, &profileSaveProcessor->GetParmsNonConst() ) );
  137. handle = session->GetSaveGameManager().ExecuteProcessor( profileSaveProcessor.get() );
  138. profile->SetState( idPlayerProfile::SAVING );
  139. }
  140. } else {
  141. idLib::Warning( "Not saving profile, profile is NULL." );
  142. }
  143. }
  144. /*
  145. ========================
  146. idProfileMgr::LoadSettingsAsync
  147. ========================
  148. */
  149. void idProfileMgr::LoadSettingsAsync() {
  150. if ( profile != NULL && saveGame_enable.GetBool() ) {
  151. if ( profileLoadProcessor->InitLoadProfile( profile, "" ) ) {
  152. // Skip the not found error because this might be the first time to play the game!
  153. profileLoadProcessor->SetSkipSystemErrorDialogMask( SAVEGAME_E_FOLDER_NOT_FOUND | SAVEGAME_E_FILE_NOT_FOUND );
  154. profileLoadProcessor->AddCompletedCallback( MakeCallback( this, &idProfileMgr::OnLoadSettingsCompleted, &profileLoadProcessor->GetParmsNonConst() ) );
  155. handle = session->GetSaveGameManager().ExecuteProcessor( profileLoadProcessor.get() );
  156. profile->SetState( idPlayerProfile::LOADING );
  157. }
  158. } else {
  159. // If not able to save the profile, just change the state and leave
  160. if ( profile == NULL ) {
  161. idLib::Warning( "Not loading profile, profile is NULL." );
  162. }
  163. if ( !saveGame_enable.GetBool() ) {
  164. idLib::Warning( "Skipping profile load because saveGame_enable = 0" );
  165. }
  166. }
  167. }
  168. /*
  169. ========================
  170. idProfileMgr::OnLoadSettingsCompleted
  171. ========================
  172. */
  173. void idProfileMgr::OnLoadSettingsCompleted( idSaveLoadParms * parms ) {
  174. // Don't process if error already detected
  175. if ( parms->errorCode != SAVEGAME_E_NONE ) {
  176. return;
  177. }
  178. // Serialize the loaded profile
  179. idFile_SaveGame ** profileFileContainer = FindFromGenericPtr( parms->files, SAVEGAME_PROFILE_FILENAME );
  180. idFile_SaveGame * profileFile = profileFileContainer == NULL ? NULL : *profileFileContainer;
  181. bool foundProfile = profileFile != NULL && profileFile->Length() > 0;
  182. if ( foundProfile ) {
  183. idTempArray< byte > buffer( MAX_PROFILE_SIZE );
  184. // Serialize settings from this buffer
  185. profileFile->MakeReadOnly();
  186. unsigned int originalChecksum;
  187. profileFile->ReadBig( originalChecksum );
  188. int dataLength = profileFile->Length() - (int)sizeof( originalChecksum );
  189. profileFile->ReadBigArray( buffer.Ptr(), dataLength );
  190. // Validate the checksum before we let the game serialize the settings
  191. unsigned int checksum = MD5_BlockChecksum( buffer.Ptr(), dataLength );
  192. if ( originalChecksum != checksum ) {
  193. idLib::Warning( "Checksum: 0x%08x, originalChecksum: 0x%08x, size = %d", checksum, originalChecksum, dataLength );
  194. parms->errorCode = SAVEGAME_E_CORRUPTED;
  195. } else {
  196. idBitMsg msg;
  197. msg.InitRead( buffer.Ptr(), (int)buffer.Size() );
  198. idSerializer ser( msg, false );
  199. if ( !profile->Serialize( ser ) ) {
  200. parms->errorCode = SAVEGAME_E_CORRUPTED;
  201. }
  202. }
  203. } else {
  204. parms->errorCode = SAVEGAME_E_FILE_NOT_FOUND;
  205. }
  206. }
  207. /*
  208. ========================
  209. idProfileMgr::OnSaveSettingsCompleted
  210. ========================
  211. */
  212. void idProfileMgr::OnSaveSettingsCompleted( idSaveLoadParms * parms ) {
  213. common->Dialog().ShowSaveIndicator( false );
  214. if ( parms->GetError() != SAVEGAME_E_NONE ) {
  215. common->Dialog().AddDialog( GDM_PROFILE_SAVE_ERROR, DIALOG_CONTINUE, NULL, NULL, false );
  216. }
  217. if ( game ) {
  218. game->Shell_UpdateSavedGames();
  219. }
  220. }
  221. /*
  222. ================================================
  223. idSaveGameProcessorSaveProfile
  224. ================================================
  225. */
  226. /*
  227. ========================
  228. idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile
  229. ========================
  230. */
  231. idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile() {
  232. profileFile = NULL;
  233. profile = NULL;
  234. }
  235. /*
  236. ========================
  237. idSaveGameProcessorSaveProfile::InitSaveProfile
  238. ========================
  239. */
  240. bool idSaveGameProcessorSaveProfile::InitSaveProfile( idPlayerProfile * profile_, const char * folder ) {
  241. // Serialize the profile and pass a file to the processor
  242. profileFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_PROFILE_FILENAME, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE );
  243. profileFile->MakeWritable();
  244. profileFile->SetMaxLength( MAX_PROFILE_SIZE );
  245. // Create a serialization object and let the game serialize the settings into the buffer
  246. const int serializeSize = MAX_PROFILE_SIZE - 8; // -8 for checksum (all platforms) and length (on 360)
  247. idTempArray< byte > buffer( serializeSize );
  248. idBitMsg msg;
  249. msg.InitWrite( buffer.Ptr(), serializeSize );
  250. idSerializer ser( msg, true );
  251. profile_->Serialize( ser );
  252. // Get and write the checksum & length first
  253. unsigned int checksum = MD5_BlockChecksum( msg.GetReadData(), msg.GetSize() );
  254. profileFile->WriteBig( checksum );
  255. idLib::PrintfIf( profile_verbose.GetBool(), "checksum: 0x%08x, length: %d\n", checksum, msg.GetSize() );
  256. // Add data to the file and prepare for save
  257. profileFile->Write( msg.GetReadData(), msg.GetSize() );
  258. profileFile->MakeReadOnly();
  259. saveFileEntryList_t files;
  260. files.Append( profileFile );
  261. idSaveGameDetails description;
  262. if ( !idSaveGameProcessorSaveFiles::InitSave( folder, files, description, idSaveGameManager::PACKAGE_PROFILE ) ) {
  263. return false;
  264. }
  265. profile = profile_;
  266. return true;
  267. }
  268. /*
  269. ========================
  270. idSaveGameProcessorSaveProfile::Process
  271. ========================
  272. */
  273. bool idSaveGameProcessorSaveProfile::Process() {
  274. // Files already setup for save, just execute as normal files
  275. return idSaveGameProcessorSaveFiles::Process();
  276. }
  277. /*
  278. ================================================
  279. idSaveGameProcessorLoadProfile
  280. ================================================
  281. */
  282. /*
  283. ========================
  284. idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile
  285. ========================
  286. */
  287. idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile() {
  288. profileFile = NULL;
  289. profile = NULL;
  290. }
  291. /*
  292. ========================
  293. idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile
  294. ========================
  295. */
  296. idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile() {
  297. }
  298. /*
  299. ========================
  300. idSaveGameProcessorLoadProfile::InitLoadFiles
  301. ========================
  302. */
  303. bool idSaveGameProcessorLoadProfile::InitLoadProfile( idPlayerProfile * profile_, const char * folder_ ) {
  304. if ( !idSaveGameProcessor::Init() ) {
  305. return false;
  306. }
  307. parms.directory = AddSaveFolderPrefix( folder_, idSaveGameManager::PACKAGE_PROFILE );
  308. parms.description.slotName = folder_;
  309. parms.mode = SAVEGAME_MBF_LOAD;
  310. profileFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_PROFILE_FILENAME, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE );
  311. parms.files.Append( profileFile );
  312. profile = profile_;
  313. return true;
  314. }
  315. /*
  316. ========================
  317. idSaveGameProcessorLoadProfile::Process
  318. ========================
  319. */
  320. bool idSaveGameProcessorLoadProfile::Process() {
  321. return idSaveGameProcessorLoadFiles::Process();
  322. }
  323. /*
  324. ========================
  325. Sys_SaveGameProfileCheck
  326. ========================
  327. */
  328. bool Sys_SaveGameProfileCheck() {
  329. bool exists = false;
  330. const char * saveFolder = "savegame";
  331. if ( fileSystem->IsFolder( saveFolder, "fs_savePath" ) == FOLDER_YES ) {
  332. idFileList * files = fileSystem->ListFiles( saveFolder, SAVEGAME_PROFILE_FILENAME );
  333. const idStrList & fileList = files->GetList();
  334. for ( int i = 0; i < fileList.Num(); i++ ) {
  335. idStr filename = fileList[i];
  336. if ( filename == SAVEGAME_PROFILE_FILENAME ) {
  337. exists = true;
  338. break;
  339. }
  340. }
  341. fileSystem->FreeFileList( files );
  342. }
  343. return exists;
  344. }