DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }