1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22 #include "common/archive.h"
23 #include "common/config-manager.h"
24 #include "common/file.h"
25 #include "common/savefile.h"
26 #include "common/system.h"
27
28 #include "cryomni3d/versailles/engine.h"
29
30 namespace CryOmni3D {
31 namespace Versailles {
32
loadGameState(int slot)33 Common::Error CryOmni3DEngine_Versailles::loadGameState(int slot) {
34 _loadedSave = slot + 1;
35 _abortCommand = kAbortLoadGame;
36 return Common::kNoError;
37 }
38
saveGameState(int slot,const Common::String & desc)39 Common::Error CryOmni3DEngine_Versailles::saveGameState(int slot, const Common::String &desc) {
40 saveGame(_isVisiting, slot + 1, desc);
41 return Common::kNoError;
42 }
43
getSaveFileName(bool visit,uint saveNum) const44 Common::String CryOmni3DEngine_Versailles::getSaveFileName(bool visit, uint saveNum) const {
45 return Common::String::format("%s%s.%04u", _targetName.c_str(), visit ? "_visit" : "", saveNum);
46 }
47
canVisit() const48 bool CryOmni3DEngine_Versailles::canVisit() const {
49 // Build a custom SearchSet
50 const Common::FSNode gameDataDir(ConfMan.get("path"));
51 Common::SearchSet visitsSearchSet;
52 visitsSearchSet.addSubDirectoryMatching(gameDataDir, "savegame/visite", 1);
53 return visitsSearchSet.hasFile("game0001.sav");
54 }
55
getSavesList(bool visit,Common::StringArray & saveNames)56 void CryOmni3DEngine_Versailles::getSavesList(bool visit, Common::StringArray &saveNames) {
57 char saveName[kSaveDescriptionLen + 1];
58 saveName[kSaveDescriptionLen] = '\0';
59 Common::String pattern = Common::String::format("%s%s.????", _targetName.c_str(),
60 visit ? "_visit" : "");
61 Common::StringArray filenames = _saveFileMan->listSavefiles(pattern);
62 sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
63
64 saveNames.clear();
65 saveNames.reserve(100);
66
67 int num = 1;
68 int slotNum;
69
70 if (visit) {
71 // Add bootstrap visit
72 const Common::FSNode gameDataDir(ConfMan.get("path"));
73 Common::SearchSet visitsSearchSet;
74 visitsSearchSet.addSubDirectoryMatching(gameDataDir, "savegame/visite", 1);
75 if (visitsSearchSet.hasFile("game0001.sav")) {
76 Common::File visitFile;
77 if (!visitFile.open("game0001.sav", visitsSearchSet)) {
78 error("Can't load visit file");
79 }
80 visitFile.read(saveName, kSaveDescriptionLen);
81 saveNames.push_back(saveName);
82 } else {
83 warning("visiting mode but no bootstrap");
84 // No bootstrap visit, too bad
85 saveNames.push_back(_messages[55]); //Fill with free slot
86 }
87 num++;
88 }
89
90 for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end();
91 ++file) {
92 // Obtain the last 4 digits of the filename, since they correspond to the save slot
93 slotNum = atoi(file->c_str() + file->size() - 4);
94
95 if (slotNum >= 1 && slotNum <= 99) {
96 while (num < slotNum) {
97 saveNames.push_back(_messages[55]); //Fill with free slot
98 num++;
99 }
100
101 num++;
102 Common::InSaveFile *in = _saveFileMan->openForLoading(*file);
103 if (in) {
104 if (in->read(saveName, kSaveDescriptionLen) == kSaveDescriptionLen) {
105 saveNames.push_back(saveName);
106 }
107 delete in;
108 }
109 }
110 }
111
112 for (uint i = saveNames.size(); i < 100; i++) {
113 saveNames.push_back(_messages[55]);
114 }
115 }
116
saveGame(bool visit,uint saveNum,const Common::String & saveName)117 void CryOmni3DEngine_Versailles::saveGame(bool visit, uint saveNum,
118 const Common::String &saveName) {
119 if (visit && saveNum == 1) {
120 error("Can't erase bootstrap visit");
121 }
122
123 Common::String saveFileName = getSaveFileName(visit, saveNum);
124
125 Common::OutSaveFile *out;
126
127 if (!(out = _saveFileMan->openForSaving(saveFileName))) {
128 return;
129 }
130
131 // Sync countdown to game variable before saving it to file
132 syncCountdown();
133
134 // Write save name
135 char saveNameC[kSaveDescriptionLen];
136 memset(saveNameC, 0, sizeof(saveNameC));
137 // Silence -Wstringop-truncation using parentheses, we don't have to have a null-terminated string here
138 (strncpy(saveNameC, saveName.c_str(), sizeof(saveNameC)));
139 out->write(saveNameC, sizeof(saveNameC));
140
141 // dummy values
142 out->writeUint32LE(0);
143 out->writeUint32BE(0);
144 out->writeUint32BE(0);
145
146 // Dialog variables
147 assert(_dialogsMan.size() < 200);
148 for (uint i = 0; i < _dialogsMan.size(); i++) {
149 out->writeByte(_dialogsMan[i]);
150 }
151 for (uint i = _dialogsMan.size(); i < 200; i++) {
152 out->writeByte(0);
153 }
154
155 // Inventory
156 assert(_inventory.size() == 50);
157 for (Inventory::const_iterator it = _inventory.begin(); it != _inventory.end(); it++) {
158 uint objId = uint(-1);
159 if (*it != nullptr) {
160 // Inventory contains pointers to objects stored in _objects
161 objId = *it - _objects.begin();
162 }
163 out->writeUint32BE(objId);
164 }
165 // Offset of inventory in toolbar
166 out->writeUint32BE(_toolbar.inventoryOffset());
167
168 // Level, place, warp position
169 out->writeUint32BE(_currentLevel);
170 out->writeUint32BE(_currentPlaceId);
171 out->writeDoubleBE(_omni3dMan.getAlpha());
172 out->writeDoubleBE(_omni3dMan.getBeta());
173
174 // Places states
175 assert(_placeStates.size() <= 100);
176 Common::Array<PlaceState>::const_iterator placeIt = _placeStates.begin();
177 for (uint i = 0; placeIt != _placeStates.end(); placeIt++, i++) {
178 out->writeUint32BE(placeIt->state);
179 }
180 for (uint i = _placeStates.size(); i < 100; i++) {
181 out->writeUint32BE(0);
182 }
183
184 // Game variables
185 assert(_gameVariables.size() < 100);
186 for (Common::Array<uint>::const_iterator it = _gameVariables.begin();
187 it != _gameVariables.end(); it++) {
188 out->writeUint32BE(*it);
189 }
190 for (uint i = _gameVariables.size(); i < 100; i++) {
191 out->writeUint32BE(0);
192 }
193
194 out->finalize();
195
196 delete out;
197 }
198
loadGame(bool visit,uint saveNum)199 bool CryOmni3DEngine_Versailles::loadGame(bool visit, uint saveNum) {
200 Common::SeekableReadStream *in;
201
202 if (visit && saveNum == 1) {
203 // Load bootstrap visit
204 const Common::FSNode gameDataDir(ConfMan.get("path"));
205 Common::SearchSet visitsSearchSet;
206 visitsSearchSet.addSubDirectoryMatching(gameDataDir, "savegame/visite", 1);
207 Common::File *visitFile = new Common::File();
208 if (!visitFile->open("game0001.sav", visitsSearchSet)) {
209 delete visitFile;
210 error("Can't load visit file");
211 }
212 in = visitFile;
213 } else {
214 Common::String saveFileName = getSaveFileName(visit, saveNum);
215 in = _saveFileMan->openForLoading(saveFileName);
216 }
217
218 if (!in || in->size() != 1260) {
219 return false;
220 }
221
222 musicStop();
223
224 // Load save name but don't use it
225 char saveNameC[kSaveDescriptionLen];
226 in->read(saveNameC, sizeof(saveNameC));
227
228 // dummy values
229 (void) in->readUint32LE();
230 (void) in->readUint32BE();
231 (void) in->readUint32BE();
232
233 // Dialog variables
234 assert(_dialogsMan.size() < 200);
235 for (uint i = 0; i < _dialogsMan.size(); i++) {
236 _dialogsMan[i] = in->readByte();
237 }
238 for (uint i = _dialogsMan.size(); i < 200; i++) {
239 // Read the remaining bytes but don't use them
240 (void) in->readByte();
241 }
242
243 // Inventory
244 assert(_inventory.size() == 50);
245 for (Inventory::iterator it = _inventory.begin(); it != _inventory.end(); it++) {
246 uint objId = in->readUint32BE();
247 if (objId >= _objects.size()) {
248 objId = uint(-1);
249 }
250 if (objId != uint(-1)) {
251 *it = _objects.begin() + objId;
252 } else {
253 *it = nullptr;
254 }
255 }
256 // Offset of inventory in toolbar
257 _toolbar.setInventoryOffset(in->readUint32BE());
258
259 // Level, place, warp position
260 _currentLevel = in->readUint32BE();
261 // Use nextPlace to force place move
262 _nextPlaceId = in->readUint32BE();
263
264 // Store alpha and beta for later use
265 double alpha = in->readDoubleBE();
266 double beta = in->readDoubleBE();
267
268 // Places states
269 // Store them and use them once we called initNewLevel, we can't call it before because it needs _gameVariables (and especially kCurrentTime) to be correctly set
270 uint32 placesStates[100];
271 for (uint i = 0; i < 100; i++) {
272 placesStates[i] = in->readUint32BE();
273 }
274
275 // Game variables
276 assert(_gameVariables.size() < 100);
277 for (Common::Array<uint>::iterator it = _gameVariables.begin(); it != _gameVariables.end();
278 it++) {
279 *it = in->readUint32BE();
280 }
281 for (uint i = _gameVariables.size(); i < 100; i++) {
282 // Read the remaining variables but don't use them
283 (void) in->readUint32BE();
284 }
285
286 delete in;
287
288 if (_gameVariables[GameVariables::kCurrentTime] == 0) {
289 _gameVariables[GameVariables::kCurrentTime] = 1;
290 }
291 initCountdown();
292
293 // Everything has been loaded, setup new level
294 // We will set places states and warp coordinates just after that to avoid them from being reset
295 initNewLevel(_currentLevel);
296
297 _omni3dMan.setAlpha(alpha);
298 _omni3dMan.setBeta(beta);
299
300 // _placeStates has just been resized in initNewLevel
301 uint i = 0;
302 for (Common::Array<PlaceState>::iterator placeIt = _placeStates.begin();
303 placeIt != _placeStates.end() && i < ARRAYSIZE(placesStates); placeIt++, i++) {
304 placeIt->state = placesStates[i];
305 }
306
307 return true;
308 }
309
310 } // End of namespace Versailles
311 } // End of namespace CryOmni3D
312