2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
2012-11-28 15:47:07 +00:00
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
2012-11-26 18:58:24 +00:00
Doom 3 BFG Edition Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code . If not , see < http : //www.gnu.org/licenses/>.
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 .
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 .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# pragma hdrstop
# include "../idlib/precompiled.h"
# include "sys_session_local.h"
idCVar saveGame_verbose ( " saveGame_verbose " , " 0 " , CVAR_BOOL | CVAR_ARCHIVE , " debug spam " ) ;
idCVar saveGame_checksum ( " saveGame_checksum " , " 1 " , CVAR_BOOL , " data integrity check " ) ;
idCVar saveGame_enable ( " saveGame_enable " , " 1 " , CVAR_BOOL , " are savegames enabled " ) ;
2012-11-28 15:47:07 +00:00
void Sys_ExecuteSavegameCommandAsyncImpl ( idSaveLoadParms * savegameParms ) ;
2012-11-26 18:58:24 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = =
Sys_ExecuteSavegameCommandAsync
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void Sys_ExecuteSavegameCommandAsync ( idSaveLoadParms * savegameParms )
{
if ( savegameParms = = NULL )
{
2012-11-26 18:58:24 +00:00
idLib : : Error ( " Programming Error with [%s] " , __FUNCTION__ ) ;
return ;
}
2012-11-28 15:47:07 +00:00
if ( ! saveGame_enable . GetBool ( ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " Savegames are disabled (saveGame_enable = 0). Skipping physical save to media. " ) ;
savegameParms - > errorCode = SAVEGAME_E_CANCELLED ;
savegameParms - > callbackSignal . Raise ( ) ;
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Sys_ExecuteSavegameCommandAsyncImpl ( savegameParms ) ;
}
# define ASSERT_ENUM_STRING_BITFIELD( string, index ) ( 1 / (int)!( string - ( 1 << index ) ) ) ? #string : ""
2012-11-28 15:47:07 +00:00
const char * saveGameErrorStrings [ SAVEGAME_E_NUM ] =
{
2012-11-26 18:58:24 +00:00
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_CANCELLED , 0 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_INSUFFICIENT_ROOM , 1 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_CORRUPTED , 2 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_UNABLE_TO_SELECT_STORAGE_DEVICE , 3 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_UNKNOWN , 4 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_INVALID_FILENAME , 5 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_STEAM_ERROR , 6 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_FOLDER_NOT_FOUND , 7 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_FILE_NOT_FOUND , 8 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_DLC_NOT_FOUND , 9 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_INVALID_USER , 10 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_PROFILE_TOO_BIG , 11 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_DISC_SWAP , 12 ) ,
ASSERT_ENUM_STRING_BITFIELD ( SAVEGAME_E_INCOMPATIBLE_NEWER_VERSION , 13 ) ,
} ;
2012-11-28 15:47:07 +00:00
CONSOLE_COMMAND ( savegamePrintErrors , " Prints error code corresponding to each bit " , 0 )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " Bit Description \n "
" --- ----------- \n " ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < SAVEGAME_E_BITS_USED ; i + + )
{
2012-11-26 18:58:24 +00:00
idLib : : Printf ( " %03d %s \n " , i , saveGameErrorStrings [ i ] ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
GetSaveGameErrorString
This returns a comma delimited string of all the errors within the bitfield . We don ' t ever expect more than one error ,
the errors are bitfields so that they can be used to mask which errors we want to handle in the engine or leave up to the
game .
2012-11-28 15:47:07 +00:00
Example :
2012-11-26 18:58:24 +00:00
SAVEGAME_E_LOAD , SAVEGAME_E_INVALID_FILENAME
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idStr GetSaveGameErrorString ( int errorMask )
{
2012-11-26 18:58:24 +00:00
idStr errorString ;
bool continueProcessing = errorMask > 0 ;
int localError = errorMask ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < SAVEGAME_E_NUM & & continueProcessing ; + + i )
{
2012-11-26 18:58:24 +00:00
int mask = ( 1 < < i ) ;
2012-11-28 15:47:07 +00:00
if ( localError & mask )
{
2012-11-26 18:58:24 +00:00
localError ^ = mask ; // turn off this error so we can quickly see if we are done
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
continueProcessing = localError > 0 ;
errorString . Append ( saveGameErrorStrings [ i ] ) ;
2012-11-28 15:47:07 +00:00
if ( continueProcessing )
{
2012-11-26 18:58:24 +00:00
errorString . Append ( " , " ) ;
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return errorString ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
GetSaveFolder
Prefixes help the PS3 filter what to show in lists
Files using the hidden prefix are not shown in the load game screen
Directory name for savegames , slot number or user - defined name appended to it
TRC R116 - PS3 folder must start with the product code
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
const idStr & GetSaveFolder ( idSaveGameManager : : packageType_t type )
{
2012-11-26 18:58:24 +00:00
static bool initialized = false ;
static idStrStatic < MAX_FOLDER_NAME_LENGTH > saveFolder [ idSaveGameManager : : PACKAGE_NUM ] ;
2012-11-28 15:47:07 +00:00
if ( ! initialized )
{
2012-11-26 18:58:24 +00:00
initialized = true ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
idStr ps3Header = " " ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
saveFolder [ idSaveGameManager : : PACKAGE_GAME ] . Format ( " %s%s " , ps3Header . c_str ( ) , SAVEGAME_GAME_DIRECTORY_PREFIX ) ;
saveFolder [ idSaveGameManager : : PACKAGE_PROFILE ] . Format ( " %s%s " , ps3Header . c_str ( ) , SAVEGAME_PROFILE_DIRECTORY_PREFIX ) ;
saveFolder [ idSaveGameManager : : PACKAGE_RAW ] . Format ( " %s%s " , ps3Header . c_str ( ) , SAVEGAME_RAW_DIRECTORY_PREFIX ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return saveFolder [ type ] ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idStr AddSaveFolderPrefix
input = RAGE_0
output = GAMES - RAGE_0
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idStr AddSaveFolderPrefix ( const char * folder , idSaveGameManager : : packageType_t type )
{
2012-11-26 18:58:24 +00:00
idStr dir = GetSaveFolder ( type ) ;
dir . Append ( folder ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return dir ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
RemoveSaveFolderPrefix
input = GAMES - RAGE_0
output = RAGE_0
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idStr RemoveSaveFolderPrefix ( const char * folder , idSaveGameManager : : packageType_t type )
{
2012-11-26 18:58:24 +00:00
idStr dir = folder ;
idStr prefix = GetSaveFolder ( type ) ;
dir . StripLeading ( prefix ) ;
return dir ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
bool SavegameReadDetailsFromFile
returns false when catastrophic error occurs , not when damaged
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool SavegameReadDetailsFromFile ( idFile * file , idSaveGameDetails & details )
{
2012-11-26 18:58:24 +00:00
details . damaged = false ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Read the DETAIL file for the enumerated data
2012-11-28 15:47:07 +00:00
if ( ! details . descriptors . ReadFromIniFile ( file ) )
{
2012-11-26 18:58:24 +00:00
details . damaged = true ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
bool ignoreChecksum = details . descriptors . GetBool ( " ignore_checksum " , false ) ;
2012-11-28 15:47:07 +00:00
if ( ! ignoreChecksum )
{
2012-11-26 18:58:24 +00:00
// Get the checksum from the dict
int readChecksum = details . descriptors . GetInt ( SAVEGAME_DETAIL_FIELD_CHECKSUM , 0 ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Calculate checksum
details . descriptors . Delete ( SAVEGAME_DETAIL_FIELD_CHECKSUM ) ;
2012-11-28 15:47:07 +00:00
int checksum = ( int ) details . descriptors . Checksum ( ) ;
if ( readChecksum = = 0 | | checksum ! = readChecksum )
{
2012-11-26 18:58:24 +00:00
details . damaged = true ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameDetails : : idSaveGameDetails
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSaveGameDetails : : idSaveGameDetails ( )
{
Clear ( ) ;
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameDetails : : Clear
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameDetails : : Clear ( )
{
2012-11-26 18:58:24 +00:00
descriptors . Clear ( ) ;
damaged = false ;
date = 0 ;
slotName [ 0 ] = NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : idSaveLoadParms
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSaveLoadParms : : idSaveLoadParms ( )
{
2012-11-26 18:58:24 +00:00
// These are not done when we set defaults because SetDefaults is called internally within the execution of the processor and
// these are set once and shouldn't be touched until the processor is re-initialized
cancelled = false ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Init ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : ~ idSaveLoadParms
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSaveLoadParms : : ~ idSaveLoadParms ( )
{
for ( int i = 0 ; i < files . Num ( ) ; + + i )
{
if ( files [ i ] - > type & SAVEGAMEFILE_AUTO_DELETE )
{
2012-11-26 18:58:24 +00:00
delete files [ i ] ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : ResetCancelled
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveLoadParms : : ResetCancelled ( )
{
2012-11-26 18:58:24 +00:00
cancelled = false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : Init
This should not touch anything statically created outside this class !
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveLoadParms : : Init ( )
{
2012-11-26 18:58:24 +00:00
files . Clear ( ) ;
mode = SAVEGAME_MBF_NONE ;
directory = " " ;
pattern = " " ;
postPattern = " " ;
requiredSpaceInBytes = 0 ;
description . Clear ( ) ;
detailList . Clear ( ) ;
callbackSignal . Clear ( ) ;
errorCode = SAVEGAME_E_NONE ;
inputDeviceId = - 1 ;
skipErrorDialogMask = 0 ;
// These are not done when we set defaults because SetDefaults is called internally within the execution of the processor and
// these are set once and shouldn't be touched until the processor is re-initialized
// cancelled = false;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : SetDefaults
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveLoadParms : : SetDefaults ( int newInputDevice )
{
2012-11-26 18:58:24 +00:00
// These are pulled out so SetDefaults() isn't called during global instantiation of objects that have savegame processors
// in them that then require a session reference.
2012-11-28 15:47:07 +00:00
Init ( ) ;
2012-11-26 18:58:24 +00:00
// fill in the user information (inputDeviceId & userId) from the master user
2012-11-28 15:47:07 +00:00
idLocalUser * user = NULL ;
if ( newInputDevice ! = - 1 )
{
2012-11-26 18:58:24 +00:00
user = session - > GetSignInManager ( ) . GetLocalUserByInputDevice ( newInputDevice ) ;
2012-11-28 15:47:07 +00:00
}
else if ( session ! = NULL )
{
2012-11-26 18:58:24 +00:00
user = session - > GetSignInManager ( ) . GetMasterLocalUser ( ) ;
}
2012-11-28 15:47:07 +00:00
if ( user ! = NULL )
{
idLocalUserWin * userWin = static_cast < idLocalUserWin * > ( user ) ;
2012-11-26 18:58:24 +00:00
userId = idStr : : Hash ( userWin - > GetGamerTag ( ) ) ;
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " profile userId/gamertag: %s (%d) \n " , userWin - > GetGamerTag ( ) , userId ) ;
inputDeviceId = user - > GetInputDevice ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : CancelSaveGameFilePipelines
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveLoadParms : : CancelSaveGameFilePipelines ( )
{
for ( int i = 0 ; i < files . Num ( ) ; i + + )
{
if ( ( files [ i ] - > type & SAVEGAMEFILE_PIPELINED ) ! = 0 )
{
idFile_SaveGamePipelined * file = dynamic_cast < idFile_SaveGamePipelined * > ( files [ i ] ) ;
2012-11-26 18:58:24 +00:00
assert ( file ! = NULL ) ;
2012-11-28 15:47:07 +00:00
if ( file - > GetMode ( ) = = idFile_SaveGamePipelined : : WRITE )
{
2012-11-26 18:58:24 +00:00
// Notify the save game file that all writes failed which will cause all
// writes on the other end of the pipeline to drop on the floor.
file - > NextWriteBlock ( NULL ) ;
2012-11-28 15:47:07 +00:00
}
else if ( file - > GetMode ( ) = = idFile_SaveGamePipelined : : READ )
{
2012-11-26 18:58:24 +00:00
// Notify end-of-file to the save game file which will cause all
// reads on the other end of the pipeline to return zero bytes.
file - > NextReadBlock ( NULL , 0 ) ;
}
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveLoadParms : : AbortSaveGameFilePipeline
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveLoadParms : : AbortSaveGameFilePipeline ( )
{
for ( int i = 0 ; i < files . Num ( ) ; i + + )
{
if ( ( files [ i ] - > type & SAVEGAMEFILE_PIPELINED ) ! = 0 )
{
idFile_SaveGamePipelined * file = dynamic_cast < idFile_SaveGamePipelined * > ( files [ i ] ) ;
2012-11-26 18:58:24 +00:00
assert ( file ! = NULL ) ;
file - > Abort ( ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameProcessor
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameProcessor : : idSaveGameProcessor
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSaveGameProcessor : : idSaveGameProcessor ( ) : working ( false ) , init ( false )
{
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameProcessor : : Init
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSaveGameProcessor : : Init ( )
{
if ( ! verify ( ! IsWorking ( ) ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " [%s] Someone is trying to execute this processor twice, this is really bad! " , this - > Name ( ) ) ;
return false ;
}
parms . ResetCancelled ( ) ;
parms . SetDefaults ( ) ;
savegameLogicTestIterator = 0 ;
2012-11-28 15:47:07 +00:00
working = false ;
2012-11-26 18:58:24 +00:00
init = true ;
completedCallbacks . Clear ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameProcessor : : IsThreadFinished
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSaveGameProcessor : : IsThreadFinished ( )
{
2012-11-26 18:58:24 +00:00
return parms . callbackSignal . Wait ( 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameProcessor : : AddCompletedCallback
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameProcessor : : AddCompletedCallback ( const idCallback & callback )
{
2012-11-26 18:58:24 +00:00
completedCallbacks . Append ( callback . Clone ( ) ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : idSaveGameManager
= = = = = = = = = = = = = = = = = = = = = = = =
*/
idSaveGameManager : : idSaveGameManager ( ) :
processor ( NULL ) ,
cancel ( false ) ,
startTime ( 0 ) ,
continueProcessing ( false ) ,
submittedProcessorHandle ( 0 ) ,
executingProcessorHandle ( 0 ) ,
lastExecutedProcessorHandle ( 0 ) ,
storageAvailable ( true ) ,
2012-11-28 15:47:07 +00:00
retryFolder ( NULL )
{
2012-11-26 18:58:24 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ~ idSaveGameManager
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
idSaveGameManager : : ~ idSaveGameManager ( )
{
2012-11-26 18:58:24 +00:00
processor = NULL ;
enumeratedSaveGames . Clear ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ExecuteProcessor
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
saveGameHandle_t idSaveGameManager : : ExecuteProcessor ( idSaveGameProcessor * processor )
{
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " [%s] : %s \n " , __FUNCTION__ , processor - > Name ( ) ) ;
2012-11-28 15:47:07 +00:00
// may not be running yet, but if we've init'd successfuly, the IsWorking() call should return true if this
2012-11-26 18:58:24 +00:00
// method has been called. You have problems when callees are asking if the processor is done working by using IsWorking()
// the next frame after they've executed the processor.
processor - > working = true ;
2012-11-28 15:47:07 +00:00
if ( this - > processor ! = NULL )
{
if ( ! verify ( this - > processor ! = processor ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " [idSaveGameManager::ExecuteProcessor]:1 Someone is trying to execute this processor twice, this is really bad, learn patience padawan! " ) ;
return processor - > GetHandle ( ) ;
2012-11-28 15:47:07 +00:00
}
else
{
idSaveGameProcessor * * localProcessor = processorQueue . Find ( processor ) ;
if ( ! verify ( localProcessor = = NULL ) )
{
2012-11-26 18:58:24 +00:00
idLib : : Warning ( " [idSaveGameManager::ExecuteProcessor]:2 Someone is trying to execute this processor twice, this is really bad, learn patience padawan! " ) ;
2012-11-28 15:47:07 +00:00
return ( * localProcessor ) - > GetHandle ( ) ;
2012-11-26 18:58:24 +00:00
}
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
processorQueue . Append ( processor ) ;
// Don't allow processors to start sub-processors.
// They need to manage their own internal state.
assert ( idLib : : IsMainThread ( ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Sys_InterlockedIncrement ( submittedProcessorHandle ) ;
processor - > parms . handle = submittedProcessorHandle ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return submittedProcessorHandle ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ExecuteProcessorAndWait
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
saveGameHandle_t idSaveGameManager : : ExecuteProcessorAndWait ( idSaveGameProcessor * processor )
{
2012-11-26 18:58:24 +00:00
saveGameHandle_t handle = ExecuteProcessor ( processor ) ;
2012-11-28 15:47:07 +00:00
if ( handle = = 0 )
{
2012-11-26 18:58:24 +00:00
return 0 ;
}
2012-11-28 15:47:07 +00:00
while ( ! IsSaveGameCompletedFromHandle ( handle ) )
{
2012-11-26 18:58:24 +00:00
Pump ( ) ;
Sys_Sleep ( 10 ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// One more pump to get the completed callback
//Pump();
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return handle ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : WaitForAllProcessors
Since the load & nextMap processors calls execute map change , we can ' t wait if they are the only ones in the queue
If there are only resettable processors in the queue or no items in the queue , don ' t wait .
We would need to overrideSimpleProcessorCheck if we were sure we had done something that would cause the processors
to bail out nicely . Something like canceling a disc swap during a loading disc swap dialog . . .
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : WaitForAllProcessors ( bool overrideSimpleProcessorCheck )
{
2012-11-26 18:58:24 +00:00
assert ( idLib : : IsMainThread ( ) ) ;
2012-11-28 15:47:07 +00:00
while ( IsWorking ( ) | | ( processorQueue . Num ( ) > 0 ) )
{
if ( ! overrideSimpleProcessorCheck )
{
2012-11-26 18:58:24 +00:00
// BEFORE WE WAIT, and potentially hang everything, make sure processors about to be executed won't sit and
// wait for themselves to complete.
// Since we pull off simple processors first, we can stop waiting when the processor being executed is not simple
2012-11-28 15:47:07 +00:00
if ( processor ! = NULL )
{
if ( ! processor - > IsSimpleProcessor ( ) )
{
2012-11-26 18:58:24 +00:00
break ;
}
2012-11-28 15:47:07 +00:00
}
else if ( ! processorQueue [ 0 ] - > IsSimpleProcessor ( ) )
{
2012-11-26 18:58:24 +00:00
break ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
saveThread . WaitForThread ( ) ;
Pump ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : CancelAllProcessors
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : CancelAllProcessors ( const bool forceCancelInFlightProcessor )
{
2012-11-26 18:58:24 +00:00
assert ( idLib : : IsMainThread ( ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
cancel = true ;
2012-11-28 15:47:07 +00:00
if ( forceCancelInFlightProcessor )
{
if ( processor ! = NULL )
{
2012-11-26 18:58:24 +00:00
processor - > GetSignal ( ) . Raise ( ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Pump ( ) ; // must be called from the main thread
Clear ( ) ;
cancel = false ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : CancelToTerminate
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : CancelToTerminate ( )
{
if ( processor ! = NULL )
{
2012-11-26 18:58:24 +00:00
processor - > parms . cancelled = true ;
processor - > GetSignal ( ) . Raise ( ) ;
saveThread . WaitForThread ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : DeviceSelectorWaitingOnSaveRetry
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSaveGameManager : : DeviceSelectorWaitingOnSaveRetry ( )
{
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
if ( retryFolder = = NULL )
{
2012-11-26 18:58:24 +00:00
return false ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
return ( idStr : : Icmp ( retryFolder , " GAME-autosave " ) = = 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : Set360RetrySaveAfterDeviceSelected
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : Set360RetrySaveAfterDeviceSelected ( const char * folder , const int64 bytes )
{
2012-11-26 18:58:24 +00:00
retryFolder = folder ;
retryBytes = bytes ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ClearRetryInfo
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : ClearRetryInfo ( )
{
2012-11-26 18:58:24 +00:00
retryFolder = NULL ;
retryBytes = 0 ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : RetrySave
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : RetrySave ( )
{
if ( DeviceSelectorWaitingOnSaveRetry ( ) & & ! common - > Dialog ( ) . HasDialogMsg ( GDM_WARNING_FOR_NEW_DEVICE_ABOUT_TO_LOSE_PROGRESS , false ) )
{
2012-11-26 18:58:24 +00:00
cmdSystem - > AppendCommandText ( " savegame autosave \n " ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ShowRetySaveDialog
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : ShowRetySaveDialog ( )
{
2012-11-26 18:58:24 +00:00
ShowRetySaveDialog ( retryFolder , retryBytes ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : ShowRetySaveDialog
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : ShowRetySaveDialog ( const char * folder , const int64 bytes )
{
2012-11-26 18:58:24 +00:00
2012-11-28 15:47:07 +00:00
idStaticList < idSWFScriptFunction * , 4 > callbacks ;
2012-11-26 18:58:24 +00:00
idStaticList < idStrId , 4 > optionText ;
2012-11-28 15:47:07 +00:00
class idSWFScriptFunction_Continue : public idSWFScriptFunction_RefCounted
{
2012-11-26 18:58:24 +00:00
public :
2012-11-28 15:47:07 +00:00
idSWFScriptVar Call ( idSWFScriptObject * thisObject , const idSWFParmList & parms )
{
2012-11-26 18:58:24 +00:00
common - > Dialog ( ) . ClearDialog ( GDM_INSUFFICENT_STORAGE_SPACE ) ;
session - > GetSaveGameManager ( ) . ClearRetryInfo ( ) ;
return idSWFScriptVar ( ) ;
}
} ;
2012-11-28 15:47:07 +00:00
callbacks . Append ( new ( TAG_SWF ) idSWFScriptFunction_Continue ( ) ) ;
2012-11-26 18:58:24 +00:00
optionText . Append ( idStrId ( " #str_dlg_continue_without_saving " ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// build custom space required string
// #str_dlg_space_required ~= "There is insufficient storage available. Please free %s and try again."
idStr format = idStrId ( " #str_dlg_space_required " ) . GetLocalizedString ( ) ;
idStr size ;
2012-11-28 15:47:07 +00:00
if ( bytes > ( 1024 * 1024 ) )
{
const float roundUp = ( ( 1024.0f * 1024.0f / 10.0f ) - 1.0f ) ;
size = va ( " %.1f MB " , ( roundUp + ( float ) bytes ) / ( 1024.0f * 1024.0f ) ) ;
}
else
{
2012-11-26 18:58:24 +00:00
const float roundUp = 1024.0f - 1.0f ;
2012-11-28 15:47:07 +00:00
size = va ( " %.0f KB " , ( roundUp + ( float ) bytes ) / 1024.0f ) ;
2012-11-26 18:58:24 +00:00
}
idStr msg = va ( format . c_str ( ) , size . c_str ( ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
common - > Dialog ( ) . AddDynamicDialog ( GDM_INSUFFICENT_STORAGE_SPACE , callbacks , optionText , true , msg , true ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : CancelWithHandle
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : CancelWithHandle ( const saveGameHandle_t & handle )
{
if ( handle = = 0 | | IsSaveGameCompletedFromHandle ( handle ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// check processor in flight first
2012-11-28 15:47:07 +00:00
if ( processor ! = NULL )
{
if ( processor - > GetHandle ( ) = = handle )
{
2012-11-26 18:58:24 +00:00
processor - > Cancel ( ) ;
return ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// remove from queue
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < processorQueue . Num ( ) ; + + i )
{
if ( processorQueue [ i ] - > GetHandle ( ) = = handle )
{
2012-11-26 18:58:24 +00:00
processorQueue [ i ] - > Cancel ( ) ;
return ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : StartNextProcessor
Get the next not - reset - capable processor . If there aren ' t any left , just get what ' s next .
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : StartNextProcessor ( )
{
if ( cancel )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
idSaveGameProcessor * nextProcessor = NULL ;
2012-11-26 18:58:24 +00:00
int index = 0 ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// pick off the first simple processor
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < processorQueue . Num ( ) ; + + i )
{
if ( processorQueue [ i ] - > IsSimpleProcessor ( ) )
{
2012-11-26 18:58:24 +00:00
index = i ;
break ;
}
}
2012-11-28 15:47:07 +00:00
if ( processorQueue . Num ( ) > 0 )
{
2012-11-26 18:58:24 +00:00
nextProcessor = processorQueue [ index ] ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
Sys_InterlockedIncrement ( executingProcessorHandle ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
processorQueue . RemoveIndex ( index ) ;
processor = nextProcessor ;
processor - > parms . callbackSignal . Raise ( ) ; // signal that the thread is ready for work
startTime = Sys_Milliseconds ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : FinishProcessor
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : FinishProcessor ( idSaveGameProcessor * localProcessor )
{
2012-11-26 18:58:24 +00:00
assert ( localProcessor ! = NULL ) ;
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " [%s] : %s, %d ms \n " , __FUNCTION__ , localProcessor - > Name ( ) , Sys_Milliseconds ( ) - startTime ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// This will delete from the files set for auto-deletion
// Don't remove files not set for auto-deletion, they may be used outside of the savegame manager by game-side callbacks for example
2012-11-28 15:47:07 +00:00
for ( int i = ( localProcessor - > parms . files . Num ( ) - 1 ) ; i > = 0 ; - - i )
{
if ( localProcessor - > parms . files [ i ] - > type & SAVEGAMEFILE_AUTO_DELETE )
{
2012-11-26 18:58:24 +00:00
delete localProcessor - > parms . files [ i ] ;
localProcessor - > parms . files . RemoveIndexFast ( i ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
localProcessor - > init = false ;
localProcessor = NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : Clear
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : Clear ( )
{
2012-11-26 18:58:24 +00:00
processorQueue . Clear ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : IsWorking
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
bool idSaveGameManager : : IsWorking ( ) const
{
2012-11-26 18:58:24 +00:00
return processor ! = NULL ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = =
idSaveGameManager : : Pump
Important sections called out with - - EXTRA LARGE - - comments !
= = = = = = = = = = = = = = = = = = = = = = = =
*/
2012-11-28 15:47:07 +00:00
void idSaveGameManager : : Pump ( )
{
2012-11-26 18:58:24 +00:00
// After a processor is done, the next is pulled off the queue so the only way the manager isn't working is if
// there isn't something executing or in the queue.
2012-11-28 15:47:07 +00:00
if ( ! IsWorking ( ) )
{
2012-11-26 18:58:24 +00:00
// Unified start to initialize system on PS3 and do appropriate checks for system combination issues
// ------------------------------------
// START
// ------------------------------------
StartNextProcessor ( ) ;
2012-11-28 15:47:07 +00:00
if ( ! IsWorking ( ) )
{
2012-11-26 18:58:24 +00:00
return ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
continueProcessing = true ;
}
2012-11-28 15:47:07 +00:00
if ( cancel )
{
2012-11-26 18:58:24 +00:00
processor - > parms . AbortSaveGameFilePipeline ( ) ;
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Quickly checks to see if the savegame thread is done, otherwise, exit and continue frame commands
2012-11-28 15:47:07 +00:00
if ( processor - > IsThreadFinished ( ) )
{
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " %s waited on processor [%s], error = 0x%08X, %s \n " , __FUNCTION__ , processor - > Name ( ) , processor - > GetError ( ) , GetSaveGameErrorString ( processor - > GetError ( ) ) . c_str ( ) ) ;
2012-11-28 15:47:07 +00:00
if ( ! cancel & & continueProcessing )
{
2012-11-26 18:58:24 +00:00
// Check for available storage unit
2012-11-28 15:47:07 +00:00
if ( session - > GetSignInManager ( ) . GetMasterLocalUser ( ) ! = NULL )
{
if ( ! session - > GetSignInManager ( ) . GetMasterLocalUser ( ) - > IsStorageDeviceAvailable ( ) )
{
2012-11-26 18:58:24 +00:00
// this will not allow further processing
processor - > parms . errorCode = SAVEGAME_E_UNABLE_TO_SELECT_STORAGE_DEVICE ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// Execute Process() on the processor, if there was an error in a previous Process() call, give the
// processor the chance to validate that error and either clean itself up or convert it to another error or none.
2012-11-28 15:47:07 +00:00
if ( processor - > GetError ( ) = = SAVEGAME_E_NONE | | processor - > ValidateLastError ( ) )
{
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " %s calling %s::Process(), error = 0x%08X, %s \n " , __FUNCTION__ , processor - > Name ( ) , processor - > GetError ( ) , GetSaveGameErrorString ( processor - > GetError ( ) ) . c_str ( ) ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// ------------------------------------
// PROCESS
// ------------------------------------
continueProcessing = processor - > Process ( ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// If we don't return here, the completedCallback will be executed before it's done with it's async operation
// during it's last process stage.
return ;
2012-11-28 15:47:07 +00:00
}
else
{
2012-11-26 18:58:24 +00:00
continueProcessing = false ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// This section does specific post-processing for each of the save commands
2012-11-28 15:47:07 +00:00
if ( ! continueProcessing )
{
2012-11-26 18:58:24 +00:00
// Clear out details if we detect corruption but keep directory/slot information
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < processor - > parms . detailList . Num ( ) ; + + i )
{
idSaveGameDetails & details = processor - > parms . detailList [ i ] ;
if ( details . damaged )
{
2012-11-26 18:58:24 +00:00
details . descriptors . Clear ( ) ;
}
}
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " %s calling %s::CompletedCallback() \n " , __FUNCTION__ , processor - > Name ( ) ) ;
processor - > working = false ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// This ensures that the savegame manager will believe the processor is done when there is a potentially
// catastrophic thing that will happen within CompletedCallback which might try to sync all threads
// The most common case of this is executing a map change (which we no longer do).
2012-11-28 15:47:07 +00:00
// We flush the heap and wait for all background processes to finish. After all this is called, we will
2012-11-26 18:58:24 +00:00
// cleanup the old processor within FinishProcessor()
2012-11-28 15:47:07 +00:00
idSaveGameProcessor * localProcessor = processor ;
2012-11-26 18:58:24 +00:00
processor = NULL ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// ------------------------------------
// COMPLETEDCALLBACK
// At this point, the handle will be completed
// ------------------------------------
Sys_InterlockedIncrement ( lastExecutedProcessorHandle ) ;
2012-11-28 15:47:07 +00:00
for ( int i = 0 ; i < localProcessor - > completedCallbacks . Num ( ) ; i + + )
{
2012-11-26 18:58:24 +00:00
localProcessor - > completedCallbacks [ i ] - > Call ( ) ;
}
localProcessor - > completedCallbacks . DeleteContents ( true ) ;
2012-11-28 15:47:07 +00:00
2012-11-26 18:58:24 +00:00
// ------------------------------------
// FINISHPROCESSOR
// ------------------------------------
FinishProcessor ( localProcessor ) ;
}
2012-11-28 15:47:07 +00:00
}
else if ( processor - > ShouldTimeout ( ) )
{
2012-11-26 18:58:24 +00:00
// Hack for the PS3 threading hang
idLib : : PrintfIf ( saveGame_verbose . GetBool ( ) , " ----- PROCESSOR TIMEOUT ----- (%s) \n " , processor - > Name ( ) ) ;
2012-11-28 15:47:07 +00:00
idSaveGameProcessor * tempProcessor = processor ;
2012-11-26 18:58:24 +00:00
CancelAllProcessors ( true ) ;
2012-11-28 15:47:07 +00:00
class idSWFScriptFunction_TryAgain : public idSWFScriptFunction_RefCounted
{
2012-11-26 18:58:24 +00:00
public :
2012-11-28 15:47:07 +00:00
idSWFScriptFunction_TryAgain ( idSaveGameManager * manager , idSaveGameProcessor * processor )
{
2012-11-26 18:58:24 +00:00
this - > manager = manager ;
2012-11-28 15:47:07 +00:00
this - > processor = processor ;
2012-11-26 18:58:24 +00:00
}
2012-11-28 15:47:07 +00:00
idSWFScriptVar Call ( idSWFScriptObject * thisObject , const idSWFParmList & parms )
{
2012-11-26 18:58:24 +00:00
common - > Dialog ( ) . ClearDialog ( GDM_ERROR_SAVING_SAVEGAME ) ;
manager - > ExecuteProcessor ( processor ) ;
return idSWFScriptVar ( ) ;
}
private :
2012-11-28 15:47:07 +00:00
idSaveGameManager * manager ;
idSaveGameProcessor * processor ;
2012-11-26 18:58:24 +00:00
} ;
2012-11-28 15:47:07 +00:00
idStaticList < idSWFScriptFunction * , 4 > callbacks ;
2012-11-26 18:58:24 +00:00
idStaticList < idStrId , 4 > optionText ;
2012-11-28 15:47:07 +00:00
callbacks . Append ( new ( TAG_SWF ) idSWFScriptFunction_TryAgain ( this , tempProcessor ) ) ;
2012-11-26 18:58:24 +00:00
optionText . Append ( idStrId ( " #STR_SWF_RETRY " ) ) ;
common - > Dialog ( ) . AddDynamicDialog ( GDM_ERROR_SAVING_SAVEGAME , callbacks , optionText , true , " " ) ;
}
}