2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
* Copyright ( c ) 2009 - 2013 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
// game saving functionality
# include <C4Include.h>
# include <C4GameSave.h>
# include <C4Components.h>
# include <C4Game.h>
# include <C4League.h>
# include <C4Console.h>
# include <C4Log.h>
# include <C4Player.h>
2009-06-05 18:00:23 +00:00
# include <C4Landscape.h>
# include <C4PXS.h>
# include <C4MassMover.h>
2011-01-08 16:04:20 +00:00
# include <C4ScriptHost.h>
2009-06-12 23:09:32 +00:00
# include <C4PlayerList.h>
2009-06-15 21:47:26 +00:00
# include <C4GameObjects.h>
2011-03-11 02:43:38 +00:00
# include <C4RoundResults.h>
2009-06-15 22:06:37 +00:00
# include <C4Record.h>
2011-01-08 16:04:20 +00:00
# include <C4Version.h>
2009-05-08 13:28:41 +00:00
// *** C4GameSave main class
bool C4GameSave : : SaveCreateGroup ( const char * szFilename , C4Group & hUseGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// erase any previous item (2do: work in C4Groups?)
EraseItem ( szFilename ) ;
// copy from previous group?
if ( GetCopyScenario ( ) )
if ( ! ItemIdentical ( Game . ScenarioFilename , szFilename ) )
if ( ! C4Group_CopyItem ( Game . ScenarioFilename , szFilename ) )
{ LogF ( LoadResStr ( " IDS_CNS_SAVEASERROR " ) , szFilename ) ; return false ; }
// open it
if ( ! hUseGroup . Open ( szFilename , ! GetCopyScenario ( ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
EraseItem ( szFilename ) ;
LogF ( LoadResStr ( " IDS_CNS_SAVEASERROR " ) , szFilename ) ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : SaveCore ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// base on original, current core
rC4S = Game . C4S ;
// Always mark current engine version
rC4S . Head . C4XVer [ 0 ] = C4XVER1 ; rC4S . Head . C4XVer [ 1 ] = C4XVER2 ;
2012-10-18 21:54:50 +00:00
rC4S . Head . C4XVer [ 2 ] = C4XVER3 ;
2009-05-08 13:28:41 +00:00
// Some flags are not to be set for initial settings:
// They depend on whether specific runtime data is present, which may simply not be stored into initial
// saves, because they rely on any data present and up-to-date within the scenario!
if ( ! fInitial )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// NoInitialize: Marks whether object data is contained and not to be created from core
2009-08-15 18:50:32 +00:00
rC4S . Head . NoInitialize = true ;
2009-05-08 13:28:41 +00:00
// the SaveGame-value, despite it's name, marks whether exact runtime data is contained
// the flag must not be altered for pure
rC4S . Head . SaveGame = GetSaveRuntimeData ( ) & & IsExact ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// some values relevant for synced saves only
if ( IsExact ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
rC4S . Head . StartupPlayerCount = Game . StartupPlayerCount ;
rC4S . Head . RandomSeed = Game . RandomSeed ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// reset some network flags
rC4S . Head . NetworkGame = 0 ;
// Title in language game was started in (not: save scenarios and net references)
if ( ! GetKeepTitle ( ) ) SCopy ( Game . ScenarioTitle . getData ( ) , rC4S . Head . Title , C4MaxTitle ) ;
// some adjustments for everything but saved scenarios
if ( IsExact ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Store used definitions
rC4S . Definitions . SetModules ( Game . DefinitionFilenames , Config . General . SystemDataPath , Config . General . UserDataPath ) ;
// Save game parameters
2010-03-28 18:58:01 +00:00
if ( ! Game . Parameters . Save ( * pSaveGroup , & Game . C4S ) ) return false ;
}
2009-05-08 13:28:41 +00:00
// clear MissionAccess in save games and records (sulai)
* rC4S . Head . MissionAccess = 0 ;
// store origin
if ( GetSaveOrigin ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// keep if assigned already (e.g., when doing a record of a savegame)
if ( ! rC4S . Head . Origin . getLength ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
rC4S . Head . Origin . Copy ( Game . ScenarioFilename ) ;
Config . ForceRelativePath ( & rC4S . Head . Origin ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( GetClearOrigin ( ) )
rC4S . Head . Origin . Clear ( ) ;
// adjust specific values (virtual call)
AdjustCore ( rC4S ) ;
// Save scenario core
return ! ! rC4S . Save ( * pSaveGroup ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : SaveScenarioSections ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// any scenario sections?
if ( ! Game . pScenarioSections ) return true ;
// prepare section filename
int iWildcardPos = SCharPos ( ' * ' , C4CFN_ScenarioSections ) ;
char fn [ _MAX_FNAME + 1 ] ;
// save all modified sections
2010-03-28 18:58:01 +00:00
for ( C4ScenarioSection * pSect = Game . pScenarioSections ; pSect ; pSect = pSect - > pNext )
{
2009-05-08 13:28:41 +00:00
// compose section filename
SCopy ( C4CFN_ScenarioSections , fn ) ;
SDelete ( fn , 1 , iWildcardPos ) ; SInsert ( fn , pSect - > szName , iWildcardPos ) ;
// do not save self, because that is implied in CurrentScenarioSection and the main landscape/object data
if ( pSect = = Game . pCurrentScenarioSection )
pSaveGroup - > DeleteEntry ( fn ) ;
else if ( pSect - > fModified )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// modified section: delete current
pSaveGroup - > DeleteEntry ( fn ) ;
// replace by new
pSaveGroup - > Add ( pSect - > szTempFilename , fn ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : SaveLandscape ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// exact?
2009-06-05 15:20:41 +00:00
if ( : : Landscape . Mode = = C4LSC_Exact | | GetForceExactLandscape ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4DebugRecOff DBGRECOFF ;
// Landscape
bool fSuccess ;
2010-03-28 18:58:01 +00:00
if ( : : Landscape . Mode = = C4LSC_Exact )
2009-06-05 15:20:41 +00:00
fSuccess = ! ! : : Landscape . Save ( * pSaveGroup ) ;
2009-05-08 13:28:41 +00:00
else
2009-06-05 15:20:41 +00:00
fSuccess = ! ! : : Landscape . SaveDiff ( * pSaveGroup , ! IsSynced ( ) ) ;
2009-05-08 13:28:41 +00:00
if ( ! fSuccess ) return false ;
DBGRECOFF . Clear ( ) ;
// PXS
2009-06-05 15:20:27 +00:00
if ( ! : : PXS . Save ( * pSaveGroup ) ) return false ;
2009-05-08 13:28:41 +00:00
// MassMover (create copy, may not modify running data)
C4MassMoverSet MassMoverSet ;
2009-06-05 15:21:28 +00:00
MassMoverSet . Copy ( : : MassMover ) ;
2009-05-08 13:28:41 +00:00
if ( ! MassMoverSet . Save ( * pSaveGroup ) ) return false ;
// Material enumeration
2009-06-05 18:46:03 +00:00
if ( ! : : MaterialMap . SaveEnumeration ( * pSaveGroup ) ) return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// static / dynamic
2009-06-05 15:20:41 +00:00
if ( : : Landscape . Mode = = C4LSC_Static )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// static map
// remove old-style landscape.bmp
pSaveGroup - > DeleteEntry ( C4CFN_Landscape ) ;
// save materials if not already done
2010-03-28 18:58:01 +00:00
if ( ! GetForceExactLandscape ( ) )
{
2009-05-08 13:28:41 +00:00
// save map
2009-08-15 18:50:32 +00:00
if ( ! : : Landscape . SaveMap ( * pSaveGroup ) ) return false ;
2009-05-08 13:28:41 +00:00
// save textures (if changed)
2009-08-15 18:50:32 +00:00
if ( ! : : Landscape . SaveTextures ( * pSaveGroup ) ) return false ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-06-05 15:20:41 +00:00
else if ( : : Landscape . Mode ! = C4LSC_Exact )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// dynamic map by landscape.txt or scenario core: nothing to save
// in fact, it doesn't even make much sense to save the Objects.txt
// but the user pressed save after all...
}
2010-03-28 18:58:01 +00:00
return true ;
}
2009-05-08 13:28:41 +00:00
2011-03-27 14:51:14 +00:00
bool C4GameSave : : SaveRuntimeData ( )
2010-03-28 18:58:01 +00:00
{
2011-07-17 19:23:32 +00:00
// Game.txt data (general runtime data and objects)
C4ValueNumbers numbers ;
2014-10-03 21:12:43 +00:00
if ( ! Game . SaveData ( * pSaveGroup , false , IsExact ( ) , IsSynced ( ) , & numbers ) )
2011-07-17 19:23:32 +00:00
{ Log ( LoadResStr ( " IDS_ERR_SAVE_RUNTIMEDATA " ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// scenario sections (exact only)
if ( IsExact ( ) ) if ( ! SaveScenarioSections ( ) )
2010-03-28 18:58:01 +00:00
{ Log ( LoadResStr ( " IDS_ERR_SAVE_SCENSECTIONS " ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// landscape
if ( ! SaveLandscape ( ) ) { Log ( LoadResStr ( " IDS_ERR_SAVE_LANDSCAPE " ) ) ; return false ; }
// Round results
if ( GetSaveUserPlayers ( ) ) if ( ! Game . RoundResults . Save ( * pSaveGroup ) )
2010-03-28 18:58:01 +00:00
{ Log ( LoadResStr ( " IDS_ERR_ERRORSAVINGROUNDRESULTS " ) ) ; return false ; }
2009-05-08 13:28:41 +00:00
// Teams
if ( ! Game . Teams . Save ( * pSaveGroup ) )
{ Log ( LoadResStr ( LoadResStr ( " IDS_ERR_ERRORSAVINGTEAMS " ) ) ) ; return false ; }
if ( GetSaveUserPlayers ( ) | | GetSaveScriptPlayers ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// player infos
2012-10-21 20:20:43 +00:00
// the stored player info filenames will point into the scenario file, and no resource information
2009-05-08 13:28:41 +00:00
// will be saved. PlayerInfo must be saved first, because those will generate the storage filenames to be used by
// C4PlayerList
C4PlayerInfoList RestoreInfos ;
RestoreInfos . SetAsRestoreInfos ( Game . PlayerInfos , GetSaveUserPlayers ( ) , GetSaveScriptPlayers ( ) , GetSaveUserPlayerFiles ( ) , GetSaveScriptPlayerFiles ( ) ) ;
if ( ! RestoreInfos . Save ( * pSaveGroup , C4CFN_SavePlayerInfos ) )
{ Log ( LoadResStr ( " IDS_ERR_SAVE_RESTOREPLAYERINFOS " ) ) ; return false ; }
// Players
// this will save the player files to the savegame scenario group only
// synchronization to the original player files will be done in global game
// synchronization (via control queue)
if ( GetSaveUserPlayerFiles ( ) | | GetSaveScriptPlayerFiles ( ) )
2010-03-28 18:58:01 +00:00
{
2009-06-12 23:09:32 +00:00
if ( ! : : Players . Save ( ( * pSaveGroup ) , GetCreateSmallFile ( ) , RestoreInfos ) )
2009-05-08 13:28:41 +00:00
{ Log ( LoadResStr ( " IDS_ERR_SAVE_PLAYERS " ) ) ; return false ; }
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// non-exact runtime data: remove any exact files
// No player files
pSaveGroup - > Delete ( C4CFN_PlayerInfos ) ;
pSaveGroup - > Delete ( C4CFN_SavePlayerInfos ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : SaveDesc ( C4Group & hToGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Unfortunately, there's no way to prealloc the buffer in an appropriate size
StdStrBuf sBuffer ;
// Header
2009-06-28 20:53:32 +00:00
sBuffer . AppendFormat ( " { \\ rtf1 \\ ansi \\ ansicpg1252 \\ deff0 \\ deflang1031{ \\ fonttbl { \\ f0 \\ fnil \\ fcharset%d Times New Roman;}} " , 0 /*FIXME: a number for UTF-8 here*/ ) ;
2009-05-08 13:28:41 +00:00
sBuffer . Append ( LineFeed ) ;
// Scenario title
sBuffer . AppendFormat ( " \\ uc1 \\ pard \\ ulnone \\ b \\ f0 \\ fs20 %s \\ par " , Game . ScenarioTitle . getData ( ) ) ;
sBuffer . Append ( LineFeed " \\ b0 \\ fs16 \\ par " LineFeed ) ;
// OK; each specializations has its own desc format
WriteDesc ( sBuffer ) ;
// End of file
2010-02-16 01:54:02 +00:00
sBuffer . Append ( LineFeed " } " LineFeed ) ;
2009-05-08 13:28:41 +00:00
// Generate Filename
StdStrBuf sFilename ; char szLang [ 3 ] ;
SCopyUntil ( Config . General . Language , szLang , ' , ' , 2 ) ;
sFilename . Format ( C4CFN_ScenarioDesc , szLang ) ;
// Save to file
return ! ! hToGroup . Add ( sFilename . getData ( ) , sBuffer , false , true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescLineFeed ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// paragraph end + cosmetics
sBuf . Append ( " \\ par " LineFeed ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescDate ( StdStrBuf & sBuf , bool fRecord )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// write local time/date
time_t tTime ; time ( & tTime ) ;
struct tm * pLocalTime ;
pLocalTime = localtime ( & tTime ) ;
2009-06-05 15:19:46 +00:00
sBuf . AppendFormat ( LoadResStr ( fRecord ? " IDS_DESC_DATEREC " : ( : : Network . isEnabled ( ) ? " IDS_DESC_DATENET " : " IDS_DESC_DATE " ) ) ,
2010-03-28 18:58:01 +00:00
pLocalTime - > tm_mday ,
pLocalTime - > tm_mon + 1 ,
pLocalTime - > tm_year + 1900 ,
pLocalTime - > tm_hour ,
pLocalTime - > tm_min ) ;
2009-05-08 13:28:41 +00:00
WriteDescLineFeed ( sBuf ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescGameTime ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Write game duration
if ( Game . Time )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sBuf . AppendFormat ( LoadResStr ( " IDS_DESC_DURATION " ) ,
2010-03-28 18:58:01 +00:00
Game . Time / 3600 , ( Game . Time % 3600 ) / 60 , Game . Time % 60 ) ;
2009-05-08 13:28:41 +00:00
WriteDescLineFeed ( sBuf ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescEngine ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2012-10-18 21:54:50 +00:00
char ver [ 32 ] ; sprintf ( ver , " %d.%d.%d " , ( int ) C4XVER1 , ( int ) C4XVER2 , ( int ) C4XVER3 ) ;
2009-05-08 13:28:41 +00:00
sBuf . AppendFormat ( LoadResStr ( " IDS_DESC_VERSION " ) , ver ) ;
WriteDescLineFeed ( sBuf ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescLeague ( StdStrBuf & sBuf , bool fLeague , const char * strLeagueName )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( fLeague )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sBuf . AppendFormat ( LoadResStr ( " IDS_PRC_LEAGUE " ) , strLeagueName ) ;
WriteDescLineFeed ( sBuf ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescDefinitions ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Definition specs
if ( Game . DefinitionFilenames [ 0 ] )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
char szDef [ _MAX_PATH + 1 ] ;
// Desc
sBuf . Append ( LoadResStr ( " IDS_DESC_DEFSPECS " ) ) ;
// Get definition modules
for ( int cnt = 0 ; SGetModule ( Game . DefinitionFilenames , cnt , szDef ) ; cnt + + )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Get exe relative path
StdStrBuf sDefFilename ;
sDefFilename . Copy ( Config . AtRelativePath ( szDef ) ) ;
// Convert rtf backslashes
sDefFilename . Replace ( " \\ " , " \\ \\ " ) ;
// Append comma
if ( cnt > 0 ) sBuf . Append ( " , " ) ;
// Apend to desc
sBuf . Append ( sDefFilename ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// End of line
WriteDescLineFeed ( sBuf ) ;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescNetworkClients ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Desc
sBuf . Append ( LoadResStr ( " IDS_DESC_CLIENTS " ) ) ;
// Client names
2009-06-05 15:19:46 +00:00
for ( C4Network2Client * pClient = : : Network . Clients . GetNextClient ( NULL ) ; pClient ; pClient = : : Network . Clients . GetNextClient ( pClient ) )
2010-03-28 18:58:01 +00:00
{ sBuf . Append ( " , " ) ; sBuf . Append ( pClient - > getName ( ) ) ; }
2009-05-08 13:28:41 +00:00
// End of line
WriteDescLineFeed ( sBuf ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescPlayers ( StdStrBuf & sBuf , bool fByTeam , int32_t idTeam )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// write out all players; only if they match the given team if specified
C4PlayerInfo * pPlr ; bool fAnyPlrWritten = false ;
2010-01-25 04:00:59 +00:00
for ( int i = 0 ; ( pPlr = Game . PlayerInfos . GetPlayerInfoByIndex ( i ) ) ; i + + )
2009-05-08 13:28:41 +00:00
if ( pPlr - > HasJoined ( ) & & ! pPlr - > IsRemoved ( ) & & ! pPlr - > IsInvisible ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( fByTeam )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( idTeam )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// match team
if ( pPlr - > GetTeam ( ) ! = idTeam ) continue ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// must be in no known team
if ( Game . Teams . GetTeamByID ( pPlr - > GetTeam ( ) ) ) continue ;
2010-01-25 04:00:59 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if ( fAnyPlrWritten )
sBuf . Append ( " , " ) ;
else if ( fByTeam & & idTeam )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Team * pTeam = Game . Teams . GetTeamByID ( idTeam ) ;
if ( pTeam ) sBuf . AppendFormat ( " %s: " , pTeam - > GetName ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
sBuf . Append ( pPlr - > GetName ( ) ) ;
fAnyPlrWritten = true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if ( fAnyPlrWritten ) WriteDescLineFeed ( sBuf ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSave : : WriteDescPlayers ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// New style using Game.PlayerInfos
if ( Game . PlayerInfos . GetPlayerCount ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sBuf . Append ( LoadResStr ( " IDS_DESC_PLRS " ) ) ;
if ( Game . Teams . IsMultiTeams ( ) & & ! Game . Teams . IsAutoGenerateTeams ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Teams defined: Print players sorted by teams
WriteDescLineFeed ( sBuf ) ;
C4Team * pTeam ; int32_t i = 0 ;
2010-01-25 04:00:59 +00:00
while ( ( pTeam = Game . Teams . GetTeamByIndex ( i + + ) ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
WriteDescPlayers ( sBuf , true , pTeam - > GetID ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Finally, print out players outside known teams (those can only be achieved by script using SetPlayerTeam)
WriteDescPlayers ( sBuf , true , 0 ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// No teams defined: Print all players that have ever joined
WriteDescPlayers ( sBuf , false , 0 ) ;
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : Save ( const char * szFilename )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// close any previous
Close ( ) ;
// create group
C4Group * pLSaveGroup = new C4Group ( ) ;
if ( ! SaveCreateGroup ( szFilename , * pLSaveGroup ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
LogF ( LoadResStr ( " IDS_ERR_SAVE_TARGETGRP " ) , szFilename ? szFilename : " NULL! " ) ;
delete pLSaveGroup ;
return false ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// save to it
return Save ( * pLSaveGroup , true ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : Save ( C4Group & hToGroup , bool fKeepGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// close any previous
Close ( ) ;
// set group
pSaveGroup = & hToGroup ; fOwnGroup = fKeepGroup ;
// PreSave-actions (virtual call)
if ( ! OnSaving ( ) ) return false ;
// always save core
if ( ! SaveCore ( ) ) { Log ( LoadResStr ( " IDS_ERR_SAVE_CORE " ) ) ; return false ; }
// cleanup group
pSaveGroup - > Delete ( C4CFN_PlayerFiles ) ;
// remove: Title text, image and icon if specified
if ( ! GetKeepTitle ( ) )
2010-03-28 18:58:01 +00:00
{
2013-05-25 18:47:17 +00:00
pSaveGroup - > Delete ( FormatString ( " %s.* " , C4CFN_ScenarioTitle ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
pSaveGroup - > Delete ( C4CFN_ScenarioIcon ) ;
2009-05-11 13:09:53 +00:00
pSaveGroup - > Delete ( FormatString ( C4CFN_ScenarioDesc , " * " ) . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
pSaveGroup - > Delete ( C4CFN_Titles ) ;
pSaveGroup - > Delete ( C4CFN_Info ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// save additional runtime data
2011-03-27 14:51:14 +00:00
if ( GetSaveRuntimeData ( ) ) if ( ! SaveRuntimeData ( ) ) return false ;
2009-05-08 13:28:41 +00:00
// Desc
if ( GetSaveDesc ( ) )
if ( ! SaveDesc ( * pSaveGroup ) )
Log ( LoadResStr ( " IDS_ERR_SAVE_DESC " ) ) ; /* nofail */
// save specialized components (virtual call)
if ( ! SaveComponents ( ) ) return false ;
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSave : : Close ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fSuccess = true ;
// any group open?
if ( pSaveGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// sort group
const char * szSortOrder = GetSortOrder ( ) ;
if ( szSortOrder ) pSaveGroup - > Sort ( szSortOrder ) ;
// close if owned group
if ( fOwnGroup )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
fSuccess = ! ! pSaveGroup - > Close ( ) ;
delete pSaveGroup ;
fOwnGroup = false ;
}
2010-03-28 18:58:01 +00:00
pSaveGroup = NULL ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return fSuccess ;
}
2009-05-08 13:28:41 +00:00
// *** C4GameSaveSavegame
bool C4GameSaveSavegame : : OnSaving ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( ! Game . IsRunning ) return true ;
// synchronization to sync player files on all clients
// this resets playing times and stores them in the players?
// but doing so would be too late when the queue is executed!
// TODO: remove it? (-> PeterW ;))
2009-06-05 15:19:46 +00:00
if ( : : Network . isEnabled ( ) )
2009-08-15 18:50:32 +00:00
Game . Input . Add ( CID_Synchronize , new C4ControlSynchronize ( true ) ) ;
2009-05-08 13:28:41 +00:00
else
2009-06-12 23:09:32 +00:00
: : Players . SynchronizeLocalFiles ( ) ;
2009-05-08 13:28:41 +00:00
// OK; save now
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4GameSaveSavegame : : AdjustCore ( C4Scenario & rC4S )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Determine save game index from trailing number in group file name
int iSaveGameIndex = GetTrailingNumber ( GetFilenameOnly ( pSaveGroup - > GetFullName ( ) . getData ( ) ) ) ;
// Looks like a decent index: set numbered icon
if ( Inside ( iSaveGameIndex , 1 , 10 ) )
rC4S . Head . Icon = 2 + ( iSaveGameIndex - 1 ) ;
// Else: set normal script icon
else
rC4S . Head . Icon = 29 ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSaveSavegame : : SaveComponents ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// special for savegames: save a screenshot
if ( ! Game . SaveGameTitle ( ( * pSaveGroup ) ) )
Log ( LoadResStr ( " IDS_ERR_SAVE_GAMETITLE " ) ) ; /* nofail */
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSaveSavegame : : WriteDesc ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// compose savegame desc
WriteDescDate ( sBuf ) ;
WriteDescGameTime ( sBuf ) ;
WriteDescDefinitions ( sBuf ) ;
2009-06-05 15:19:46 +00:00
if ( : : Network . isEnabled ( ) ) WriteDescNetworkClients ( sBuf ) ;
2009-05-08 13:28:41 +00:00
WriteDescPlayers ( sBuf ) ;
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// *** C4GameSaveRecord
void C4GameSaveRecord : : AdjustCore ( C4Scenario & rC4S )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// specific recording flags
2009-08-15 18:50:32 +00:00
rC4S . Head . Replay = true ;
2009-05-08 13:28:41 +00:00
if ( ! rC4S . Head . Film ) rC4S . Head . Film = C4SFilm_Normal ; /* default to film */
rC4S . Head . Icon = 29 ;
// default record title
char buf [ 1024 + 1 ] ;
2012-10-18 21:54:50 +00:00
sprintf ( buf , " %03i %s [%d.%d.%d] " , iNum , Game . ScenarioTitle . getData ( ) , ( int ) C4XVER1 , ( int ) C4XVER2 , ( int ) C4XVER3 ) ;
2009-05-08 13:28:41 +00:00
SCopy ( buf , rC4S . Head . Title , C4MaxTitle ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSaveRecord : : SaveComponents ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// special: records need player infos even if done initially
if ( fInitial ) Game . PlayerInfos . Save ( ( * pSaveGroup ) , C4CFN_PlayerInfos ) ;
// for !fInitial, player infos will be saved as regular runtime data
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4GameSaveRecord : : WriteDesc ( StdStrBuf & sBuf )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// compose record desc
WriteDescDate ( sBuf , true ) ;
WriteDescGameTime ( sBuf ) ;
WriteDescEngine ( sBuf ) ;
WriteDescDefinitions ( sBuf ) ;
WriteDescLeague ( sBuf , fLeague , Game . Parameters . League . getData ( ) ) ;
2009-06-05 15:19:46 +00:00
if ( : : Network . isEnabled ( ) ) WriteDescNetworkClients ( sBuf ) ;
2009-05-08 13:28:41 +00:00
WriteDescPlayers ( sBuf ) ;
// done, success
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// *** C4GameSaveNetwork
void C4GameSaveNetwork : : AdjustCore ( C4Scenario & rC4S )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// specific dynamic flags
2009-08-15 18:50:32 +00:00
rC4S . Head . NetworkGame = true ;
2009-05-08 13:28:41 +00:00
rC4S . Head . NetworkRuntimeJoin = ! fInitial ;
2010-03-28 18:58:01 +00:00
}