//=========================================================================== // 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 #include #include #include #ifndef WORLD_BUILDER #include #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 #include #include #ifdef RAD_PS2 #include #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( 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( 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( 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 ] ); } }