PlayerProfile.cpp (17782B)
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 #include "PlayerProfile.h" 29 #include "PS3_Includes.h" 30 #include "PSN/PS3_Session.h" 31 32 const int32 FRAMEWORK_PROFILE_VER = 1; 33 34 35 // Store master volume settings in archived cvars, becausue we want them to apply 36 // even if a user isn't signed in. 37 // The range is from 0 to 15, which matches the setting in vanilla DOOM. 38 idCVar s_volume_sound( "s_volume_sound", "8", CVAR_ARCHIVE | CVAR_INTEGER, "sound volume", 0, 15 ); 39 idCVar s_volume_midi( "s_volume_midi", "8", CVAR_ARCHIVE | CVAR_INTEGER, "music volume", 0, 15 ); 40 41 42 43 /* 44 ================================================ 45 idProfileMgr 46 ================================================ 47 */ 48 49 /* 50 ======================== 51 idProfileMgr 52 ======================== 53 */ 54 idProfileMgr::idProfileMgr() : 55 profileSaveProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorSaveProfile ), 56 profileLoadProcessor( new (TAG_SAVEGAMES) idSaveGameProcessorLoadProfile ), 57 profile( NULL ), 58 handle( 0 ) { 59 } 60 61 62 /* 63 ================================================ 64 ~idProfileMgr 65 ================================================ 66 */ 67 idProfileMgr::~idProfileMgr() { 68 delete profileSaveProcessor; 69 profileSaveProcessor = NULL; 70 71 delete profileLoadProcessor; 72 profileLoadProcessor = NULL; 73 } 74 75 /* 76 ======================== 77 idProfileMgr::Init 78 ======================== 79 */ 80 void idProfileMgr::Init( idPlayerProfile * profile_ ) { 81 profile = profile_; 82 handle = 0; 83 } 84 85 /* 86 ======================== 87 idProfileMgr::Pump 88 ======================== 89 */ 90 void idProfileMgr::Pump() { 91 // 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 92 if ( profile == NULL ) { 93 return; 94 } 95 96 // See if we are done with saving/loading the profile 97 bool saving = profile->GetState() == idPlayerProfile::SAVING; 98 bool loading = profile->GetState() == idPlayerProfile::LOADING; 99 if ( ( saving || loading ) && psn_session->GetSaveGameManager()->IsSaveGameCompletedFromHandle( handle ) ) { 100 profile->SetState( idPlayerProfile::IDLE ); 101 102 if ( saving ) { 103 // Done saving 104 } else if ( loading ) { 105 // Done loading 106 const idSaveLoadParms & parms = profileLoadProcessor->GetParms(); 107 if ( parms.GetError() == SAVEGAME_E_FOLDER_NOT_FOUND || parms.GetError() == SAVEGAME_E_FILE_NOT_FOUND ) { 108 profile->SaveSettings(); 109 } else if ( parms.GetError() != SAVEGAME_E_NONE ) { 110 profile->SetState( idPlayerProfile::ERR ); 111 } 112 } 113 } 114 115 // See if we need to save/load the profile 116 if ( profile->GetRequestedState() == idPlayerProfile::SAVE_REQUESTED ) { 117 SaveSettings(); 118 profile->SetRequestedState( idPlayerProfile::IDLE ); 119 } else if ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED ) { 120 LoadSettings(); 121 profile->SetRequestedState( idPlayerProfile::IDLE ); 122 } 123 } 124 125 /* 126 ======================== 127 idProfileMgr::GetProfile 128 ======================== 129 */ 130 idPlayerProfile * idProfileMgr::GetProfile() { 131 if ( profile == NULL ) { 132 return NULL; 133 } 134 135 bool loading = ( profile->GetState() == idPlayerProfile::LOADING ) || ( profile->GetRequestedState() == idPlayerProfile::LOAD_REQUESTED ); 136 if ( loading ) { 137 return NULL; 138 } 139 140 return profile; 141 } 142 143 /* 144 ======================== 145 idProfileMgr::SaveSettings 146 ======================== 147 */ 148 void idProfileMgr::SaveSettings() { 149 if ( profile != NULL && saveGame_enable.GetBool() ) { 150 // Issue the async save... 151 if ( profileSaveProcessor->InitSaveProfile( profile, "" ) ) { 152 handle = psn_session->GetSaveGameManager()->ExecuteProcessor( profileSaveProcessor ); 153 profile->SetState( idPlayerProfile::SAVING ); 154 } 155 } else { 156 // If not able to save the profile, just change the state and leave 157 if ( profile == NULL ) { 158 idLib::Warning( "Not saving profile, profile is NULL." ); 159 } 160 if ( !saveGame_enable.GetBool() ) { 161 idLib::Warning( "Skipping profile save because saveGame_enable = 0" ); 162 } 163 } 164 } 165 166 /* 167 ======================== 168 idProfileMgr::LoadSettings 169 ======================== 170 */ 171 void idProfileMgr::LoadSettings() { 172 if ( profile != NULL && saveGame_enable.GetBool() ) { 173 if ( profileLoadProcessor->InitLoadProfile( profile, "" ) ) { 174 // Skip the not found error because this might be the first time to play the game! 175 profileLoadProcessor->SetSkipSystemErrorDialogMask( SAVEGAME_E_FOLDER_NOT_FOUND | SAVEGAME_E_FILE_NOT_FOUND ); 176 177 handle = psn_session->GetSaveGameManager()->ExecuteProcessor( profileLoadProcessor ); 178 profile->SetState( idPlayerProfile::LOADING ); 179 } 180 } else { 181 // If not able to save the profile, just change the state and leave 182 if ( profile == NULL ) { 183 idLib::Warning( "Not loading profile, profile is NULL." ); 184 } 185 if ( !saveGame_enable.GetBool() ) { 186 idLib::Warning( "Skipping profile load because saveGame_enable = 0" ); 187 } 188 } 189 } 190 191 /* 192 ================================================ 193 idSaveGameProcessorSaveProfile 194 ================================================ 195 */ 196 197 /* 198 ======================== 199 idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile 200 ======================== 201 */ 202 idSaveGameProcessorSaveProfile::idSaveGameProcessorSaveProfile() { 203 profileFile = NULL; 204 profile = NULL; 205 } 206 207 /* 208 ======================== 209 idSaveGameProcessorSaveProfile::InitSaveProfile 210 ======================== 211 */ 212 bool idSaveGameProcessorSaveProfile::InitSaveProfile( idPlayerProfile * profile_, const char * folder ) { 213 214 // Serialize the profile and pass a file to the processor 215 profileFile = new (TAG_SAVEGAMES) idFile_Memory( SAVEGAME_PROFILE_FILENAME ); 216 profileFile->MakeWritable(); 217 profileFile->SetMaxLength( MAX_PROFILE_SIZE ); 218 219 idTempArray< byte > buffer( MAX_PROFILE_SIZE ); 220 idBitMsg msg; 221 msg.InitWrite( buffer.Ptr(), MAX_PROFILE_SIZE ); 222 idSerializer ser( msg, true ); 223 profile_->SerializeSettings( ser ); 224 225 profileFile->Write( msg.GetReadData(), msg.GetSize() ); 226 profileFile->MakeReadOnly(); 227 228 idList< idSaveFileEntry > files; 229 files.Append( idSaveFileEntry( profileFile, SAVEGAMEFILE_BINARY | SAVEGAMEFILE_AUTO_DELETE, SAVEGAME_PROFILE_FILENAME ) ); 230 231 idSaveGameDetails description; 232 if ( !idSaveGameProcessor::Init() ) { 233 return false; 234 } 235 236 if ( files.Num() == 0 ) { 237 idLib::Warning( "No files to save." ); 238 return false; 239 } 240 241 // Setup save system 242 parms.directory = AddSaveFolderPrefix( folder, idSaveGameManager::PACKAGE_PROFILE ); 243 parms.mode = SAVEGAME_MBF_SAVE | SAVEGAME_MBF_HIDDEN; // do NOT delete the existing files 244 parms.saveFileType = SAVEFILE_TYPE_AUTO; 245 for ( int i = 0; i < files.Num(); ++i ) { 246 parms.files.Append( files[i] ); 247 } 248 249 250 description.title = idLocalization::GetString( "#str_savegame_title" ); 251 description.subTitle = idLocalization::GetString( "#str_savegame_profile_heading" ); 252 description.summary = idLocalization::GetString( "#str_savegame_profile_desc" ); 253 254 255 // Add static image as the thumbnail 256 staticScreenshotFile = new (TAG_SAVEGAMES) idFile_Memory( "image" ); 257 258 // Open up the Image file and Make it a memory file. 259 void* thumbImage = NULL; 260 int imagesize = fileSystem->ReadFile( "base/textures/PROFILE.PNG", &thumbImage ); // This file lives at USRData.. i think. 261 staticScreenshotFile->MakeWritable(); 262 staticScreenshotFile->Write( thumbImage, imagesize ); 263 staticScreenshotFile->MakeReadOnly(); 264 265 parms.files.Append( idSaveFileEntry( staticScreenshotFile, SAVEGAMEFILE_THUMB, "image" ) ); 266 fileSystem->FreeFile( thumbImage ); 267 268 269 this->parms.description = description; 270 parms.description.slotName = folder; 271 272 273 274 // TODO:KC - what was the purpose of this? 275 // JAF idKeyInput::SetUserDeviceNumForBind( profile_->GetDeviceNumForProfile() ); 276 277 profile = profile_; 278 279 return true; 280 } 281 282 /* 283 ======================== 284 idSaveGameProcessorSaveProfile::Process 285 ======================== 286 */ 287 bool idSaveGameProcessorSaveProfile::Process() { 288 // Files already setup for save, just execute as normal files 289 290 // Platform-specific implementation 291 // This will start a worker thread for async operation. 292 // It will always signal when it's completed. 293 Sys_ExecuteSavegameCommandAsync( &parms ); 294 295 return false; 296 } 297 298 /* 299 ================================================ 300 idSaveGameProcessorLoadProfile 301 ================================================ 302 */ 303 304 /* 305 ======================== 306 idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile 307 ======================== 308 */ 309 idSaveGameProcessorLoadProfile::idSaveGameProcessorLoadProfile() { 310 profileFile = NULL; 311 profile = NULL; 312 } 313 314 /* 315 ======================== 316 idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile 317 ======================== 318 */ 319 idSaveGameProcessorLoadProfile::~idSaveGameProcessorLoadProfile() { 320 } 321 322 /* 323 ======================== 324 idSaveGameProcessorLoadProfile::InitLoadFiles 325 ======================== 326 */ 327 bool idSaveGameProcessorLoadProfile::InitLoadProfile( idPlayerProfile * profile_, const char * folder_ ) { 328 if ( !idSaveGameProcessor::Init() ) { 329 return false; 330 } 331 332 parms.directory = AddSaveFolderPrefix( folder_, idSaveGameManager::PACKAGE_PROFILE ); 333 parms.description.slotName = folder_; 334 parms.mode = SAVEGAME_MBF_LOAD | SAVEGAME_MBF_HIDDEN; 335 parms.saveFileType = SAVEFILE_TYPE_AUTO; 336 337 profileFile = new (TAG_SAVEGAMES) idFile_Memory( SAVEGAME_PROFILE_FILENAME ); 338 parms.files.Append( idSaveFileEntry( profileFile, SAVEGAMEFILE_BINARY, SAVEGAME_PROFILE_FILENAME ) ); 339 340 profile = profile_; 341 342 return true; 343 } 344 345 /* 346 ======================== 347 idSaveGameProcessorLoadProfile::Process 348 ======================== 349 */ 350 bool idSaveGameProcessorLoadProfile::Process() { 351 Sys_ExecuteSavegameCommandAsync( &parms ); 352 353 return false; 354 } 355 356 /* 357 ======================== 358 idSaveGameProcessorLoadProfile::PostProcess 359 ======================== 360 */ 361 void idSaveGameProcessorLoadProfile::PostProcess() { 362 // Serialize the loaded profile 363 bool foundProfile = profileFile->Length() > 0; 364 365 if ( foundProfile ) { 366 idTempArray< byte> buffer( MAX_PROFILE_SIZE ); 367 368 // Serialize settings from this buffer 369 profileFile->MakeReadOnly(); 370 profileFile->ReadBigArray( buffer.Ptr(), profileFile->Length() ); 371 372 idBitMsg msg; 373 msg.InitRead( buffer.Ptr(), (int)buffer.Size() ); 374 idSerializer ser( msg, false ); 375 profile->SerializeSettings( ser ); 376 377 // JAF idKeyInput::SetUserDeviceNumForBind( profile->GetDeviceNumForProfile() ); 378 379 } else { 380 parms.errorCode = SAVEGAME_E_FILE_NOT_FOUND; 381 } 382 383 delete profileFile; 384 } 385 386 /* 387 ======================== 388 Contains data that needs to be saved out on a per player profile basis, global for the lifetime of the player so 389 the data can be shared across computers. 390 - HUD tint colors 391 - key bindings 392 - etc... 393 ======================== 394 */ 395 396 /* 397 ======================== 398 idPlayerProfile::idPlayerProfile 399 ======================== 400 */ 401 idPlayerProfile::idPlayerProfile() { 402 SetDefaults(); 403 404 // Don't have these in SetDefaults because they're used for state management and SetDefaults is called when 405 // loading the profile 406 state = IDLE; 407 requestedState = IDLE; 408 } 409 410 /* 411 ======================== 412 idPlayerProfile::SetDefaults 413 ======================== 414 */ 415 void idPlayerProfile::SetDefaults() { 416 417 achievementBits = 0; 418 seenInstallMessage = false; 419 stats.SetNum( MAX_PLAYER_PROFILE_STATS ); 420 for ( int i = 0; i < MAX_PLAYER_PROFILE_STATS; ++i ) { 421 stats[i].i = 0; 422 } 423 424 deviceNum = 0; 425 state = IDLE; 426 requestedState = IDLE; 427 frameScaleX = 0.85f; 428 frameScaleY = 0.85f; 429 } 430 431 /* 432 ======================== 433 idPlayerProfile::Init 434 ======================== 435 */ 436 void idPlayerProfile::Init() { 437 SetDefaults(); 438 } 439 440 /* 441 ======================== 442 idPlayerProfile::~idPlayerProfile 443 ======================== 444 */ 445 idPlayerProfile::~idPlayerProfile() { 446 } 447 448 /* 449 ======================== 450 idPlayerProfile::SerializeSettings 451 ======================== 452 */ 453 bool idPlayerProfile::SerializeSettings( idSerializer & ser ) { 454 int flags = cvarSystem->GetModifiedFlags(); 455 456 // Default to current tag/version 457 int32 tag = GetProfileTag(); 458 int32 version = FRAMEWORK_PROFILE_VER; 459 460 // Serialize tag/version 461 ser.SerializePacked( tag ); 462 if ( tag != GetProfileTag() ) { 463 idLib::Warning( "Profile tag did not match, profile will be re-initialized" ); 464 SetDefaults(); 465 SaveSettings(); // Flag the profile to save so we have the latest version stored 466 467 return false; 468 } 469 ser.SerializePacked( version ); 470 if ( version != FRAMEWORK_PROFILE_VER ) { 471 // For now, don't allow profiles with invalid versions load 472 // We could easily support old version by doing a few version checks below to pick and choose what we load as well. 473 idLib::Warning( "Profile version did not match. Profile will be replaced" ); 474 SetDefaults(); 475 SaveSettings(); // Flag the profile to save so we have the latest version stored 476 477 return false; 478 } 479 480 // Serialize audio settings 481 SERIALIZE_BOOL( ser, seenInstallMessage ); 482 483 // New setting to save to make sure that we have or haven't seen this achievement before used to pass TRC R149d 484 ser.Serialize( achievementBits ); 485 486 ser.Serialize( frameScaleX ); 487 ser.Serialize( frameScaleY ); 488 SERIALIZE_BOOL( ser, alwaysRun ); 489 490 491 // we save all the cvar-based settings in the profile even though some cvars are archived 492 // so that we are consistent and don't miss any or get affected when the archive flag is changed 493 SERIALIZE_CVAR_INT( ser, s_volume_sound ); 494 SERIALIZE_CVAR_INT( ser, s_volume_midi ); 495 496 // Don't trigger profile save due to modified archived cvars during profile load 497 cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE ); // must clear because set() is an OR operation, not assignment... 498 cvarSystem->SetModifiedFlags( flags ); 499 500 return true; 501 } 502 /* 503 ======================== 504 idPlayerProfile::GetLevel 505 ======================== 506 */ 507 int idPlayerProfile::GetLevel() const { 508 return 1; 509 } 510 511 /* 512 ======================== 513 idPlayerProfile::StatSetInt 514 ======================== 515 */ 516 void idPlayerProfile::StatSetInt( int s, int v ) { 517 stats[s].i = v; 518 } 519 520 /* 521 ======================== 522 idPlayerProfile::StatSetFloat 523 ======================== 524 */ 525 void idPlayerProfile::StatSetFloat( int s, float v ) { 526 stats[s].f = v; 527 } 528 529 /* 530 ======================== 531 idPlayerProfile::StatGetInt 532 ======================== 533 */ 534 int idPlayerProfile::StatGetInt( int s ) const { 535 return stats[s].i; 536 } 537 538 /* 539 ======================== 540 idPlayerProfile::StatGetFloat 541 ======================== 542 */ 543 float idPlayerProfile::StatGetFloat( int s ) const { 544 return stats[s].f; 545 } 546 547 /* 548 ======================== 549 idPlayerProfile::SaveSettings 550 ======================== 551 */ 552 void idPlayerProfile::SaveSettings() { 553 if ( state != SAVING ) { 554 if ( GetRequestedState() == IDLE ) { 555 SetRequestedState( SAVE_REQUESTED ); 556 } 557 } 558 } 559 560 /* 561 ======================== 562 idPlayerProfile::SaveSettings 563 ======================== 564 */ 565 void idPlayerProfile::LoadSettings() { 566 if ( state != LOADING ) { 567 if ( verify( GetRequestedState() == IDLE ) ) { 568 SetRequestedState( LOAD_REQUESTED ); 569 } 570 } 571 } 572 573 /* 574 ======================== 575 idPlayerProfile::SetAchievementBit 576 ======================== 577 */ 578 void idPlayerProfile::SetAchievementBit( const int id ) { 579 if ( id > 63 ) { 580 assert( false ); // FIXME: add another set of achievement bit flags 581 return; 582 } 583 584 achievementBits |= (int64)1 << id; 585 } 586 587 /* 588 ======================== 589 idPlayerProfile::ClearAchievementBit 590 ======================== 591 */ 592 void idPlayerProfile::ClearAchievementBit( const int id ) { 593 if ( id > 63 ) { 594 assert( false ); // FIXME: add another set of achievement bit flags 595 return; 596 } 597 598 achievementBits &= ~( (int64)1 << id ); 599 } 600 601 /* 602 ======================== 603 idPlayerProfile::GetAchievementBit 604 ======================== 605 */ 606 bool idPlayerProfile::GetAchievementBit( const int id ) const { 607 if ( id > 63 ) { 608 assert( false ); // FIXME: add another set of achievement bit flags 609 return false; 610 } 611 612 return ( achievementBits & (int64)1 << id ) != 0; 613 } 614 615 /* 616 ======================== 617 Returns the value stored in the music volume cvar. 618 ======================== 619 */ 620 int idPlayerProfile::GetMusicVolume() const { 621 return s_volume_midi.GetInteger(); 622 } 623 624 /* 625 ======================== 626 Returns the value stored in the sound volume cvar. 627 ======================== 628 */ 629 int idPlayerProfile::GetSoundVolume() const { 630 return s_volume_sound.GetInteger(); 631 } 632 633 /* 634 ======================== 635 Sets the music volume cvar. 636 ======================== 637 */ 638 void idPlayerProfile::SetMusicVolume( int volume ) { 639 s_volume_midi.SetInteger( volume ); 640 } 641 642 /* 643 ======================== 644 Sets the sound volume cvar. 645 ======================== 646 */ 647 void idPlayerProfile::SetSoundVolume( int volume ) { 648 s_volume_sound.SetInteger( volume ); 649 }