| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program 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.
- **
- ** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: GameState.cpp ////////////////////////////////////////////////////////////////////////////
- // Author: Colin Day, September 2002
- // Desc: Game state singleton from which to load and save the game state
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h"
- #include "Common/File.h"
- #include "Common/FileSystem.h"
- #include "Common/GameEngine.h"
- #include "Common/GameState.h"
- #include "Common/GameStateMap.h"
- #include "Common/LatchRestore.h"
- #include "Common/MapObject.h"
- #include "Common/PlayerList.h"
- #include "Common/RandomValue.h"
- #include "Common/Radar.h"
- #include "Common/Team.h"
- #include "Common/WellKnownKeys.h"
- #include "Common/XferLoad.h"
- #include "Common/XferSave.h"
- #include "GameClient/CampaignManager.h"
- #include "GameClient/GadgetListBox.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/GameText.h"
- #include "GameClient/MapUtil.h"
- #include "GameClient/MessageBox.h"
- #include "GameClient/InGameUI.h"
- #include "GameClient/ParticleSys.h"
- #include "GameClient/TerrainVisual.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/GhostObject.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/SidesList.h"
- #include "GameLogic/TerrainLogic.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // PUBLIC DATA ////////////////////////////////////////////////////////////////////////////////////
- GameState *TheGameState = NULL;
- // PRIVATE DATA ///////////////////////////////////////////////////////////////////////////////////
- static const Char *SAVE_FILE_EOF = "SG_EOF";
- static const Char *SAVE_GAME_EXTENSION = ".sav";
- static const Char *ZERO_NAME_ONLY = "00000000";
- static const Int MAX_SAVE_FILE_NUMBER = 99999999;
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #define GAME_STATE_BLOCK_STRING "CHUNK_GameState" // block of save game data with game info data
- #define CAMPAIGN_BLOCK_STRING "CHUNK_Campaign" // block of game data that has campaign info
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- SaveGameInfo::SaveGameInfo( void )
- {
- date.day = 0;
- date.dayOfWeek = 0;
- date.hour = 0;
- date.milliseconds = 0;
- date.minute = 0;
- date.month = 0;
- date.second = 0;
- date.year = 0;
- missionNumber = 0;
- saveFileType = SAVE_FILE_TYPE_NORMAL;
- } // end SaveGameInfo
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- SaveGameInfo::~SaveGameInfo( void )
- {
- } // end ~SaveGameInfo
- // ------------------------------------------------------------------------------------------------
- /** Is this date newer than the other one passed in? */
- // ------------------------------------------------------------------------------------------------
- Bool SaveDate::isNewerThan( SaveDate *other )
- {
- // year
- if( year > other->year )
- return TRUE;
- else if( year < other->year )
- return FALSE;
- else
- {
- // month
- if( month > other->month )
- return TRUE;
- else if( month < other->month )
- return FALSE;
- else
- {
-
- // day
- if( day > other->day )
- return TRUE;
- else if( day < other->day )
- return FALSE;
- else
- {
- // hour
- if( hour > other->hour )
- return TRUE;
- else if( hour < other->hour )
- return FALSE;
- else
- {
- // minute
- if( minute > other->minute )
- return TRUE;
- else if( minute < other->minute )
- return FALSE;
- else
- {
- // second
- if( second > other->second )
- return TRUE;
- else if( second < other->second )
- return FALSE;
- else
- {
- // millisecond
- if( milliseconds > other->milliseconds )
- return TRUE;
- else
- return FALSE;
- } // end else
- } // end else
- } // end else
- } // end else
- } // end else
- } // end else
- } // end isNewerThan
- // ------------------------------------------------------------------------------------------------
- /** Find a snapshot block info that matches the token passed in */
- // ------------------------------------------------------------------------------------------------
- GameState::SnapshotBlock *GameState::findBlockInfoByToken( AsciiString token, SnapshotType which )
- {
- // sanity
- if( token.isEmpty() )
- return NULL;
- // search for match our list
- SnapshotBlock *blockInfo;
- SnapshotBlockListIterator it;
- for( it = m_snapshotBlockList[which].begin(); it != m_snapshotBlockList[which].end(); ++it )
- {
- // get info
- blockInfo = &(*it);
- // check for match
- if( blockInfo->blockName == token )
- return blockInfo;
- } // end for
- // not found
- return NULL;
- } // end findLexiconEntryByToken
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- UnicodeString getUnicodeDateBuffer(SYSTEMTIME timeVal)
- {
- // setup date buffer for local region date format
- #define DATE_BUFFER_SIZE 256
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
- UnicodeString displayDateBuffer;
- if (GetVersionEx(&osvi))
- { //check if we're running Win9x variant since they may need different characters
- if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
- {
- char dateBuffer[ DATE_BUFFER_SIZE ];
- GetDateFormat( LOCALE_SYSTEM_DEFAULT,
- DATE_SHORTDATE,
- &timeVal,
- NULL,
- dateBuffer, sizeof(dateBuffer) );
- displayDateBuffer.translate(dateBuffer);
- return displayDateBuffer;
- }
- }
- wchar_t dateBuffer[ DATE_BUFFER_SIZE ];
- GetDateFormatW( LOCALE_SYSTEM_DEFAULT,
- DATE_SHORTDATE,
- &timeVal,
- NULL,
- dateBuffer, sizeof(dateBuffer) );
- displayDateBuffer.set(dateBuffer);
- return displayDateBuffer;
- //displayDateBuffer.format( L"%ls", dateBuffer );
- }
- UnicodeString getUnicodeTimeBuffer(SYSTEMTIME timeVal)
- {
- // setup time buffer for local region time format
- UnicodeString displayTimeBuffer;
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
- if (GetVersionEx(&osvi))
- { //check if we're running Win9x variant since they may need different characters
- if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
- {
- char timeBuffer[ DATE_BUFFER_SIZE ];
- GetTimeFormat( LOCALE_SYSTEM_DEFAULT,
- TIME_NOSECONDS|TIME_FORCE24HOURFORMAT|TIME_NOTIMEMARKER,
- &timeVal,
- NULL,
- timeBuffer, sizeof(timeBuffer) );
- displayTimeBuffer.translate(timeBuffer);
- return displayTimeBuffer;
- }
- }
- // setup time buffer for local region time format
- #define TIME_BUFFER_SIZE 256
- wchar_t timeBuffer[ TIME_BUFFER_SIZE ];
- GetTimeFormatW( LOCALE_SYSTEM_DEFAULT,
- TIME_NOSECONDS,
- &timeVal,
- NULL,
- timeBuffer,
- sizeof(timeBuffer) );
- displayTimeBuffer.set(timeBuffer);
- return displayTimeBuffer;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- GameState::GameState( void )
- {
- m_availableGames = NULL;
- m_isInLoadGame = FALSE;
- } // end GameState
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- GameState::~GameState( void )
- {
- // clear our snapshot block list
- for (Int i=0; i<SNAPSHOT_MAX; ++i)
- m_snapshotBlockList[i].clear();
- // make certain that the post process list is clean
- m_snapshotPostProcessList.clear();
- // clear any available game
- clearAvailableGames();
- } // end ~GameState
- // ------------------------------------------------------------------------------------------------
- /** Init the game state subsystem */
- // ------------------------------------------------------------------------------------------------
- void GameState::init( void )
- {
- // add all the snapshot objects to our list of data blocks for save game files
- addSnapshotBlock( GAME_STATE_BLOCK_STRING, TheGameState, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( CAMPAIGN_BLOCK_STRING, TheCampaignManager, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_GameStateMap", TheGameStateMap, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_TerrainLogic", TheTerrainLogic, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_TeamFactory", TheTeamFactory, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_Players", ThePlayerList, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_GameLogic", TheGameLogic, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_Radar", TheRadar, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_ScriptEngine", TheScriptEngine, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_SidesList", TheSidesList, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_TacticalView", TheTacticalView, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_GameClient", TheGameClient, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_InGameUI", TheInGameUI, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_Partition", ThePartitionManager, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_ParticleSystem", TheParticleSystemManager, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_TerrainVisual", TheTerrainVisual, SNAPSHOT_SAVELOAD );
- addSnapshotBlock( "CHUNK_GhostObject", TheGhostObjectManager, SNAPSHOT_SAVELOAD );
- // add all the snapshot objects to our list of data blocks for deep CRCs of logic
- addSnapshotBlock( "CHUNK_TeamFactory", TheTeamFactory, SNAPSHOT_DEEPCRC_LOGICONLY );
- addSnapshotBlock( "CHUNK_Players", ThePlayerList, SNAPSHOT_DEEPCRC_LOGICONLY );
- addSnapshotBlock( "CHUNK_GameLogic", TheGameLogic, SNAPSHOT_DEEPCRC_LOGICONLY );
- addSnapshotBlock( "CHUNK_ScriptEngine", TheScriptEngine, SNAPSHOT_DEEPCRC_LOGICONLY );
- addSnapshotBlock( "CHUNK_SidesList", TheSidesList, SNAPSHOT_DEEPCRC_LOGICONLY );
- addSnapshotBlock( "CHUNK_Partition", ThePartitionManager, SNAPSHOT_DEEPCRC_LOGICONLY );
- m_isInLoadGame = FALSE;
- } // end init
- // ------------------------------------------------------------------------------------------------
- /** Reset */
- // ------------------------------------------------------------------------------------------------a
- void GameState::reset( void )
- {
- // clear the post process snapshot list
- m_snapshotPostProcessList.clear();
- // clear any available game
- clearAvailableGames();
- m_isInLoadGame = FALSE;
- } // end reset
- // ------------------------------------------------------------------------------------------------
- /** Clear any available games entries */
- // ------------------------------------------------------------------------------------------------
- void GameState::clearAvailableGames( void )
- {
- AvailableGameInfo *gameInfo;
- while( m_availableGames )
- {
- gameInfo = m_availableGames->next;
- delete m_availableGames;
- m_availableGames = gameInfo;
- } // end while
- } // end clearAvailableGames
- // ------------------------------------------------------------------------------------------------
- /** Add a snapshot and block name pair to the systems used to load and save */
- // ------------------------------------------------------------------------------------------------
- void GameState::addSnapshotBlock( AsciiString blockName, Snapshot *snapshot, SnapshotType which )
- {
- // sanity
- if( blockName.isEmpty() || snapshot == NULL )
- {
- DEBUG_CRASH(( "addSnapshotBlock: Invalid parameters\n" ));
- return;
- } // end if
- // add to the list
- SnapshotBlock blockInfo;
- blockInfo.snapshot = snapshot;
- blockInfo.blockName = blockName;
- m_snapshotBlockList[which].push_back( blockInfo );
- } // end addSnapshotBlock
- // ------------------------------------------------------------------------------------------------
- /** Given the filename of a save file, find the highest filename number */
- // ------------------------------------------------------------------------------------------------
- static void findHighFileNumber( AsciiString filename, void *userData )
- {
- // sanity
- if( filename.isEmpty() )
- return;
- // sanity check for ".sav" at the end of the filename
- if( filename.endsWithNoCase( SAVE_GAME_EXTENSION ) == FALSE )
- return;
- // strip off the extension at the end of the filename
- AsciiString nameOnly = filename;
- for( Int count = 0; count < strlen( SAVE_GAME_EXTENSION ); count++ )
- nameOnly.removeLastChar();
-
- // convert filename (which is only numbers) to a number
- Int fileNumber = atoi( nameOnly.str() );
- //
- // atoi will return zero if the string could not be converted, if the filename is
- // not literally "00000000.sav" then that means the conversion could not be done so
- // we should reject this filename from further processing
- //
- if( fileNumber == 0 && nameOnly.compare( ZERO_NAME_ONLY ) != 0 )
- return;
- // compare against the file number we're keeping tally of in the user data parameter
- Int *highFileNumber = (Int *)userData;
- if( fileNumber >= *highFileNumber )
- *highFileNumber = fileNumber;
- } // end findHighFileNumber
- // ------------------------------------------------------------------------------------------------
- /** Given the save files on disk, find the "next" filename to use when saving a game */
- // ------------------------------------------------------------------------------------------------
- AsciiString GameState::findNextSaveFilename( UnicodeString desc )
- {
- // works, but needs approval from mgmt (srj)
- // GS activating for patch
- #define COCKBEER_DONT_USE_NORMAL_FILE_NAMES_THANKS
- #ifdef USE_NORMAL_FILE_NAMES_THANKS
- Int i;
- AsciiString adesc;
- for (i = 0; i < desc.getLength(); ++i)
- {
- char c = (char)desc.getCharAt(i);
- if (isalnum(c))
- adesc.concat(c);
- else
- adesc.concat('_');
- }
- for (i = 1; i <= 9999; ++i)
- {
- AsciiString leaf;
- leaf.format("%s_%04d%s", adesc.str(), i, SAVE_GAME_EXTENSION);
- AsciiString path = getFilePathInSaveDirectory(leaf);
- if( _access( path.str(), 0 ) == -1 )
- return leaf; // note that this returns the leaf, not the full path
- }
- #else
- //
- // This method has code to support two modes of finding the next filename, one of
- // them starts with a filename of 00000000.sav and counts up the number looking for
- // a file that doesn't exist and therefore a filename we can use (LOWEST_NUMBER).
- // The other method iterates all save files and returns a filename that is one larger
- // than the largest filename number encountered (HIGHEST_NUMBER)
- //
- enum FindNextFileType { LOWEST_NUMBER = 0, HIGHEST_NUMBER = 1 };
- FindNextFileType searchType = LOWEST_NUMBER;
- if( searchType == HIGHEST_NUMBER )
- {
- // iterate all the save files in the directory and find the highest file number
- Int highFileNumber = -1;
- iterateSaveFiles( findHighFileNumber, &highFileNumber );
- // check for a filename that is too big (this is unlikely but theoretically possible)
- if( highFileNumber + 1 > MAX_SAVE_FILE_NUMBER )
- return AsciiString::TheEmptyString;
- // construct filename with a number higher than the highest one we found
- AsciiString filename;
- filename.format( "%08d%s", highFileNumber + 1, SAVE_GAME_EXTENSION );
- return filename;
-
- } // end if
- else if( searchType == LOWEST_NUMBER )
- {
- AsciiString filename;
- AsciiString fullPath;
- Int i = 0;
- while( TRUE )
- {
- // construct filename (########.sav)
- filename.format( "%08d%s", i, SAVE_GAME_EXTENSION );
- // construct full path to file given the filename
- fullPath = getFilePathInSaveDirectory(filename);
- // if file does not exist we're all good
- if( _access( fullPath.str(), 0 ) == -1 )
- return filename;
- // test the text filename
- i++;
- // check for at the max limit (this is highly unlikely but possible)
- if( i > MAX_SAVE_FILE_NUMBER )
- return AsciiString::TheEmptyString;
- } // end while
- } // end else if
- else
- {
- DEBUG_CRASH(( "GameState::findNextSaveFilename - Unknown file search type '%d'\n", searchType ));
- return AsciiString::TheEmptyString;
- } // end else
- #endif
- // no appropriate filename could be found, return the empty string
- return AsciiString::TheEmptyString;
- } // end findNextSaveFilename
- // ------------------------------------------------------------------------------------------------
- /** Save the current state of the engine in a save file
- * NOTE: filename is a *filename only* */
- // ------------------------------------------------------------------------------------------------
- SaveCode GameState::saveGame( AsciiString filename, UnicodeString desc,
- SaveFileType saveType, SnapshotType which )
- {
- // if there is no filename, this is a new file being created, find an appropriate filename
- if( filename.isEmpty() )
- filename = findNextSaveFilename( desc );
- if( filename.isEmpty() )
- {
- DEBUG_CRASH(( "GameState::saveGame - Unable to find valid filename for save game\n" ));
- return SC_NO_FILE_AVAILABLE;
- } // end if
- // make absolutely sure the save directory exists
- CreateDirectory( getSaveDirectory().str(), NULL );
- // construct path to file
- AsciiString filepath = getFilePathInSaveDirectory(filename);
- // save description as current description in the game state
- m_gameInfo.description = desc;
- // open the save file
- XferSave xferSave;
- try {
- xferSave.open( filepath );
- } catch(...) {
- // print error message to the user
- TheInGameUI->message( "GUI:Error" );
- DEBUG_LOG(( "Error opening file '%s'\n", filepath.str() ));
- return SC_ERROR;
- }
- // save our save file type
- SaveGameInfo *gameInfo = getSaveGameInfo();
- gameInfo->saveFileType = saveType;
- // save our mission map name if applicable
- if( saveType == SAVE_FILE_TYPE_MISSION )
- gameInfo->missionMapName = TheCampaignManager->getCurrentMap();
- else
- gameInfo->missionMapName.clear();
- // set the pristine map to the current campaign map
- // this is now done during startNewGame()
- // gameInfo->pristineMapName = TheCampaignManager->getCurrentMap();
- // write the save file
- try
- {
- // save file
- xferSaveData( &xferSave, which );
- } // end try
- catch( ... )
- {
- UnicodeString ufilepath;
- ufilepath.translate(filepath);
- UnicodeString msg;
- msg.format( TheGameText->fetch("GUI:ErrorSavingGame"), ufilepath.str() );
- MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, NULL);
- // close the file and get out of here
- xferSave.close();
- return SC_ERROR;
-
- } // end catch
- // close the file
- xferSave.close();
- // print message to the user for game successfully saved
- UnicodeString msg = TheGameText->fetch( "GUI:GameSaveComplete" );
- TheInGameUI->message( msg );
- return SC_OK;
- } // end saveGame
- // ------------------------------------------------------------------------------------------------
- /** A mission save */
- // ------------------------------------------------------------------------------------------------
- SaveCode GameState::missionSave( void )
- {
- // get campaign
- Campaign *campaign = TheCampaignManager->getCurrentCampaign();
- // get mission #
- Int missionNumber = TheCampaignManager->getCurrentMissionNumber() + 1;
- // format a string for the mission save description
- UnicodeString format = TheGameText->fetch( "GUI:MissionSave" );
- UnicodeString desc;
- desc.format( format, TheGameText->fetch( campaign->m_campaignNameLabel ).str(), missionNumber );
- // do an automatic mission save
- return TheGameState->saveGame( AsciiString(""), desc, SAVE_FILE_TYPE_MISSION );
- } // end missionSave
- // ------------------------------------------------------------------------------------------------
- /** Load the save game pointed to by filename */
- // ------------------------------------------------------------------------------------------------
- SaveCode GameState::loadGame( AvailableGameInfo gameInfo )
- {
- // sanity check for file
- if( doesSaveGameExist( gameInfo.filename ) == FALSE )
- return SC_FILE_NOT_FOUND;
- // clear game data just like loading from the debug map load screen for mission saves
- if( gameInfo.saveGameInfo.saveFileType == SAVE_FILE_TYPE_MISSION )
- {
- if (TheGameLogic->isInGame())
- TheGameLogic->clearGameData( FALSE );
- } // end if
- //
- // clear the save directory of any temporary "scratch pad" maps that were extracted
- // from any previously loaded save game files
- //
- TheGameStateMap->clearScratchPadMaps();
- // construct path to file
- AsciiString filepath = getFilePathInSaveDirectory(gameInfo.filename);
- // open the save file
- XferLoad xferLoad;
- xferLoad.open( filepath );
- // clear out the game engine
- TheGameEngine->reset();
- // lock creation of new ghost objects
- TheGhostObjectManager->saveLockGhostObjects( TRUE );
- LatchRestore<Bool> inLoadGame(m_isInLoadGame, TRUE);
- // load the save data
- Bool error = FALSE;
- try
- {
- // load file
- xferSaveData( &xferLoad, SNAPSHOT_SAVELOAD );
- } // end try
- catch( ... )
- {
- error = TRUE;
- } // end catch
- // close the file
- xferLoad.close();
- // un-savelock the ghost objects
- TheGhostObjectManager->saveLockGhostObjects( FALSE );
- try
- {
- // do the post-process from a save game load
- gameStatePostProcessLoad();
- }
- catch (...)
- {
- error = TRUE;
- }
- // check for error
- if( error == TRUE )
- {
- // clear it out, again
- if (TheGameLogic->isInGame())
- TheGameLogic->clearGameData( FALSE );
- TheGameEngine->reset();
- // print error message to the user
- UnicodeString ufilepath;
- ufilepath.translate(filepath);
- UnicodeString msg;
- msg.format( TheGameText->fetch("GUI:ErrorLoadingGame"), ufilepath.str() );
- MessageBoxOk(TheGameText->fetch("GUI:Error"), msg, NULL);
- return SC_INVALID_DATA; // you can't use a naked "throw" outside of a catch statement!
- } // end if
- //
- // when loading a mission save, we want to do as much normal loading stuff as we
- // can cause we don't have any real save game data to load other than the
- // game state map info and campaign manager stuff
- //
- if( getSaveGameInfo()->saveFileType == SAVE_FILE_TYPE_MISSION )
- {
- InitRandom(0);
- TheWritableGlobalData->m_pendingFile = getSaveGameInfo()->missionMapName;
- GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_NEW_GAME );
- msg->appendIntegerArgument(GAME_SINGLE_PLAYER);
- msg->appendIntegerArgument(TheCampaignManager->getGameDifficulty());
- msg->appendIntegerArgument(TheCampaignManager->getRankPoints());
- // remove the mission save data, we've got all we need and have started the load
- SaveGameInfo *gameInfo = getSaveGameInfo();
- gameInfo->saveFileType = SAVE_FILE_TYPE_NORMAL;
- gameInfo->missionMapName.clear();
- } // end if
-
- return SC_OK;
- } // end loadGame
- //-------------------------------------------------------------------------------------------------
- AsciiString GameState::getSaveDirectory() const
- {
- AsciiString tmp = TheGlobalData->getPath_UserData();
- tmp.concat("Save\\");
- return tmp;
- }
- //-------------------------------------------------------------------------------------------------
- AsciiString GameState::getFilePathInSaveDirectory(const AsciiString& leaf) const
- {
- AsciiString tmp = getSaveDirectory();
- tmp.concat(leaf);
- return tmp;
- }
- //-------------------------------------------------------------------------------------------------
- Bool GameState::isInSaveDirectory(const AsciiString& path) const
- {
- return path.startsWithNoCase(getSaveDirectory());
- }
- // ------------------------------------------------------------------------------------------------
- AsciiString GameState::getMapLeafName(const AsciiString& in) const
- {
- char* p = strrchr(in.str(), '\\');
- if (p)
- {
- //
- // p points to the last '\' (if found), however, if a '\' was found there better
- // be another character beyond it, otherwise the map filename would actually
- // be a *directory* Just move to the first character beyond it so we are looking
- // at the name only
- //
- ++p;
- DEBUG_ASSERTCRASH( p != NULL && *p != 0, ("GameState::xfer - Illegal map name encountered\n") );
- return p;
- }
- else
- {
- return in;
- }
- }
- // ------------------------------------------------------------------------------------------------
- static const char* findLastBackslashInRangeInclusive(const char* start, const char* end)
- {
- while (end >= start)
- {
- if (*end == '\\')
- return end;
- --end;
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- static AsciiString getMapLeafAndDirName(const AsciiString& in)
- {
- const char* start = in.str();
- const char* end = in.str() + in.getLength() - 1;
- const char* p = findLastBackslashInRangeInclusive(start, end);
- if (p)
- {
- const char* p2 = findLastBackslashInRangeInclusive(start, p-1);
- if (p2)
- {
- // we have something like:
- // maps\foo\foo.map
- // c:\mydocs\c&cdata\maps\foo\foo.map
- return p2 + 1;
- }
- else
- {
- // we have something like:
- // save\foo.map
- return in;
- }
- }
- else
- {
- DEBUG_CRASH(("Illegal map-dir-name... should have at least one backslash"));
- return in;
- }
- }
- // ------------------------------------------------------------------------------------------------
- static AsciiString removeExtension(const AsciiString& in)
- {
- char buf[1024];
- strcpy(buf, in.str());
- char* p = strrchr(buf, '.');
- if (p)
- {
- *p = 0;
- }
- return AsciiString(buf);
- }
- // ------------------------------------------------------------------------------------------------
- const char* PORTABLE_SAVE = "Save\\";
- const char* PORTABLE_MAPS = "Maps\\";
- const char* PORTABLE_USER_MAPS = "UserData\\Maps\\";
- // ------------------------------------------------------------------------------------------------
- AsciiString GameState::realMapPathToPortableMapPath(const AsciiString& in) const
- {
- AsciiString prefix;
- if (in.startsWithNoCase(getSaveDirectory()))
- {
- prefix = PORTABLE_SAVE;
- prefix.concat(getMapLeafName(in));
- }
- else if (in.startsWithNoCase(TheMapCache->getMapDir()))
- {
- prefix = PORTABLE_MAPS;
- prefix.concat(getMapLeafAndDirName(in));
- }
- else if (in.startsWithNoCase(TheMapCache->getUserMapDir()))
- {
- prefix = PORTABLE_USER_MAPS;
- prefix.concat(getMapLeafAndDirName(in));
- }
- else
- {
- DEBUG_CRASH(("Map file was not found in any of the expected directories; this is impossible"));
- //throw INI_INVALID_DATA;
- // uncaught exceptions crash us. better to just use a bad path.
- prefix = in;
- }
- prefix.toLower();
- return prefix;
- }
- // ------------------------------------------------------------------------------------------------
- AsciiString GameState::portableMapPathToRealMapPath(const AsciiString& in) const
- {
- AsciiString prefix;
- if (in.startsWithNoCase(PORTABLE_SAVE))
- {
- // the save dir ends with "\\"
- prefix = getSaveDirectory();
- prefix.concat(getMapLeafName(in));
- }
- else if (in.startsWithNoCase(PORTABLE_MAPS))
- {
- // the map dir DOES NOT end with "\\", must add it
- prefix = TheMapCache->getMapDir();
- prefix.concat("\\");
- prefix.concat(getMapLeafAndDirName(in));
- }
- else if (in.startsWithNoCase(PORTABLE_USER_MAPS))
- {
- // the map dir DOES NOT end with "\\", must add it
- prefix = TheMapCache->getUserMapDir();
- prefix.concat("\\");
- prefix.concat(getMapLeafAndDirName(in));
- }
- else
- {
- DEBUG_CRASH(("Map file was not found in any of the expected directories; this is impossible"));
- //throw INI_INVALID_DATA;
- // uncaught exceptions crash us. better to just use a bad path.
- prefix = in;
- }
- prefix.toLower();
- return prefix;
- }
- // ------------------------------------------------------------------------------------------------
- /** Does the save game file exist */
- // ------------------------------------------------------------------------------------------------
- Bool GameState::doesSaveGameExist( AsciiString filename )
- {
- // construct full path to file
- AsciiString filepath = getFilePathInSaveDirectory(filename);
- // open file
- XferLoad xfer;
- try
- {
- // try to open it
- xfer.open( filepath );
- } // end try
- catch( ... )
- {
- // unable to open file, it must not be here
- return FALSE;
- } // end catch
-
- // close the file, we don't want to to anything with it right now
- xfer.close();
- return TRUE;
- } // doesSaveGameExist
- // ------------------------------------------------------------------------------------------------
- /** Get save game info from the filename specified */
- // ------------------------------------------------------------------------------------------------
- void GameState::getSaveGameInfoFromFile( AsciiString filename, SaveGameInfo *saveGameInfo )
- {
- AsciiString token;
- Int blockSize;
- Bool done = FALSE;
- SnapshotBlock *blockInfo;
- // sanity
- if( filename.isEmpty() == TRUE || saveGameInfo == NULL )
- {
- DEBUG_CRASH(( "GameState::getSaveGameInfoFromFile - Illegal parameters\n" ));
- return;
- } // end if
- // open file for partial loading
- XferLoad xferLoad;
- xferLoad.open( filename );
- //
- // disable post processing cause we're not really doing a load of game data that
- // needs post processing and we don't want to keep track of any snapshots we loaded
- //
- xferLoad.setOptions( XO_NO_POST_PROCESSING );
- // read all data blocks in the file
- while( done == FALSE )
- {
- // read next token
- xferLoad.xferAsciiString( &token );
- // check for end of file token
- if( token.compareNoCase( SAVE_FILE_EOF ) == 0 )
- {
- // we should never get here, if we did, we didn't find block of data we needed
- DEBUG_CRASH(( "GameState::getSaveGameInfoFromFile - Game info not found in file '%s'\n", filename.str() ));
- done = TRUE;
- } // end if
- else
- {
- // find matching token in the save file lexicon
- blockInfo = findBlockInfoByToken( token, SNAPSHOT_SAVELOAD );
- if( blockInfo == NULL )
- throw SC_UNKNOWN_BLOCK;
- // read the data size of this block
- blockSize = xferLoad.beginBlock();
- // is this the block of game info data
- if( stricmp( token.str(), GAME_STATE_BLOCK_STRING ) == 0 )
- {
- GameState tempGameState;
- // parse this data
- try
- {
- // load data
- xferLoad.xferSnapshot( &tempGameState );
- } // end try
- catch( ... )
- {
- DEBUG_CRASH(( "GameState::getSaveGameInfoFromFile - Error loading block '%s' in file '%s'\n",
- blockInfo->blockName.str(), filename.str() ));
- throw;
- } // end catch
- // data was found, copy game state info over
- *saveGameInfo = *tempGameState.getSaveGameInfo();
-
- // we're all done with this file now
- done = TRUE;
- } // end if
- else
- {
- // not a block we care about, just skip it
- xferLoad.skip( blockSize );
- // end of block
- xferLoad.endBlock();
- } // end else
- } // end else, valid data block token
- } // end while, not done
- // close the file
- xferLoad.close();
- } // end getSaveGameInfoFromFile
- // ------------------------------------------------------------------------------------------------
- /** Create game info and add to available list */
- // ------------------------------------------------------------------------------------------------
- static void addGameToAvailableList( AsciiString filename, void *userData )
- {
- AvailableGameInfo **listHead = (AvailableGameInfo **)userData;
- // sanity
- DEBUG_ASSERTCRASH( listHead != NULL, ("addGameToAvailableList - Illegal parameters\n") );
- DEBUG_ASSERTCRASH( filename.isEmpty() == FALSE, ("addGameToAvailableList - Illegal filename\n") );
-
- try {
- // get header info from this listbox
- SaveGameInfo saveGameInfo;
- TheGameState->getSaveGameInfoFromFile( filename, &saveGameInfo );
- // allocate new info
- AvailableGameInfo *newInfo = new AvailableGameInfo;
- // assign data
- newInfo->prev = NULL;
- newInfo->next = NULL;
- newInfo->saveGameInfo = saveGameInfo;
- newInfo->filename = filename;
- // attach to list
- if( *listHead == NULL )
- *listHead = newInfo;
- else
- {
- AvailableGameInfo *curr, *prev;
- // insert this info so that the most recent games are always at the top of this list
- for( curr = *listHead; curr != NULL; curr = curr->next )
- {
- // save current as previous
- prev = curr;
- // check to see if curr is older than the new info, if so, put new info just ahead of curr
- if( newInfo->saveGameInfo.date.isNewerThan( &curr->saveGameInfo.date ) )
- {
- if( curr->prev )
- curr->prev->next = newInfo;
- else
- *listHead = newInfo;
- newInfo->prev = curr->prev;
- curr->prev = newInfo;
- newInfo->next = curr;
- break;
- } // end if
- } // end for
- // if not inserted, put at end
- if( curr == NULL )
- {
- prev->next = newInfo;
- newInfo->prev = prev;
- } // end if
- } // end else
- } catch(...) {
- // Do nothing - just return.
- }
- } // end addGameToAvailableList
- // ------------------------------------------------------------------------------------------------
- /** Populate the listbox passed in with a list of the save games present on the hard drive */
- // ------------------------------------------------------------------------------------------------
- void GameState::populateSaveGameListbox( GameWindow *listbox, SaveLoadLayoutType layoutType )
- {
- Int index;
- // sanity
- if( listbox == NULL )
- return;
- // first clear all entries in the listbox
- GadgetListBoxReset( listbox );
- // setup the first entry of the listbox to be a new game when saving is allowed
- if( layoutType != SLLT_LOAD_ONLY )
- {
- UnicodeString newGameText = TheGameText->fetch( "GUI:NewSaveGame" );
- Color newGameColor = GameMakeColor( 200, 200, 255, 255 );
- index = GadgetListBoxAddEntryText( listbox, newGameText, newGameColor, -1 );
- GadgetListBoxSetItemData( listbox, NULL, index );
-
- } // end if
- // clear the available games
- clearAvailableGames();
- // iterate all the save files in the directory and populate the listbox
- iterateSaveFiles( addGameToAvailableList, &m_availableGames );
- // add all games found to the list box
- AvailableGameInfo *info;
- SaveGameInfo *saveGameInfo;
- SYSTEMTIME systemTime;
- UnsignedInt count = 0;
- for( info = m_availableGames; info; info = info->next, count++ )
- {
- // get save game info
- saveGameInfo = &info->saveGameInfo;
- // setup a system time structure given the data we saved in the file
- systemTime.wYear = saveGameInfo->date.year;
- systemTime.wMonth = saveGameInfo->date.month;
- systemTime.wDayOfWeek = saveGameInfo->date.dayOfWeek;
- systemTime.wDay = saveGameInfo->date.day;
- systemTime.wHour = saveGameInfo->date.hour;
- systemTime.wMinute = saveGameInfo->date.minute;
- systemTime.wSecond = saveGameInfo->date.second;
- systemTime.wMilliseconds = saveGameInfo->date.milliseconds;
- // setup date buffer for local region date format
- UnicodeString displayDateBuffer = getUnicodeDateBuffer(systemTime);
- // setup time buffer for local region time format
- UnicodeString displayTimeBuffer = getUnicodeTimeBuffer(systemTime);
- // description string
- UnicodeString displayLabel = saveGameInfo->description;
- if( displayLabel.isEmpty() == TRUE )
- {
- Bool exists = FALSE;
-
- displayLabel = TheGameText->fetch( saveGameInfo->mapLabel, &exists );
- if( exists == FALSE )
- displayLabel.format( L"%S", saveGameInfo->mapLabel.str() );
- } // end if
- // pick color for text (we alternate it each game)
- Color color;
- if( saveGameInfo->saveFileType == SAVE_FILE_TYPE_MISSION )
- color = GameMakeColor( 200, 255, 200, 255 );
- else if( count & 0x1 )
- color = GameMakeColor( 255, 255, 255, 255 );
- else
- color = GameMakeColor( 170, 170, 235, 255 );
- // add string to listbox
- index = GadgetListBoxAddEntryText( listbox, displayLabel, color, -1, 0 );
- GadgetListBoxAddEntryText( listbox, displayTimeBuffer, color, index, 1 );
- GadgetListBoxAddEntryText( listbox, displayDateBuffer, color, index, 2 );
- // add this available game info in the user data pointer of that listbox item
- GadgetListBoxSetItemData( listbox, info, index );
- } // end for, info
- // select the top "new game" entry
- GadgetListBoxSetSelected( listbox, 0 );
- } // end pupulateSaveGameListbox
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // PRIVATE METHODS ////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // ------------------------------------------------------------------------------------------------
- /** Iterate the save game files */
- // ------------------------------------------------------------------------------------------------
- void GameState::iterateSaveFiles( IterateSaveFileCallback callback, void *userData )
- {
- // sanity
- if( callback == NULL )
- return;
- // save the current directory
- char currentDirectory[ _MAX_PATH ];
- GetCurrentDirectory( _MAX_PATH, currentDirectory );
- // switch into the save directory
- SetCurrentDirectory( getSaveDirectory().str() );
- // iterate all items in the directory
- WIN32_FIND_DATA item; // search item
- HANDLE hFile = INVALID_HANDLE_VALUE; // handle for search resources
- Bool done = FALSE;
- Bool first = TRUE;
- while( done == FALSE )
- {
- // if our first time through we need to start the search
- if( first )
- {
- // start search
- hFile = FindFirstFile( "*", &item );
- if( hFile == INVALID_HANDLE_VALUE )
- return;
- // we are no longer on our first item
- first = FALSE;
- } // end if, first
- // see if this is a file, and therefore a possible save file
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
- {
- // see if there is a ".sav" at end of this filename
- Char *c = strrchr( item.cFileName, '.' );
- if( c && stricmp( c, ".sav" ) == 0 )
- {
- // construction asciistring filename
- AsciiString filename;
- filename.set( item.cFileName );
- // call the callback
- callback( filename, userData );
- } // end if, a save file
- } // end if
- // on to the next file
- if( FindNextFile( hFile, &item ) == 0 )
- done = TRUE;
- } // end while
- // close search resources
- FindClose( hFile );
- // restore the current directory
- SetCurrentDirectory( currentDirectory );
- } // end iterateSaveFiles
- // ------------------------------------------------------------------------------------------------
- /** Save game to xfer or load game using xfer */
- // ------------------------------------------------------------------------------------------------
- void GameState::friend_xferSaveDataForCRC( Xfer *xfer, SnapshotType which )
- {
- DEBUG_LOG(("GameState::friend_xferSaveDataForCRC() - SnapshotType %d\n", which));
- SaveGameInfo *gameInfo = getSaveGameInfo();
- gameInfo->description.clear();
- gameInfo->saveFileType = SAVE_FILE_TYPE_NORMAL;
- gameInfo->missionMapName.clear();
- gameInfo->pristineMapName.clear();
- xferSaveData(xfer, which);
- }
- // ------------------------------------------------------------------------------------------------
- /** Save game to xfer or load game using xfer */
- // ------------------------------------------------------------------------------------------------
- void GameState::xferSaveData( Xfer *xfer, SnapshotType which )
- {
- // sanity
- if( xfer == NULL )
- throw SC_INVALID_XFER;
- // save or load all blocks
- if( xfer->getXferMode() == XFER_SAVE )
- {
- DEBUG_LOG(("GameState::xferSaveData() - XFER_SAVE\n"));
- // save all blocks
- AsciiString blockName;
- SnapshotBlock *blockInfo;
- SnapshotBlockListIterator it;
- for( it = m_snapshotBlockList[which].begin(); it != m_snapshotBlockList[which].end(); ++it )
- {
-
- // get list data
- blockInfo = &(*it);
- // get block name
- blockName = blockInfo->blockName;
- DEBUG_LOG(("Looking at block '%s'\n", blockName.str()));
- //
- // for mission save files, we only save the game state block and campaign manager
- // because anything else is not needed.
- //
- if( getSaveGameInfo()->saveFileType != SAVE_FILE_TYPE_MISSION ||
- (blockName.compareNoCase( GAME_STATE_BLOCK_STRING ) == 0 ||
- blockName.compareNoCase( CAMPAIGN_BLOCK_STRING ) == 0) )
- {
- // xfer block name
- xfer->xferAsciiString( &blockName );
- // xfer this block
- try
- {
- // begin new data block
- xfer->beginBlock();
- // xfer block data
- xfer->xferSnapshot( blockInfo->snapshot );
- // end this block
- xfer->endBlock();
- } // end try
- catch( ... )
- {
- DEBUG_CRASH(( "Error saving block '%s' in file '%s'\n",
- blockName.str(), xfer->getIdentifier() ));
- throw;
- } // end catch
- } // end if
- } // end for, all snapshots
- // write an end of file token
- AsciiString eofToken = SAVE_FILE_EOF;
- xfer->xferAsciiString( &eofToken );
-
- } // end if, save
- else
- {
- DEBUG_LOG(("GameState::xferSaveData() - not XFER_SAVE\n"));
- AsciiString token;
- Int blockSize;
- Bool done = FALSE;
- SnapshotBlock *blockInfo;
- // read all data blocks in the file
- while( done == FALSE )
- {
- // read next token
- xfer->xferAsciiString( &token );
- // check for end of file token
- if( token.compareNoCase( SAVE_FILE_EOF ) == 0 )
- {
- // all done
- done = TRUE;
- } // end if
- else
- {
- // find matching token in the save file lexicon
- blockInfo = findBlockInfoByToken( token, which );
- if( blockInfo == NULL )
- {
- // log the block not found
- DEBUG_LOG(( "GameState::xferSaveData - Skipping unknown block '%s'\n", token ));
- //
- // block was not found, this could have been a block from an older file
- // format where the block was removed, skip the block data and try to continue
- //
- Int dataSize = xfer->beginBlock();
- xfer->skip( dataSize );
- // continue with while loop reading block tokens
- continue;
- } // end if
- try
- {
- // read block start
- blockSize = xfer->beginBlock();
-
- // parse this data
- xfer->xferSnapshot( blockInfo->snapshot );
- // read block end
- xfer->endBlock();
- } // end try
- catch( ... )
- {
- DEBUG_CRASH(( "Error loading block '%s' in file '%s'\n",
- blockInfo->blockName.str(), xfer->getIdentifier() ));
- throw;
- } // end catch
- } // end else, valid data block token
- } // end while, not done
- } // end else, load
- } // end xferSaveData
- // ------------------------------------------------------------------------------------------------
- /** Add a snapshot to the post process list for later */
- // ------------------------------------------------------------------------------------------------
- void GameState::addPostProcessSnapshot( Snapshot *snapshot )
- {
- // sanity
- if( snapshot == NULL )
- {
- DEBUG_CRASH(( "GameState::addPostProcessSnapshot - invalid parameters\n" ));
- return;
- } // end if
- /*
- //
- // This is n^2 and gets real real, REAL slow on game maps. jba.
- // Please keep this code around tho, it can be useful in debugging save games
- //
- // verify the snapshot isn't in the list already
- SnapshotListIterator it;
- for( it = m_snapshotPostProcessList.begin(); it != m_snapshotPostProcessList.end(); ++it )
- {
- if( (*it) == snapshot )
- {
- DEBUG_CRASH(( "GameState::addPostProcessSnapshot - snapshot is already in list!\n" ));
- return;
- } // end if
- } // end for, it
- */
- // add to the list
- m_snapshotPostProcessList.push_back( snapshot );
- } // end addPostProcessSnapshot
- // ------------------------------------------------------------------------------------------------
- /** Post process entry point after all game data has been xferd from disk */
- // ------------------------------------------------------------------------------------------------
- void GameState::gameStatePostProcessLoad( void )
- {
- // post process each snapshot that registered with us
- SnapshotListIterator it;
- Snapshot *snapshot;
- for( it = m_snapshotPostProcessList.begin(); it != m_snapshotPostProcessList.end(); /*emtpy*/ )
- {
- // get snapshot
- snapshot = *it;
- // increment iterator
- ++it;
- // do processing
- snapshot->loadPostProcess();
- } // end for
- // clear the snapshot post process list as we are now done with it
- m_snapshotPostProcessList.clear();
- // evil... must ensure this is updated prior to the script engine running the first time.
- ThePartitionManager->update();
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- /** Xfer method for the game state itself
- * Version Info:
- * 1: Initial version
- * 2: Added save file type and mission map name (regular save vs automatic mission save) */
- // ------------------------------------------------------------------------------------------------
- void GameState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // get structure for our current game info
- SaveGameInfo *saveGameInfo = getSaveGameInfo();
- // version 2
- if( version >= 2 )
- {
- // file type
- xfer->xferUser( &saveGameInfo->saveFileType, sizeof( SaveFileType ) );
- // mission map name
- xfer->xferAsciiString( &saveGameInfo->missionMapName );
- } // end if
- // current system time
- SYSTEMTIME systemTime;
- GetLocalTime( &systemTime );
- // date and time
- saveGameInfo->date.year = systemTime.wYear;
- xfer->xferUnsignedShort( &saveGameInfo->date.year );
- saveGameInfo->date.month = systemTime.wMonth;
- xfer->xferUnsignedShort( &saveGameInfo->date.month );
- saveGameInfo->date.day = systemTime.wDay;
- xfer->xferUnsignedShort( &saveGameInfo->date.day );
- saveGameInfo->date.dayOfWeek = systemTime.wDayOfWeek;
- xfer->xferUnsignedShort( &saveGameInfo->date.dayOfWeek );
- saveGameInfo->date.hour = systemTime.wHour;
- xfer->xferUnsignedShort( &saveGameInfo->date.hour );
- saveGameInfo->date.minute = systemTime.wMinute;
- xfer->xferUnsignedShort( &saveGameInfo->date.minute );
- saveGameInfo->date.second = systemTime.wSecond;
- xfer->xferUnsignedShort( &saveGameInfo->date.second );
- saveGameInfo->date.milliseconds = systemTime.wMilliseconds;
- xfer->xferUnsignedShort( &saveGameInfo->date.milliseconds );
- // user description
- xfer->xferUnicodeString( &saveGameInfo->description );
- Bool exists = FALSE;
- Dict *dict = MapObject::getWorldDict();
- if( dict )
- saveGameInfo->mapLabel = dict->getAsciiString( TheKey_mapName, &exists );
- // if no label was found, we'll use the map name (just filename, no directory info)
- if( exists == FALSE || saveGameInfo->mapLabel == AsciiString::TheEmptyString )
- {
- char string[ _MAX_PATH ];
- strcpy( string, TheGlobalData->m_mapName.str() );
- char *p = strrchr( string, '\\' );
- if( p == NULL )
- saveGameInfo->mapLabel = TheGlobalData->m_mapName;
- else
- {
- p++; // skip the '\' we're on
- saveGameInfo->mapLabel.set( p );
- } // end else
- } // end if
- // xfer map label
- xfer->xferAsciiString( &saveGameInfo->mapLabel );
- // campaign info
- Campaign *campaign = TheCampaignManager->getCurrentCampaign();
- if( campaign )
- {
- // campaign side
- saveGameInfo->campaignSide = campaign->m_name;
- xfer->xferAsciiString( &saveGameInfo->campaignSide );
- // campaign mission number
- saveGameInfo->missionNumber = TheCampaignManager->getCurrentMissionNumber();
- xfer->xferInt( &saveGameInfo->missionNumber );
-
- } // end if
- else
- {
- // write empty campaign side
- saveGameInfo->campaignSide = AsciiString::TheEmptyString;
- xfer->xferAsciiString( &saveGameInfo->campaignSide );
- // invalid mission number
- saveGameInfo->missionNumber = CampaignManager::INVALID_MISSION_NUMBER;
- xfer->xferInt( &saveGameInfo->missionNumber );
- } // end else
- } // end xfer
|