1286 lines
36 KiB
C++
1286 lines
36 KiB
C++
//===========================================================================
|
|
// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
|
|
//
|
|
// Component: GameDataManager
|
|
//
|
|
// Description: Implementation of the GameDataManager class.
|
|
//
|
|
// Authors: Tony Chu
|
|
//
|
|
// Revisions Date Author Revision
|
|
// 2002/09/16 TChu Created for SRR2
|
|
//
|
|
//===========================================================================
|
|
|
|
//#define PRINT_RAW_DATA // to debug output
|
|
|
|
//===========================================================================
|
|
// Includes
|
|
//===========================================================================
|
|
#include <data/gamedatamanager.h>
|
|
#include <data/savegameinfo.h>
|
|
#include <data/memcard/memorycardmanager.h>
|
|
#include <string.h>
|
|
|
|
#ifndef WORLD_BUILDER
|
|
#include <memory/srrmemory.h>
|
|
#else
|
|
#define MEMTRACK_PUSH_GROUP(x)
|
|
#define MEMTRACK_POP_GROUP(x)
|
|
#define GMA_PERSISTENT 0
|
|
#define GMA_DEFAULT 0
|
|
#define GMA_TEMP 0
|
|
#endif
|
|
|
|
#include <radfile.hpp>
|
|
#include <raddebugwatch.hpp>
|
|
#include <radtime.hpp>
|
|
|
|
#ifdef RAD_PS2
|
|
#include <radstring.hpp>
|
|
#endif
|
|
#ifdef RAD_PS2
|
|
const unsigned int MAX_GAME_DATA_SIZE = 8600; // in bytes (since ps2 have different byte packing)
|
|
#else
|
|
const unsigned int MAX_GAME_DATA_SIZE = 7500; // in bytes
|
|
#endif
|
|
|
|
// Static pointer to instance of singleton.
|
|
GameDataManager* GameDataManager::spInstance = NULL;
|
|
|
|
//===========================================================================
|
|
// Local Constants
|
|
//===========================================================================
|
|
|
|
#ifdef RAD_PS2
|
|
const char* SAVED_GAME_TITLE = "The Simpsons:Hit & Run"; // there should be no space betw. "Simpsons:" and "Hit"
|
|
// due to line break inserted there
|
|
#endif
|
|
|
|
#ifdef RAD_XBOX
|
|
// Xbox TCR Requirement!!!
|
|
//
|
|
#define ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
const unsigned int MINIMUM_LOAD_SAVE_TIME = 2000; // in msec
|
|
#else
|
|
#define ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
const unsigned int MINIMUM_LOAD_SAVE_TIME = 1000; // in msec
|
|
#endif
|
|
|
|
#ifdef RAD_PS2
|
|
const unsigned int MINIMUM_DELETE_TIME = 3000; // in msec
|
|
#else
|
|
const unsigned int MINIMUM_DELETE_TIME = 1000; // in msec
|
|
#endif
|
|
|
|
#ifdef RAD_PS2
|
|
/* Filename for PS2 saved games must be prefixed with the following:
|
|
*
|
|
* (BISLPS | BISLPM | BISCPS | BASLUS | BASCUS | BESLES | BESCES)-#####
|
|
*
|
|
* Example: "BASCUS-12345savegame.dat"
|
|
*
|
|
*/
|
|
#ifdef PAL
|
|
#define PS2_FILENAME_PREFIX "BESLES-51897"
|
|
#else
|
|
#define PS2_FILENAME_PREFIX "BASLUS-20624"
|
|
#endif
|
|
#else
|
|
#define PS2_FILENAME_PREFIX ""
|
|
#endif
|
|
|
|
#ifdef DEBUGWATCH
|
|
const char* WATCHER_NAMESPACE = "Game Data Manager";
|
|
|
|
unsigned int g_wCurrentSlot = 0;
|
|
|
|
static void LoadGameFromWatcher()
|
|
{
|
|
if( GetMemoryCardManager()->GetCurrentDrive() != NULL )
|
|
{
|
|
GetGameDataManager()->LoadGame( g_wCurrentSlot );
|
|
}
|
|
else
|
|
{
|
|
rTunePrintf( "*** Can't load game! No memory device selected!\n" );
|
|
}
|
|
}
|
|
|
|
static void SaveGameFromWatcher()
|
|
{
|
|
if( GetMemoryCardManager()->GetCurrentDrive() != NULL )
|
|
{
|
|
GetGameDataManager()->SaveGame( g_wCurrentSlot );
|
|
}
|
|
else
|
|
{
|
|
rTunePrintf( "*** Can't save game! No memory device selected!\n" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//===========================================================================
|
|
// Public Member Functions
|
|
//===========================================================================
|
|
|
|
//==============================================================================
|
|
// GameDataManager::CreateInstance
|
|
//==============================================================================
|
|
//
|
|
// Description: - Creates the Game Data Manager.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return: Pointer to the GameDataManager.
|
|
//
|
|
// Constraints: This is a singleton so only one instance is allowed.
|
|
//
|
|
//==============================================================================
|
|
GameDataManager* GameDataManager::CreateInstance()
|
|
{
|
|
MEMTRACK_PUSH_GROUP( "GameDataManager" );
|
|
spInstance = new GameDataManager;
|
|
rAssert( spInstance != NULL );
|
|
MEMTRACK_POP_GROUP( "GameDataManager" );
|
|
|
|
// create other singletons owned by GameDataManager
|
|
//
|
|
MemoryCardManager* pMemoryCardManager = MemoryCardManager::CreateInstance();
|
|
rAssert( pMemoryCardManager != NULL );
|
|
|
|
return spInstance;
|
|
}
|
|
|
|
//==============================================================================
|
|
// GameDataManager::DestroyInstance
|
|
//==============================================================================
|
|
//
|
|
// Description: Destroy the Game Data Manager.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return: None.
|
|
//
|
|
//==============================================================================
|
|
void GameDataManager::DestroyInstance()
|
|
{
|
|
// destroy other singletons owned by GameDataManager
|
|
//
|
|
MemoryCardManager::DestroyInstance();
|
|
|
|
rAssert( spInstance != NULL );
|
|
|
|
delete spInstance;
|
|
spInstance = NULL;
|
|
}
|
|
|
|
//==============================================================================
|
|
// GameDataManager::GetInstance
|
|
//==============================================================================
|
|
//
|
|
// Description: - Access point for the GameDataManager singleton.
|
|
// - Creates the GameDataManager if needed.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return: Pointer to the GameDataManager.
|
|
//
|
|
// Constraints: This is a singleton so only one instance is allowed.
|
|
//
|
|
//==============================================================================
|
|
GameDataManager* GameDataManager::GetInstance()
|
|
{
|
|
rAssert( spInstance != NULL );
|
|
|
|
return spInstance;
|
|
}
|
|
|
|
//===========================================================================
|
|
// GameDataManager::GameDataManager
|
|
//===========================================================================
|
|
// Description:
|
|
//
|
|
// Constraints: None.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return:
|
|
//
|
|
//===========================================================================
|
|
GameDataManager::GameDataManager()
|
|
: m_numRegisteredGameData( 0 ),
|
|
m_gameDataBuffer( NULL ),
|
|
m_gameDataSize( 0 ),
|
|
m_gameDataLoadCallback( NULL ),
|
|
m_gameDataSaveCallback( NULL ),
|
|
m_gameDataDeleteCallback( NULL ),
|
|
#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
m_minimumLoadSaveTime( MINIMUM_LOAD_SAVE_TIME ),
|
|
#else
|
|
m_minimumLoadSaveTime( 0 ),
|
|
#endif
|
|
m_elapsedOperationTime( 0 ),
|
|
m_isGameLoaded( false ),
|
|
m_saveGameInfoHandler( NULL ),
|
|
m_radFile( NULL ),
|
|
m_currentFileOperation( FILE_OP_NONE )
|
|
{
|
|
rAssertMsg( sizeof( GameDataByte ) == 1,
|
|
"WARNING: *** Size of GameDataByte is not 1 byte! Is that OK??" );
|
|
|
|
for( unsigned int i = 0; i < sizeof( m_registeredGameData ) /
|
|
sizeof( m_registeredGameData[ 0 ] ); i++ )
|
|
{
|
|
m_registeredGameData[ i ] = NULL;
|
|
}
|
|
|
|
// create the save game info handler, and register it before anything else
|
|
//
|
|
m_saveGameInfoHandler = new SaveGameInfo;
|
|
rAssert( m_saveGameInfoHandler );
|
|
this->RegisterGameData( m_saveGameInfoHandler,
|
|
SaveGameInfo::GetSize(),
|
|
"Save Game Info" );
|
|
}
|
|
|
|
//===========================================================================
|
|
// GameDataManager::~GameDataManager
|
|
//===========================================================================
|
|
// Description:
|
|
//
|
|
// Constraints: None.
|
|
//
|
|
// Parameters: None.
|
|
//
|
|
// Return:
|
|
//
|
|
//===========================================================================
|
|
GameDataManager::~GameDataManager()
|
|
{
|
|
#ifdef DEBUGWATCH
|
|
::radDbgWatchDelete( &g_wCurrentSlot );
|
|
#endif
|
|
|
|
if( m_saveGameInfoHandler != NULL )
|
|
{
|
|
delete m_saveGameInfoHandler;
|
|
m_saveGameInfoHandler = NULL;
|
|
}
|
|
|
|
for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
|
|
{
|
|
if( m_registeredGameData[ i ] != NULL )
|
|
{
|
|
delete m_registeredGameData[ i ];
|
|
m_registeredGameData[ i ] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
GameDataManager::Init()
|
|
{
|
|
MEMTRACK_PUSH_GROUP( "GameDataManager" );
|
|
// initialize memory card manager
|
|
//
|
|
GetMemoryCardManager()->Init( this );
|
|
|
|
#ifdef DEBUGWATCH
|
|
::radDbgWatchAddUnsignedInt( &g_wCurrentSlot,
|
|
"Game Slot",
|
|
WATCHER_NAMESPACE,
|
|
NULL, NULL, 0, NUM_GAME_SLOTS - 1 );
|
|
|
|
::radDbgWatchAddFunction( "Load Game",
|
|
(RADDEBUGWATCH_CALLBACK)LoadGameFromWatcher,
|
|
NULL,
|
|
WATCHER_NAMESPACE );
|
|
|
|
::radDbgWatchAddFunction( "Save Game",
|
|
(RADDEBUGWATCH_CALLBACK)SaveGameFromWatcher,
|
|
NULL,
|
|
WATCHER_NAMESPACE );
|
|
#endif
|
|
MEMTRACK_POP_GROUP( "GameDataManager" );
|
|
}
|
|
|
|
void
|
|
GameDataManager::Update( unsigned int elapsedTime )
|
|
{
|
|
if( m_currentFileOperation != FILE_OP_NONE )
|
|
{
|
|
#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
if( m_elapsedOperationTime > m_minimumLoadSaveTime )
|
|
{
|
|
if( m_currentFileOperation == FILE_OP_LOAD_COMPLETED )
|
|
{
|
|
if( m_gameDataLoadCallback != NULL )
|
|
{
|
|
m_gameDataLoadCallback->OnLoadGameComplete( Success );
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
}
|
|
|
|
if( m_currentFileOperation == FILE_OP_SAVE_COMPLETED )
|
|
{
|
|
if( m_gameDataSaveCallback != NULL )
|
|
{
|
|
m_gameDataSaveCallback->OnSaveGameComplete( Success );
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
}
|
|
}
|
|
#endif // ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
|
|
if( m_elapsedOperationTime > MINIMUM_DELETE_TIME )
|
|
{
|
|
if( m_currentFileOperation == FILE_OP_DELETE_COMPLETED )
|
|
{
|
|
if( m_gameDataDeleteCallback != NULL )
|
|
{
|
|
m_gameDataDeleteCallback->OnDeleteGameComplete( m_lastError );
|
|
m_gameDataDeleteCallback = NULL;
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
}
|
|
}
|
|
|
|
m_elapsedOperationTime += elapsedTime;
|
|
}
|
|
}
|
|
|
|
void
|
|
GameDataManager::RegisterGameData( GameDataHandler* gdHandler,
|
|
unsigned int numBytes,
|
|
const char* name )
|
|
{
|
|
MEMTRACK_PUSH_GROUP( "GameDataManager" );
|
|
rAssert( gdHandler != NULL );
|
|
rAssert( numBytes >= 0 );
|
|
|
|
rAssertMsg( m_numRegisteredGameData < MAX_NUM_GAME_DATA,
|
|
"ERROR: *** Exceeded maximum number of registered game data!" );
|
|
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PushHeap( GMA_PERSISTENT );
|
|
#endif
|
|
|
|
// create new game data
|
|
//
|
|
GameData* newGameData = new GameData;
|
|
rAssert( newGameData );
|
|
newGameData->m_gdHandler = gdHandler;
|
|
newGameData->m_numBytes = numBytes;
|
|
|
|
#ifdef RAD_DEBUG
|
|
if( name != NULL )
|
|
{
|
|
strncpy( newGameData->m_name, name, sizeof( newGameData->m_name ) );
|
|
}
|
|
else
|
|
{
|
|
strcpy( newGameData->m_name, "" );
|
|
}
|
|
|
|
rTunePrintf( ">> Registered Game Data [%d]: %s (%d bytes)\n",
|
|
m_numRegisteredGameData,
|
|
newGameData->m_name,
|
|
newGameData->m_numBytes );
|
|
#endif
|
|
|
|
// add to registered list of game data
|
|
//
|
|
m_registeredGameData[ m_numRegisteredGameData ] = newGameData;
|
|
m_numRegisteredGameData++;
|
|
|
|
// update game data size
|
|
//
|
|
m_gameDataSize += numBytes;
|
|
|
|
rReleasePrintf( ">> Total Game Data Size = %d bytes\n", m_gameDataSize );
|
|
|
|
// rReleaseAssertMsg( m_gameDataSize <= MAX_GAME_DATA_SIZE,
|
|
// "ERROR: *** Max Game Data Size Exceeded!" );
|
|
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PopHeap( GMA_PERSISTENT );
|
|
#endif
|
|
|
|
MEMTRACK_POP_GROUP( "GameDataManager" );
|
|
}
|
|
|
|
void
|
|
GameDataManager::LoadGame( unsigned int slot, GameDataLoadCallback* callback, const char *load_filename )
|
|
{
|
|
rAssertMsg( slot < NUM_GAME_SLOTS, "ERROR: *** Invalid game slot!" );
|
|
|
|
rReleasePrintf( ">> Loading game from slot %d ... ...\n", slot + 1 );
|
|
|
|
m_gameDataLoadCallback = callback;
|
|
|
|
// open save game file for reading
|
|
//
|
|
char filename[ radFileFilenameMax + 1 ];
|
|
if (load_filename==NULL) // set up predefined filename
|
|
this->FormatSavedGameFilename( filename,
|
|
sizeof( filename ),
|
|
slot );
|
|
else
|
|
strcpy( filename, load_filename ); // filename is passed in for xbox
|
|
IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
|
|
rAssert( currentDrive );
|
|
#ifndef RAD_WIN32
|
|
currentDrive->SaveGameOpenAsync( &m_radFile,
|
|
filename,
|
|
false,
|
|
OpenExisting,
|
|
NULL,
|
|
m_gameDataSize,
|
|
true);
|
|
#else
|
|
currentDrive->FileOpenAsync( &m_radFile,
|
|
filename,
|
|
false,
|
|
OpenExisting );
|
|
#endif // ~RAD_WIN32
|
|
rAssert( m_radFile );
|
|
m_radFile->AddCompletionCallback( this, NULL );
|
|
|
|
// allocate temp memory for game data buffer
|
|
//
|
|
rAssert( m_gameDataBuffer == NULL );
|
|
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PushHeap( GMA_TEMP );
|
|
#endif
|
|
|
|
m_gameDataBuffer = new GameDataByte[ m_gameDataSize ];
|
|
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PopHeap( GMA_TEMP );
|
|
#endif
|
|
|
|
rAssert( m_gameDataBuffer != NULL );
|
|
|
|
m_currentFileOperation = FILE_OP_OPEN_FOR_READING;
|
|
m_elapsedOperationTime = 0;
|
|
}
|
|
|
|
radFileError GameDataManager::DeleteGame( const char *fileName,
|
|
bool async,
|
|
GameDataDeleteCallback* callback )
|
|
{
|
|
rAssert(m_currentFileOperation == FILE_OP_NONE);
|
|
m_currentFileOperation = FILE_OP_DELETE;
|
|
|
|
IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
|
|
|
|
rAssert( currentDrive );
|
|
|
|
m_lastError = Success;
|
|
|
|
if( async )
|
|
{
|
|
currentDrive->DestroyFileAsync( fileName, true );
|
|
currentDrive->AddCompletionCallback( this, NULL );
|
|
|
|
rAssert( callback != NULL );
|
|
m_gameDataDeleteCallback = callback;
|
|
|
|
m_elapsedOperationTime = 0;
|
|
}
|
|
else
|
|
{
|
|
currentDrive->DestroyFileSync( fileName, true );
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
}
|
|
|
|
return m_lastError;
|
|
}
|
|
|
|
void
|
|
GameDataManager::SaveGame( unsigned int slot, GameDataSaveCallback* callback )
|
|
{
|
|
rAssertMsg( slot < NUM_GAME_SLOTS, "ERROR: *** Invalid game slot!" );
|
|
|
|
rReleasePrintf( ">> Saving game to slot %d ... ...\n", slot + 1 );
|
|
|
|
m_gameDataSaveCallback = callback;
|
|
|
|
// allocate temp memory for game data buffer
|
|
//
|
|
rAssert( m_gameDataBuffer == NULL );
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PushHeap( GMA_TEMP );
|
|
#endif
|
|
|
|
m_gameDataBuffer = new GameDataByte[ m_gameDataSize ];
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PopHeap( GMA_TEMP );
|
|
#endif
|
|
rAssert( m_gameDataBuffer != NULL );
|
|
|
|
this->SaveAllData();
|
|
|
|
// update saved game title in memcard info before saving
|
|
//
|
|
#ifdef RAD_GAMECUBE
|
|
char levelMissionInfo[ 32 ];
|
|
m_saveGameInfoHandler->FormatLevelMissionInfo( levelMissionInfo );
|
|
|
|
char savedGameTitle[ 32 ];
|
|
sprintf( savedGameTitle, "Slot %d %s", slot + 1, levelMissionInfo );
|
|
|
|
GetMemoryCardManager()->UpdateMemcardInfo( savedGameTitle );
|
|
#endif
|
|
#ifdef RAD_PS2
|
|
char levelMissionInfo[ 32 ];
|
|
m_saveGameInfoHandler->FormatLevelMissionInfo( levelMissionInfo );
|
|
|
|
char savedGameTitle[ 32 ];
|
|
sprintf( savedGameTitle, "%s (%d)", SAVED_GAME_TITLE, slot + 1 );
|
|
|
|
GetMemoryCardManager()->UpdateMemcardInfo( savedGameTitle, 13 );
|
|
#endif
|
|
#ifdef RAD_XBOX
|
|
GetMemoryCardManager()->UpdateMemcardInfo( NULL );
|
|
#endif
|
|
#ifdef RAD_WIN32
|
|
GetMemoryCardManager()->UpdateMemcardInfo( NULL );
|
|
#endif
|
|
|
|
// open save game file for writing
|
|
//
|
|
char filename[ radFileFilenameMax + 1 ];
|
|
this->FormatSavedGameFilename( filename,
|
|
sizeof( filename ),
|
|
slot );
|
|
|
|
IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
|
|
rAssert( currentDrive );
|
|
|
|
#ifdef RAD_XBOX
|
|
m_saveGameInfoHandler->FormatDisplay(filename, sizeof(filename)); // define filename for xbox
|
|
#endif
|
|
|
|
#ifndef RAD_WIN32
|
|
const radMemcardInfo* memcardInfo = GetMemoryCardManager()->GetMemcardInfo();
|
|
bool simpleName = false;
|
|
#ifdef RAD_XBOX
|
|
simpleName = true; // set the flag so radcore doesn't process ':' and '/'
|
|
#endif
|
|
|
|
#ifdef RAD_PS2
|
|
radFileOpenFlags fileOpenFlags = OpenAlways;
|
|
#else
|
|
radFileOpenFlags fileOpenFlags = CreateAlways;
|
|
#endif
|
|
currentDrive->SaveGameOpenAsync( &m_radFile,
|
|
filename,
|
|
true,
|
|
fileOpenFlags,
|
|
const_cast<radMemcardInfo*>( memcardInfo ),
|
|
m_gameDataSize, simpleName );
|
|
#else
|
|
currentDrive->FileOpenAsync( &m_radFile,
|
|
filename,
|
|
true,
|
|
CreateAlways );
|
|
#endif // ~RAD_WIN32
|
|
|
|
|
|
rAssert( m_radFile );
|
|
m_radFile->AddCompletionCallback( this, NULL );
|
|
|
|
m_currentFileOperation = FILE_OP_OPEN_FOR_WRITING;
|
|
m_elapsedOperationTime = 0;
|
|
}
|
|
|
|
void
|
|
GameDataManager::ResetGame()
|
|
{
|
|
// tell all game data handlers to reset their data to defaults
|
|
//
|
|
for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
|
|
{
|
|
rAssert( m_registeredGameData[ i ] != NULL &&
|
|
m_registeredGameData[ i ]->m_gdHandler != NULL );
|
|
|
|
m_registeredGameData[ i ]->m_gdHandler->ResetData();
|
|
}
|
|
|
|
// game is no longer loaded
|
|
//
|
|
m_isGameLoaded = false;
|
|
}
|
|
|
|
void
|
|
GameDataManager::SetMinimumLoadSaveTime( unsigned int minimumTime )
|
|
{
|
|
m_minimumLoadSaveTime = minimumTime;
|
|
}
|
|
|
|
void
|
|
GameDataManager::RestoreDefaultMinimumLoadSaveTime()
|
|
{
|
|
#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
m_minimumLoadSaveTime = MINIMUM_LOAD_SAVE_TIME;
|
|
#else
|
|
m_minimumLoadSaveTime = 0;
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
GameDataManager::GetSaveGameInfo( IRadDrive* pDrive, unsigned int slot,
|
|
SaveGameInfo* saveGameInfo,
|
|
bool *file_corrupt_flag)
|
|
{
|
|
rAssert( pDrive != NULL );
|
|
rAssert( saveGameInfo != NULL );
|
|
// open save game file for checking if it exists
|
|
//
|
|
char filename[ radFileFilenameMax + 1 ];
|
|
|
|
#ifdef RAD_XBOX
|
|
// xbox, we get the filename from the drive instead of predefined slot based filename
|
|
IRadDrive::DirectoryInfo dir_info;
|
|
dir_info.m_Type = IRadDrive::DirectoryInfo::IsDone; // initialize first, in case it errs out
|
|
|
|
|
|
pDrive->FindFirstSync("*",&dir_info);
|
|
|
|
if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
|
|
return false;
|
|
|
|
for (unsigned int i = 0; i < slot; i++)
|
|
{
|
|
dir_info.m_Type = IRadDrive::DirectoryInfo::IsDone; // initialize first, in case it errs out
|
|
pDrive->FindNextSync(&dir_info);
|
|
if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
|
|
break;
|
|
}
|
|
|
|
if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
|
|
return false;
|
|
strcpy(filename,dir_info.m_Name);
|
|
|
|
#else
|
|
this->FormatSavedGameFilename( filename,
|
|
sizeof( filename ),
|
|
slot );
|
|
#endif
|
|
|
|
m_lastError = Success; // want to know what is the error from opensync
|
|
if (file_corrupt_flag)
|
|
*file_corrupt_flag = false;
|
|
|
|
|
|
#ifndef RAD_WIN32
|
|
bool simpleName = false;
|
|
#ifdef RAD_XBOX
|
|
simpleName = true; // set up flag for xbox so radcore doesn't process ':' '/' character
|
|
#endif
|
|
pDrive->SaveGameOpenSync( &m_radFile,
|
|
filename,
|
|
false,
|
|
OpenExisting,
|
|
0,
|
|
0,
|
|
simpleName);
|
|
#else
|
|
|
|
eFileOperation temp = m_currentFileOperation;
|
|
m_currentFileOperation = FILE_OP_FILE_CHECK;
|
|
pDrive->FileOpenSync( &m_radFile,
|
|
filename,
|
|
false,
|
|
OpenExisting );
|
|
|
|
m_currentFileOperation = temp;
|
|
|
|
#endif // ~RAD_WIN32
|
|
|
|
rAssert( m_radFile );
|
|
bool saveGameExists = false;
|
|
// since for xbox we know the file is definitely there
|
|
// we could get FileNotFound because of old files, just to cut down
|
|
// on bug report, let's treat that as corrupt file
|
|
#ifdef RAD_XBOX
|
|
if (m_lastError==FileNotFound)
|
|
m_lastError = DataCorrupt;
|
|
#endif
|
|
|
|
if (m_lastError==DataCorrupt && file_corrupt_flag)
|
|
{
|
|
*file_corrupt_flag = true;
|
|
saveGameExists = true;
|
|
}
|
|
else
|
|
{
|
|
saveGameExists = m_radFile->IsOpen();
|
|
if( saveGameExists )
|
|
{
|
|
// read save game info from file (synchronously)
|
|
//
|
|
unsigned int numBytes = SaveGameInfo::GetSize();
|
|
|
|
rAssert( m_gameDataBuffer == NULL );
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PushHeap( GMA_TEMP );
|
|
#endif
|
|
m_gameDataBuffer = new GameDataByte[ numBytes ];
|
|
#ifndef WORLD_BUILDER
|
|
HeapMgr()->PopHeap( GMA_TEMP );
|
|
#endif
|
|
rAssert( m_gameDataBuffer != NULL );
|
|
|
|
m_radFile->ReadSync( m_gameDataBuffer, numBytes );
|
|
saveGameInfo->LoadData( m_gameDataBuffer, numBytes );
|
|
|
|
if( m_gameDataBuffer != NULL )
|
|
{
|
|
delete [] m_gameDataBuffer;
|
|
m_gameDataBuffer = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef RAD_XBOX
|
|
strcpy(saveGameInfo->m_displayFilename, filename); // use the filename as the display name
|
|
#else
|
|
saveGameInfo->FormatDisplay( saveGameInfo->m_displayFilename, sizeof(saveGameInfo->m_displayFilename) );
|
|
#endif
|
|
|
|
m_radFile->Release();
|
|
m_radFile = NULL;
|
|
|
|
return saveGameExists;
|
|
}
|
|
|
|
bool
|
|
GameDataManager::DoesSaveGameExist( IRadDrive* pDrive, bool check_valid/* = true*/, bool forAllSlots )
|
|
{
|
|
bool saveGameExists = false;
|
|
unsigned int numSavedGameExists = 0;
|
|
|
|
unsigned int to_check_file = NUM_GAME_SLOTS;
|
|
#ifdef RAD_XBOX
|
|
to_check_file = 1; // on xbox since we query the drive for the file,
|
|
// just check first one
|
|
#endif
|
|
|
|
// check if at least one saved game file exists on the drive
|
|
//
|
|
for( unsigned int i = 0; i < to_check_file; i++ )
|
|
{
|
|
SaveGameInfo saveGameInfo;
|
|
bool corrupt = false;
|
|
if( this->GetSaveGameInfo( pDrive, i, &saveGameInfo, &corrupt ) )
|
|
{
|
|
if( !check_valid ) // no need to check validity
|
|
{
|
|
saveGameExists = true;
|
|
|
|
if( forAllSlots )
|
|
{
|
|
numSavedGameExists++;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
// ok, we found a saved game, but let's make sure the saved game
|
|
// info data are valid so we at least know the file size is correct
|
|
//
|
|
else if( !corrupt && saveGameInfo.CheckData() )
|
|
{
|
|
// everything's cool, we have a valid saved game
|
|
//
|
|
saveGameExists = true;
|
|
|
|
if( forAllSlots )
|
|
{
|
|
numSavedGameExists++;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no save game file for current slot
|
|
//
|
|
if( forAllSlots )
|
|
{
|
|
// if checking for save game file exists in all slots, then
|
|
// we've found a slot w/out a save game file, so stop checking
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( forAllSlots )
|
|
{
|
|
saveGameExists = (numSavedGameExists == NUM_GAME_SLOTS);
|
|
}
|
|
|
|
return saveGameExists;
|
|
}
|
|
|
|
bool
|
|
GameDataManager::FindMostRecentSaveGame( IRadDrive* pDrive,
|
|
unsigned int& slot,
|
|
radDate& timeStamp )
|
|
{
|
|
bool saveGameExists = false;
|
|
|
|
timeStamp.m_Year = 0;
|
|
|
|
// search for most recent save game file on the drive
|
|
//
|
|
for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
|
|
{
|
|
SaveGameInfo saveGameInfo;
|
|
if( this->GetSaveGameInfo( pDrive, i, &saveGameInfo ) )
|
|
{
|
|
if( saveGameInfo.CheckData() )
|
|
{
|
|
// everything's cool, we have a valid saved game
|
|
//
|
|
saveGameExists = true;
|
|
|
|
// now let's check if this is the most recent one thus far
|
|
//
|
|
const SaveGameInfoData* pData = saveGameInfo.GetData();
|
|
rAssert( pData != NULL );
|
|
if( SaveGameInfo::CompareTimeStamps( pData->m_timeStamp, timeStamp ) > 0 )
|
|
{
|
|
memcpy( &timeStamp, &pData->m_timeStamp, sizeof( radDate ) );
|
|
|
|
slot = i; // update most recent saved game slot
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return saveGameExists;
|
|
}
|
|
|
|
void
|
|
GameDataManager::FormatSavedGameFilename( char* filename,
|
|
unsigned int filenameLength,
|
|
unsigned int slot )
|
|
{
|
|
rAssert( filename != NULL );
|
|
rAssert( filenameLength >= 32 );
|
|
|
|
#ifdef RAD_XBOX
|
|
sprintf( filename, "Saved Game (Slot %d)", slot + 1 );
|
|
#else
|
|
sprintf( filename, "%sSave%d", PS2_FILENAME_PREFIX, slot + 1 );
|
|
#endif
|
|
}
|
|
|
|
void
|
|
GameDataManager::OnFileOperationsComplete( void* pUserData )
|
|
{
|
|
switch( m_currentFileOperation )
|
|
{
|
|
// cases during loading
|
|
//
|
|
|
|
case FILE_OP_OPEN_FOR_READING: // done opening file for reading
|
|
{
|
|
rAssert( m_radFile );
|
|
rAssert( m_radFile->IsOpen() );
|
|
|
|
m_radFile->ReadAsync( m_gameDataBuffer,
|
|
m_gameDataSize );
|
|
|
|
m_radFile->AddCompletionCallback( this, NULL );
|
|
m_currentFileOperation = FILE_OP_READ;
|
|
|
|
break;
|
|
}
|
|
|
|
case FILE_OP_READ: // done reading data from file
|
|
{
|
|
rAssert( m_radFile );
|
|
|
|
m_radFile->Release();
|
|
m_radFile = NULL;
|
|
|
|
bool loadOK = this->LoadAllData();
|
|
if( loadOK )
|
|
{
|
|
#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
m_currentFileOperation = FILE_OP_LOAD_COMPLETED;
|
|
#else
|
|
if( m_gameDataLoadCallback != NULL )
|
|
{
|
|
m_gameDataLoadCallback->OnLoadGameComplete( Success );
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
#endif
|
|
rReleasePrintf( ">> Load game completed successfully. (%d ms)\n\n",
|
|
m_elapsedOperationTime );
|
|
}
|
|
else
|
|
{
|
|
if( m_gameDataLoadCallback != NULL )
|
|
{
|
|
m_gameDataLoadCallback->OnLoadGameComplete( DataCorrupt );
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
}
|
|
|
|
// free up temp memory allocated for game data buffer
|
|
//
|
|
if( m_gameDataBuffer != NULL )
|
|
{
|
|
delete [] m_gameDataBuffer;
|
|
m_gameDataBuffer = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// cases during saving
|
|
//
|
|
|
|
case FILE_OP_OPEN_FOR_WRITING: // done opening file for writing
|
|
{
|
|
rAssert( m_radFile );
|
|
rAssert( m_radFile->IsOpen() );
|
|
|
|
m_radFile->WriteAsync( m_gameDataBuffer,
|
|
m_gameDataSize );
|
|
|
|
m_radFile->AddCompletionCallback( this, NULL );
|
|
m_currentFileOperation = FILE_OP_WRITE;
|
|
|
|
break;
|
|
}
|
|
|
|
case FILE_OP_WRITE: // done writing data to file
|
|
{
|
|
rAssert( m_radFile );
|
|
|
|
m_radFile->CommitAsync();
|
|
|
|
m_radFile->AddCompletionCallback( this, NULL );
|
|
m_currentFileOperation = FILE_OP_COMMIT;
|
|
|
|
break;
|
|
}
|
|
|
|
case FILE_OP_COMMIT: // done committing all data writes to file
|
|
{
|
|
rAssert( m_radFile );
|
|
|
|
m_radFile->Release();
|
|
m_radFile = NULL;
|
|
|
|
#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
|
|
m_currentFileOperation = FILE_OP_SAVE_COMPLETED;
|
|
#else
|
|
if( m_gameDataSaveCallback != NULL )
|
|
{
|
|
m_gameDataSaveCallback->OnSaveGameComplete( Success );
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
#endif
|
|
rReleasePrintf( ">> Save game completed successfully. (%d ms)\n\n",
|
|
m_elapsedOperationTime );
|
|
|
|
// free up temp memory allocated for game data buffer
|
|
//
|
|
if( m_gameDataBuffer != NULL )
|
|
{
|
|
delete [] m_gameDataBuffer;
|
|
m_gameDataBuffer = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
rAssertMsg( m_currentFileOperation == FILE_OP_NONE,
|
|
"ERROR: *** Invalid file operation!" );
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
GameDataManager::OnDriveOperationsComplete( void* pUserData )
|
|
{
|
|
switch( m_currentFileOperation )
|
|
{
|
|
case FILE_OP_DELETE:
|
|
{
|
|
m_currentFileOperation = FILE_OP_DELETE_COMPLETED;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
GameDataManager::OnDriveError( radFileError error,
|
|
const char* pDriveName,
|
|
void* pUserData )
|
|
{
|
|
m_lastError = error; // cache error for synchronous call
|
|
switch( m_currentFileOperation )
|
|
{
|
|
case FILE_OP_OPEN_FOR_READING:
|
|
case FILE_OP_READ:
|
|
{
|
|
rTunePrintf( "*** Error [%d] occurred on drive [%s] during loading!\n",
|
|
error, pDriveName );
|
|
|
|
#ifdef RAD_XBOX
|
|
// on xbox we know the filename already
|
|
// this could happen because of old version file format
|
|
|
|
if (m_lastError==FileNotFound)
|
|
m_lastError = DataCorrupt;
|
|
#endif
|
|
|
|
if( m_gameDataLoadCallback != NULL )
|
|
{
|
|
m_gameDataLoadCallback->OnLoadGameComplete( error );
|
|
}
|
|
|
|
if( m_radFile != NULL )
|
|
{
|
|
m_radFile->Release();
|
|
m_radFile = NULL;
|
|
}
|
|
|
|
// free up temp memory allocated for game data buffer
|
|
//
|
|
if( m_gameDataBuffer != NULL )
|
|
{
|
|
delete [] m_gameDataBuffer;
|
|
m_gameDataBuffer = NULL;
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
|
|
break;
|
|
}
|
|
case FILE_OP_OPEN_FOR_WRITING:
|
|
case FILE_OP_WRITE:
|
|
case FILE_OP_COMMIT:
|
|
{
|
|
rTunePrintf( "*** Error [%d] occurred on drive [%s] during saving!\n",
|
|
error, pDriveName );
|
|
|
|
if( m_gameDataSaveCallback != NULL )
|
|
{
|
|
m_gameDataSaveCallback->OnSaveGameComplete( error );
|
|
}
|
|
|
|
if( m_radFile != NULL )
|
|
{
|
|
m_radFile->Release();
|
|
m_radFile = NULL;
|
|
}
|
|
|
|
// free up temp memory allocated for game data buffer
|
|
//
|
|
if( m_gameDataBuffer != NULL )
|
|
{
|
|
delete [] m_gameDataBuffer;
|
|
m_gameDataBuffer = NULL;
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
|
|
break;
|
|
}
|
|
case FILE_OP_DELETE:
|
|
{
|
|
rTunePrintf( "*** Error [%d] occurred on drive [%s] during deleting!\n",
|
|
error, pDriveName );
|
|
|
|
if( m_gameDataDeleteCallback != NULL )
|
|
{
|
|
m_gameDataDeleteCallback->OnDeleteGameComplete( error );
|
|
m_gameDataDeleteCallback = NULL;
|
|
}
|
|
|
|
m_currentFileOperation = FILE_OP_NONE;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// rDebugPrintf( "*** WARNING: Unhandled drive error [%d] occurred on drive: %s\n",
|
|
// error, pDriveName );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we should always return false, since we're always going to prompt
|
|
// the user first before attempting to retry any operation
|
|
//
|
|
return false;
|
|
}
|
|
|
|
bool GameDataManager::IsUsingDrive() const
|
|
{
|
|
return m_currentFileOperation != FILE_OP_NONE;
|
|
}
|
|
|
|
//===========================================================================
|
|
// Private Member Functions
|
|
//===========================================================================
|
|
|
|
bool
|
|
GameDataManager::LoadAllData()
|
|
{
|
|
GameDataByte* currentGameDataBuffer = m_gameDataBuffer;
|
|
|
|
for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
|
|
{
|
|
rAssert( m_registeredGameData[ i ] );
|
|
|
|
// get number of bytes for current game data
|
|
//
|
|
unsigned int numDataBytes = m_registeredGameData[ i ]->m_numBytes;
|
|
rAssert( numDataBytes >= 0 );
|
|
|
|
#ifdef PRINT_RAW_DATA
|
|
rDebugPrintf( "Game Data Load (%s): ",
|
|
m_registeredGameData[ i ]->m_name );
|
|
|
|
this->PrintRawData( currentGameDataBuffer, numDataBytes );
|
|
|
|
rDebugPrintf( "\n" );
|
|
#endif
|
|
|
|
// call current game data handler to load data
|
|
//
|
|
rAssert( m_registeredGameData[ i ]->m_gdHandler );
|
|
m_registeredGameData[ i ]->m_gdHandler->LoadData( currentGameDataBuffer,
|
|
numDataBytes );
|
|
|
|
if( i == 0 ) // 0 = saved game info data handler
|
|
{
|
|
bool isDataValid = m_saveGameInfoHandler->CheckData();
|
|
if( !isDataValid )
|
|
{
|
|
// saved game info data is not valid, assume file corrupted
|
|
//
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// advance reference to current game data buffer
|
|
//
|
|
currentGameDataBuffer += numDataBytes;
|
|
}
|
|
|
|
// set flag indicating that a saved game has been loaded
|
|
//
|
|
m_isGameLoaded = true;
|
|
|
|
// sanity check
|
|
//
|
|
rAssert( currentGameDataBuffer - m_gameDataBuffer == static_cast<int>( m_gameDataSize ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
GameDataManager::SaveAllData()
|
|
{
|
|
MEMTRACK_PUSH_GROUP( "GameDataManager" );
|
|
GameDataByte* currentGameDataBuffer = m_gameDataBuffer;
|
|
|
|
for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
|
|
{
|
|
rAssert( m_registeredGameData[ i ] );
|
|
|
|
// get number of bytes for current game data
|
|
//
|
|
unsigned int numDataBytes = m_registeredGameData[ i ]->m_numBytes;
|
|
rAssert( numDataBytes >= 0 );
|
|
/*
|
|
// create temporary buffer for client to write in
|
|
//
|
|
GameDataByte* tempDataBuffer = new( GMA_TEMP ) GameDataByte[ numDataBytes ];
|
|
rAssert( tempDataBuffer );
|
|
*/
|
|
// call current game data handler to save data
|
|
//
|
|
rAssert( m_registeredGameData[ i ]->m_gdHandler );
|
|
m_registeredGameData[ i ]->m_gdHandler->SaveData( currentGameDataBuffer,
|
|
numDataBytes );
|
|
/*
|
|
// copy data from temporary buffer to current buffer
|
|
//
|
|
memcpy( currentGameDataBuffer, tempDataBuffer, numDataBytes );
|
|
|
|
// and destroy temporary data buffer
|
|
//
|
|
if( tempDataBuffer != NULL )
|
|
{
|
|
delete tempDataBuffer;
|
|
tempDataBuffer = NULL;
|
|
}
|
|
*/
|
|
#ifdef PRINT_RAW_DATA
|
|
rDebugPrintf( "Game Data Save (%s): ",
|
|
m_registeredGameData[ i ]->m_name );
|
|
|
|
this->PrintRawData( currentGameDataBuffer, numDataBytes );
|
|
|
|
rDebugPrintf( "\n" );
|
|
#endif
|
|
|
|
// advance reference to current game data buffer
|
|
//
|
|
currentGameDataBuffer += numDataBytes;
|
|
}
|
|
|
|
// sanity check
|
|
//
|
|
rAssert( currentGameDataBuffer - m_gameDataBuffer == static_cast<int>( m_gameDataSize ) );
|
|
MEMTRACK_POP_GROUP("GameDataManager");
|
|
return true;
|
|
}
|
|
|
|
void
|
|
GameDataManager::PrintRawData( GameDataByte* dataBuffer,
|
|
unsigned int numBytes )
|
|
{
|
|
rAssert( dataBuffer != NULL );
|
|
|
|
for( unsigned int i = 0; i < numBytes; i++ )
|
|
{
|
|
rDebugPrintf( "[ %02X ]", dataBuffer[ i ] );
|
|
}
|
|
}
|
|
|