1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // game saving functionality
17 // merely controls what to save when how - actual saving procedures reside in the subclasses
18 //
19 // Game saving is done on the following occasions:
20 // -scenario saving (C4GameSaveScenario) [SyncStateScenario; KeepFiles]
21 // -savegames (C4GameSaveSavegame) [SyncStateSavegame; KeepFiles]
22 // -records (C4GameSaveRecord) [SyncStateSynced; KeepFiles] - initially and while game is running
23 // -network synchronizations (C4GameSaveNetwork) [SyncStateSynced] - in lobby and runtime mode
24 // -network references (C4GameSaveNetReference) [SyncStateScenario]
25 
26 #ifndef INC_C4GameSave
27 #define INC_C4GameSave
28 
29 #include "landscape/C4Scenario.h"
30 #include "c4group/C4Components.h"
31 
32 class C4GameSave
33 {
34 private:
35 	C4Scenario rC4S; // local scenario core copy
36 
37 protected:
38 	C4Group *pSaveGroup; // group file written to
39 	bool fOwnGroup;      // whether group file is owned
40 
41 	// if set, the game is saved at initial (pre-frame0) state
42 	// (lobby-dynamics, initial records and network references)
43 	// no runtime data will be saved for initial-state-saves
44 	// SafeGame/NoInitialize-core-settings will be kept in its current state for initial saves
45 	bool fInitial;
46 
47 	// sync state describes what to save
48 	enum SyncState
49 	{
50 		SyncNONE = 0,
51 		SyncScenario = 1, // save (eventually static) landscape and objects only to play as a separate scenario later
52 		SyncSavegame = 2, // save all runtime data, so the scenario can be continued at a future date
53 		SyncSynchronized = 3 // save exact runtime data to be network- or replay-save
54 	} Sync; // sync is set by ctor
55 
56 	// query functions
GetSaveRuntimeData()57 	virtual bool GetSaveRuntimeData() { return !fInitial; }               // save exact landscape, players, etc.
GetKeepTitle()58 	virtual bool GetKeepTitle() { return !IsExact(); }            // whether original, localized title with image and icon shall be deleted
GetSaveDesc()59 	virtual bool GetSaveDesc() { return true; }                   // should WriteDescData be executed in Save()-call?
GetCopyScenario()60 	virtual bool GetCopyScenario() { return true; }               // return whether the savegame depends on the game scenario file
GetSortOrder()61 	virtual const char *GetSortOrder() { return C4FLS_Scenario; } // return nullptr to prevent sorting
GetCreateSmallFile()62 	virtual bool GetCreateSmallFile() { return false; }           // return whether file size should be minimized
GetForceExactLandscape()63 	virtual bool GetForceExactLandscape() { return GetSaveRuntimeData() && IsExact(); } // whether exact landscape shall be saved
GetSaveOrigin()64 	virtual bool GetSaveOrigin() { return false; }                // return whether C4S.Head.Origin shall be set
GetClearOrigin()65 	virtual bool GetClearOrigin() { return !GetSaveOrigin(); }    // return whether C4S.Head.Origin shall be cleared if it's set
GetSaveUserPlayers()66 	virtual bool GetSaveUserPlayers() { return IsExact(); }       // return whether joined user players shall be saved into SavePlayerInfos
GetSaveScriptPlayers()67 	virtual bool GetSaveScriptPlayers() { return IsExact(); }       // return whether joined script players shall be saved into SavePlayerInfos
GetSaveUserPlayerFiles()68 	virtual bool GetSaveUserPlayerFiles() { return IsExact(); }       // return whether .ocp files of joined user players shall be put into the scenario
GetSaveScriptPlayerFiles()69 	virtual bool GetSaveScriptPlayerFiles() { return IsExact(); }       // return whether .ocp files of joined script players shall be put into the scenario
70 
71 	// savegame specializations
AdjustCore(C4Scenario & rC4S)72 	virtual void AdjustCore(C4Scenario &rC4S) {}         // set specific C4S values
WriteDesc(StdStrBuf & sBuf)73 	virtual bool WriteDesc(StdStrBuf &sBuf) { return true; } // write desc (contents only)
SaveComponents()74 	virtual bool SaveComponents() { return true; }       // save (or remove) custom components for specialization
OnSaving()75 	virtual bool OnSaving() { return true; } // callback for special actions to be performed when saving (like, add sync)
76 
77 	// query sync level
IsExact()78 	bool IsExact() { return Sync>=SyncSavegame; }      // exact save (players, always exact landscape, etc.)
IsSynced()79 	bool IsSynced() { return Sync>=SyncSynchronized; } // synchronized
80 
81 	// protected constructor
C4GameSave(bool fAInitial,SyncState ASync)82 	C4GameSave(bool fAInitial, SyncState ASync) : pSaveGroup(nullptr), fOwnGroup(false), fInitial(fAInitial), Sync(ASync) { }
83 protected:
84 	// some desc writing helpers
85 	void WriteDescLineFeed(StdStrBuf &sBuf); // append a line break to desc
86 	void WriteDescDate(StdStrBuf &sBuf, bool fRecord = false);     // append current date to desc buffer
87 	void WriteDescGameTime(StdStrBuf &sBuf); // append game time to desc buffer, if it's >0
88 	void WriteDescDefinitions(StdStrBuf &sBuf);    // append used definition filenames to desc buffer
89 	void WriteDescNetworkClients(StdStrBuf &sBuf); // append current network client list to desc buffer
90 	void WriteDescPlayers(StdStrBuf &sBuf, bool fByTeam, int32_t idTeam); // helper func used by WriteDescPlayers: Write all players matching team
91 	void WriteDescPlayers(StdStrBuf &sBuf);        // append currently participating players to desc buffer
92 	void WriteDescLeague(StdStrBuf &sBuf, bool fLeague, const char *strLeagueName); // append league status
93 	void WriteDescEngine(StdStrBuf &sBuf);         // append engine build
94 private:
95 	// saving subcalls
96 	bool SaveCreateGroup(const char *szFilename, C4Group &hUseGroup); // create/copy group at target filename
97 	bool SaveCore();             // save C4S core
98 	bool SaveScenarioSections(); // save scenario sections
99 	bool SaveLandscape();        // save current landscape
100 	bool SaveRuntimeData(); // save any runtime data
101 public:
~C4GameSave()102 	virtual ~C4GameSave() { Close(); } // dtor: close group
103 
104 	bool Save(const char *szFilename); // create group at filename and do actual saving; group is kept open until dtor or Close()-call!
105 	bool Save(C4Group &hToGroup, bool fKeepGroup);      // save game directly to target group
106 	bool SaveDesc(C4Group &hToGroup);                   // save scenario desc to file
107 	bool Close();                      // close scenario group
108 
GetGroup()109 	C4Group *GetGroup() { return pSaveGroup; } // get scenario saving group; only open between calls to Save() and Close()
110 };
111 
112 class C4GameSaveScenario : public C4GameSave
113 {
114 public:
C4GameSaveScenario(bool fForceExactLandscape,bool fSaveOrigin)115 	C4GameSaveScenario(bool fForceExactLandscape, bool fSaveOrigin) : C4GameSave(false, SyncScenario), fForceExactLandscape(fForceExactLandscape), fSaveOrigin(fSaveOrigin) {} // ctor
116 
117 protected:
118 	bool fForceExactLandscape;
119 	bool fSaveOrigin;
GetSaveOrigin()120 	bool GetSaveOrigin() override { return fSaveOrigin; }
GetClearOrigin()121 	bool GetClearOrigin() override { return false; } // always keep existing origin
GetSaveDesc()122 	bool GetSaveDesc() override { return false; }    // should WriteDescData be executed in Save()-call?
GetForceExactLandscape()123 	bool GetForceExactLandscape() override { return C4GameSave::GetForceExactLandscape() || fForceExactLandscape; }
GetSaveScriptPlayers()124 	bool GetSaveScriptPlayers() override { return true; } // script players are also saved; but user players aren't!
GetSaveScriptPlayerFiles()125 	bool GetSaveScriptPlayerFiles() override { return true; } // script players are also saved; but user players aren't!
126 };
127 
128 class C4GameSaveSavegame : public C4GameSave
129 {
130 public:
C4GameSaveSavegame()131 	C4GameSaveSavegame() : C4GameSave(false, SyncSavegame) {}
132 
133 protected:
134 	// savegame specializations
GetSaveOrigin()135 	bool GetSaveOrigin() override { return true; }   // origin must be saved in savegames
GetSaveUserPlayerFiles()136 	bool GetSaveUserPlayerFiles() override { return false; } // user player files are not needed in savegames, because they will be replaced by player files of resuming playerss
137 	void AdjustCore(C4Scenario &rC4S) override;      // set specific C4S values
138 	bool WriteDesc(StdStrBuf &sBuf) override;        // write savegame desc (contents only)
139 	bool SaveComponents() override;                  // custom savegame components (title)
140 	bool OnSaving() override;                        // add sync when saving
141 
142 };
143 
144 class C4GameSaveRecord : public C4GameSave
145 {
146 private:
147 	int iNum; // record number
148 	bool fLeague; // recording of a league game?
149 	bool fCopyScenario; // copy scenario?
150 
151 public:
152 	C4GameSaveRecord(bool fAInitial, int iANum, bool fLeague, bool fCopyScenario = true)
C4GameSave(fAInitial,SyncSynchronized)153 			: C4GameSave(fAInitial, SyncSynchronized), iNum(iANum), fLeague(fLeague), fCopyScenario(fCopyScenario)
154 	{}
155 
156 protected:
157 	// query functions
GetSaveDesc()158 	bool GetSaveDesc() override { return false; }     // desc is saved by external call when the record is finished
GetCreateSmallFile()159 	bool GetCreateSmallFile() override { return true; } // no need to save players complete with portraits
GetSaveOrigin()160 	bool GetSaveOrigin() override { return true; }    // origin must be saved to trace language packs, folder local material, etc. for records
161 
GetCopyScenario()162 	bool GetCopyScenario() override { return fCopyScenario; } // records without copied scenario are a lot smaller can be reconstructed later (used for streaming)
163 
164 	// savegame specializations
165 	void AdjustCore(C4Scenario &rC4S) override;       // set specific C4S values
166 	bool WriteDesc(StdStrBuf &sBuf) override;         // write desc (contents only) - using old-style unchecked string buffers here...
167 	bool SaveComponents() override;                   // custom components: PlayerInfos even if fInitial
168 
169 };
170 
171 class C4GameSaveNetwork : public C4GameSave
172 {
173 public:
C4GameSaveNetwork(bool fAInitial)174 	C4GameSaveNetwork(bool fAInitial) : C4GameSave(fAInitial, SyncSynchronized) {}
175 
176 protected:
177 	// query functions
GetSaveOrigin()178 	bool GetSaveOrigin() override { return true; }     // clients must know where to get music and localization
GetKeepTitle()179 	bool GetKeepTitle() override { return false; }     // always delete title files (not used in dynamics)
GetSaveDesc()180 	bool GetSaveDesc() override { return false; }      // no desc in dynamics
GetCreateSmallFile()181 	bool GetCreateSmallFile() override { return true; }// return whether file size should be minimized
182 
GetCopyScenario()183 	bool GetCopyScenario() override { return false; }    // network dynamics do not base on normal scenario
184 	// savegame specializations
185 	void AdjustCore(C4Scenario &rC4S) override;           // set specific C4S values
186 };
187 
188 #endif // INC_C4GameSave
189