sys_profile.cpp (12796B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 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. 23 24 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. 25 26 =========================================================================== 27 */ 28 #pragma hdrstop 29 #include "../idlib/precompiled.h" 30 31 #define SAVEGAME_PROFILE_FILENAME "profile.bin" 32 33 idCVar profile_verbose( "profile_verbose", "0", CVAR_BOOL, "Turns on debug spam for profiles" ); 34 35 /* 36 ================================================ 37 idProfileMgr 38 ================================================ 39 */ 40 41 /* 42 ======================== 43 idProfileMgr 44 ======================== 45 */ 46 idProfileMgr::idProfileMgr() : 47 profileSaveProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorSaveProfile ), 48 profileLoadProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorLoadProfile ), 49 profile( NULL ), 50 handle( 0 ) { 51 } 52 53 54 /* 55 ================================================ 56 ~idProfileMgr 57 ================================================ 58 */ 59 idProfileMgr::~idProfileMgr() { 60 } 61 62 /* 63 ======================== 64 idProfileMgr::Init 65 ======================== 66 */ 67 void idProfileMgr::Init( idLocalUser * user_ ) { 68 user = user_; 69 handle = 0; 70 } 71 72 /* 73 ======================== 74 idProfileMgr::Pump 75 ======================== 76 */ 77 void idProfileMgr::Pump() { 78 // 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 79 if ( profile == NULL ) { 80 return; 81 } 82 83 // See if we are done with saving/loading the profile 84 bool saving = profile->GetState() == idPlayerProfile::SAVING; 85 bool loading = profile->GetState() == idPlayerProfile::LOADING; 86 if ( ( saving || loading ) && session->IsSaveGameCompletedFromHandle( handle ) ) { 87 profile->SetState( idPlayerProfile::IDLE ); 88 89 if ( saving ) { 90 // Done saving 91 } else if ( loading ) { 92 // Done loading 93 const idSaveLoadParms & parms = profileLoadProcessor->GetParms(); 94 if ( parms.GetError() == SAVEGAME_E_FOLDER_NOT_FOUND || parms.GetError() == SAVEGAME_E_FILE_NOT_FOUND ) { 95 profile->SaveSettings( true ); 96 } else if ( parms.GetError() == SAVEGAME_E_CORRUPTED ) { 97 idLib::Warning( "Profile corrupt, creating a new one..." ); 98 common->Dialog().AddDialog( GDM_CORRUPT_PROFILE, DIALOG_CONTINUE, NULL, NULL, false ); 99 profile->SetDefaults(); 100 profile->SaveSettings( true ); 101 } else if ( parms.GetError() != SAVEGAME_E_NONE ) { 102 profile->SetState( idPlayerProfile::ERR ); 103 } 104 105 session->OnLocalUserProfileLoaded( user ); 106 } 107 } else if ( saving || loading ) { 108 return; 109 } 110 111 // See if we need to save/load the profile 112 if ( profile->GetRequestedState() == idPlayerProfile::SAVE_REQUESTED && profile->IsDirty() ) { 113 profile->MarkDirty( false ); 114 SaveSettingsAsync(); 115 // Syncs the steam data 116 //session->StoreStats(); 117 profile->SetRequestedState( idPlayerProfile::IDLE ); 118 } else if ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED ) { 119 LoadSettingsAsync(); 120 profile->SetRequestedState( idPlayerProfile::IDLE ); 121 } 122 } 123 124 /* 125 ======================== 126 idProfileMgr::GetProfile 127 ======================== 128 */ 129 idPlayerProfile * idProfileMgr::GetProfile() { 130 assert( user != NULL ); 131 if ( profile == NULL ) { 132 // Lazy instantiation 133 // Create a new profile 134 profile = idPlayerProfile::CreatePlayerProfile( user->GetInputDevice() ); 135 if ( profile == NULL ) { 136 return NULL; 137 } 138 } 139 140 bool loading = ( profile->GetState() == idPlayerProfile::LOADING ) || ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED ); 141 if ( loading ) { 142 return NULL; 143 } 144 145 return profile; 146 } 147 148 /* 149 ======================== 150 idProfileMgr::SaveSettingsAsync 151 ======================== 152 */ 153 void idProfileMgr::SaveSettingsAsync() { 154 if ( !saveGame_enable.GetBool() ) { 155 idLib::Warning( "Skipping profile save because saveGame_enable = 0" ); 156 } 157 158 if ( GetProfile() != NULL ) { 159 // Issue the async save... 160 if ( profileSaveProcessor->InitSaveProfile( profile, "" ) ) { 161 162 163 profileSaveProcessor->AddCompletedCallback( MakeCallback( this, &idProfileMgr::OnSaveSettingsCompleted, &profileSaveProcessor->GetParmsNonConst() ) ); 164 handle = session->GetSaveGameManager().ExecuteProcessor( profileSaveProcessor.get() ); 165 profile->SetState( idPlayerProfile::SAVING ); 166 167 } 168 } else { 169 idLib::Warning( "Not saving profile, profile is NULL." ); 170 } 171 } 172 173 /* 174 ======================== 175 idProfileMgr::LoadSettingsAsync 176 ======================== 177 */ 178 void idProfileMgr::LoadSettingsAsync() { 179 if ( profile != NULL && saveGame_enable.GetBool() ) { 180 if ( profileLoadProcessor->InitLoadProfile( profile, "" ) ) { 181 // Skip the not found error because this might be the first time to play the game! 182 profileLoadProcessor->SetSkipSystemErrorDialogMask( SAVEGAME_E_FOLDER_NOT_FOUND | SAVEGAME_E_FILE_NOT_FOUND ); 183 184 profileLoadProcessor->AddCompletedCallback( MakeCallback( this, &idProfileMgr::OnLoadSettingsCompleted, &profileLoadProcessor->GetParmsNonConst() ) ); 185 handle = session->GetSaveGameManager().ExecuteProcessor( profileLoadProcessor.get() ); 186 profile->SetState( idPlayerProfile::LOADING ); 187 188 189 } 190 } else { 191 // If not able to save the profile, just change the state and leave 192 if ( profile == NULL ) { 193 idLib::Warning( "Not loading profile, profile is NULL." ); 194 } 195 if ( !saveGame_enable.GetBool() ) { 196 idLib::Warning( "Skipping profile load because saveGame_enable = 0" ); 197 } 198 } 199 } 200 201 /* 202 ======================== 203 idProfileMgr::OnLoadSettingsCompleted 204 ======================== 205 */ 206 void idProfileMgr::OnLoadSettingsCompleted( idSaveLoadParms * parms ) { 207 208 209 210 211 212 // Don't process if error already detected 213 if ( parms->errorCode != SAVEGAME_E_NONE ) { 214 return; 215 } 216 217 // Serialize the loaded profile 218 idFile_SaveGame ** profileFileContainer = FindFromGenericPtr( parms->files, SAVEGAME_PROFILE_FILENAME ); 219 idFile_SaveGame * profileFile = profileFileContainer == NULL ? NULL : *profileFileContainer; 220 221 bool foundProfile = profileFile != NULL && profileFile->Length() > 0; 222 223 if ( foundProfile ) { 224 idTempArray< byte > buffer( MAX_PROFILE_SIZE ); 225 226 // Serialize settings from this buffer 227 profileFile->MakeReadOnly(); 228 unsigned int originalChecksum; 229 profileFile->ReadBig( originalChecksum ); 230 231 int dataLength = profileFile->Length() - (int)sizeof( originalChecksum ); 232 profileFile->ReadBigArray( buffer.Ptr(), dataLength ); 233 234 // Validate the checksum before we let the game serialize the settings 235 unsigned int checksum = MD5_BlockChecksum( buffer.Ptr(), dataLength ); 236 if ( originalChecksum != checksum ) { 237 idLib::Warning( "Checksum: 0x%08x, originalChecksum: 0x%08x, size = %d", checksum, originalChecksum, dataLength ); 238 parms->errorCode = SAVEGAME_E_CORRUPTED; 239 } else { 240 idBitMsg msg; 241 msg.InitRead( buffer.Ptr(), (int)buffer.Size() ); 242 idSerializer ser( msg, false ); 243 if ( !profile->Serialize( ser ) ) { 244 parms->errorCode = SAVEGAME_E_CORRUPTED; 245 } 246 } 247 248 } else { 249 parms->errorCode = SAVEGAME_E_FILE_NOT_FOUND; 250 } 251 } 252 253 /* 254 ======================== 255 idProfileMgr::OnSaveSettingsCompleted 256 ======================== 257 */ 258 void idProfileMgr::OnSaveSettingsCompleted( idSaveLoadParms * parms ) { 259 common->Dialog().ShowSaveIndicator( false ); 260 261 if ( parms->GetError() != SAVEGAME_E_NONE ) { 262 common->Dialog().AddDialog( GDM_PROFILE_SAVE_ERROR, DIALOG_CONTINUE, NULL, NULL, false ); 263 } 264 if ( game ) { 265 game->Shell_UpdateSavedGames(); 266 } 267 } 268 269 /* 270 ================================================ 271 idSaveGameProcessorSaveProfile 272 ================================================ 273 */ 274 275 /* 276 ======================== 277 idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile 278 ======================== 279 */ 280 idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile() { 281 profileFile = NULL; 282 profile = NULL; 283 284 } 285 286 /* 287 ======================== 288 idSaveGameProcessorSaveProfile::InitSaveProfile 289 ======================== 290 */ 291 bool idSaveGameProcessorSaveProfile::InitSaveProfile( idPlayerProfile * profile_, const char * folder ) { 292 // Serialize the profile and pass a file to the processor 293 profileFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_PROFILE_FILENAME, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE ); 294 profileFile->MakeWritable(); 295 profileFile->SetMaxLength( MAX_PROFILE_SIZE ); 296 297 // Create a serialization object and let the game serialize the settings into the buffer 298 const int serializeSize = MAX_PROFILE_SIZE - 8; // -8 for checksum (all platforms) and length (on 360) 299 idTempArray< byte > buffer( serializeSize ); 300 idBitMsg msg; 301 msg.InitWrite( buffer.Ptr(), serializeSize ); 302 idSerializer ser( msg, true ); 303 profile_->Serialize( ser ); 304 305 // Get and write the checksum & length first 306 unsigned int checksum = MD5_BlockChecksum( msg.GetReadData(), msg.GetSize() ); 307 profileFile->WriteBig( checksum ); 308 309 idLib::PrintfIf( profile_verbose.GetBool(), "checksum: 0x%08x, length: %d\n", checksum, msg.GetSize() ); 310 311 // Add data to the file and prepare for save 312 profileFile->Write( msg.GetReadData(), msg.GetSize() ); 313 profileFile->MakeReadOnly(); 314 315 saveFileEntryList_t files; 316 files.Append( profileFile ); 317 318 idSaveGameDetails description; 319 if ( !idSaveGameProcessorSaveFiles::InitSave( folder, files, description, idSaveGameManager::PACKAGE_PROFILE ) ) { 320 return false; 321 } 322 323 324 profile = profile_; 325 326 327 return true; 328 } 329 330 /* 331 ======================== 332 idSaveGameProcessorSaveProfile::Process 333 ======================== 334 */ 335 bool idSaveGameProcessorSaveProfile::Process() { 336 337 338 // Files already setup for save, just execute as normal files 339 return idSaveGameProcessorSaveFiles::Process(); 340 341 } 342 343 344 /* 345 ================================================ 346 idSaveGameProcessorLoadProfile 347 ================================================ 348 */ 349 350 /* 351 ======================== 352 idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile 353 ======================== 354 */ 355 idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile() { 356 profileFile = NULL; 357 profile = NULL; 358 359 } 360 361 /* 362 ======================== 363 idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile 364 ======================== 365 */ 366 idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile() { 367 } 368 369 /* 370 ======================== 371 idSaveGameProcessorLoadProfile::InitLoadFiles 372 ======================== 373 */ 374 bool idSaveGameProcessorLoadProfile::InitLoadProfile( idPlayerProfile * profile_, const char * folder_ ) { 375 if ( !idSaveGameProcessor::Init() ) { 376 return false; 377 } 378 379 parms.directory = AddSaveFolderPrefix( folder_, idSaveGameManager::PACKAGE_PROFILE ); 380 parms.description.slotName = folder_; 381 parms.mode = SAVEGAME_MBF_LOAD; 382 383 profileFile = new (TAG_SAVEGAMES) idFile_SaveGame( SAVEGAME_PROFILE_FILENAME, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE ); 384 parms.files.Append( profileFile ); 385 386 profile = profile_; 387 388 389 return true; 390 } 391 392 /* 393 ======================== 394 idSaveGameProcessorLoadProfile::Process 395 ======================== 396 */ 397 bool idSaveGameProcessorLoadProfile::Process() { 398 399 400 return idSaveGameProcessorLoadFiles::Process(); 401 402 } 403 404 405 /* 406 ======================== 407 Sys_SaveGameProfileCheck 408 ======================== 409 */ 410 bool Sys_SaveGameProfileCheck() { 411 bool exists = false; 412 const char * saveFolder = "savegame"; 413 414 if ( fileSystem->IsFolder( saveFolder, "fs_savePath" ) == FOLDER_YES ) { 415 idFileList * files = fileSystem->ListFiles( saveFolder, SAVEGAME_PROFILE_FILENAME ); 416 const idStrList & fileList = files->GetList(); 417 418 for ( int i = 0; i < fileList.Num(); i++ ) { 419 idStr filename = fileList[i]; 420 if ( filename == SAVEGAME_PROFILE_FILENAME ) { 421 exists = true; 422 break; 423 } 424 } 425 426 fileSystem->FreeFileList( files ); 427 } 428 429 return exists; 430 }