openclonk/src/control/C4GameSave.cpp

565 lines
16 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +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
*
* 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 "control/C4GameSave.h"
2009-05-08 13:28:41 +00:00
#include "c4group/C4Components.h"
#include "game/C4Game.h"
#include "lib/C4Log.h"
#include "landscape/C4Landscape.h"
#include "landscape/C4PXS.h"
#include "landscape/C4MassMover.h"
#include "player/C4PlayerList.h"
#include "control/C4RoundResults.h"
#include "control/C4Record.h"
#include "C4Version.h"
#include "control/C4GameParameters.h"
#include "script/C4Value.h"
#include "network/C4Network2.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;
// 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
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.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?
if (::Landscape.GetMode() == LandscapeMode::Exact || GetForceExactLandscape())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4DebugRecOff DBGRECOFF;
// Landscape
bool fSuccess;
if (::Landscape.GetMode() == LandscapeMode::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
if (::Landscape.GetMode() == LandscapeMode::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
if (!::Landscape.SaveMap(*pSaveGroup)) return false;
2009-05-08 13:28:41 +00:00
// save textures (if changed)
if (!::Landscape.SaveTextures(*pSaveGroup)) return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
else if (::Landscape.GetMode() != LandscapeMode::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
bool C4GameSave::SaveRuntimeData()
2010-03-28 18:58:01 +00:00
{
// Game.txt data (general runtime data and objects)
C4ValueNumbers numbers;
if (!Game.SaveData(*pSaveGroup, false, IsExact(), IsSynced(), &numbers))
{ 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
// 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;
// Scenario title
sBuffer.Append(Game.ScenarioTitle.getData());
sBuffer.Append(LineFeed LineFeed);
2009-05-08 13:28:41 +00:00
// OK; each specializations has its own desc format
WriteDesc(sBuffer);
// 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(LineFeed 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
{
char ver[32]; sprintf(ver, "%d.%d", (int)C4XVER1, (int)C4XVER2);
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));
// 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
{
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
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())
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
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];
sprintf(buf, "%03i %s [%d.%d]", iNum, Game.ScenarioTitle.getData(), (int)C4XVER1, (int)C4XVER2);
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
rC4S.Head.NetworkGame=true;
2009-05-08 13:28:41 +00:00
rC4S.Head.NetworkRuntimeJoin = !fInitial;
2010-03-28 18:58:01 +00:00
}