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 * Save and restore scene and game.
22 */
23
24 #include "tinsel/actors.h"
25 #include "tinsel/config.h"
26 #include "tinsel/dialogs.h"
27 #include "tinsel/drives.h"
28 #include "tinsel/dw.h"
29 #include "tinsel/rince.h"
30 #include "tinsel/savescn.h"
31 #include "tinsel/timers.h"
32 #include "tinsel/tinlib.h"
33 #include "tinsel/tinsel.h"
34
35 #include "common/serializer.h"
36 #include "common/savefile.h"
37 #include "common/textconsole.h"
38 #include "common/translation.h"
39
40 #include "gui/message.h"
41
42 namespace Tinsel {
43
44
45 /**
46 * The current savegame format version.
47 * Our save/load system uses an elaborate scheme to allow us to modify the
48 * savegame while keeping full backward compatibility, in the sense that newer
49 * ScummVM versions always are able to load old savegames.
50 * In order to achieve that, we store a version in the savegame files, and whenever
51 * the savegame layout is modified, the version is incremented.
52 *
53 * This roughly works by marking each savegame entry with a range of versions
54 * for which it is valid; the save/load code iterates over all entries, but
55 * only saves/loads those which are valid for the version of the savegame
56 * which is being loaded/saved currently.
57 */
58 #define CURRENT_VER 3
59
60 //----------------- GLOBAL GLOBAL DATA --------------------
61
62 int g_thingHeld = 0;
63 int g_restoreCD = 0;
64 SRSTATE g_SRstate = SR_IDLE;
65
66 //----------------- EXTERN FUNCTIONS --------------------
67
68 // in DOS_DW.C
69 extern void syncSCdata(Common::Serializer &s);
70
71 // in PCODE.C
72 extern void syncGlobInfo(Common::Serializer &s);
73
74 // in POLYGONS.C
75 extern void syncPolyInfo(Common::Serializer &s);
76
77 extern int g_sceneCtr;
78
79 extern bool g_ASceneIsSaved;
80
81 //----------------- LOCAL DEFINES --------------------
82
83 struct SaveGameHeader {
84 uint32 id;
85 uint32 size;
86 uint32 ver;
87 char desc[SG_DESC_LEN];
88 TimeDate dateTime;
89 uint32 playTime;
90 bool scnFlag;
91 byte language;
92 uint16 numInterpreters; // Savegame version 2 or later only
93 };
94
95 enum {
96 DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM"
97 DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWorld 2 ScummVM"
98 SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 + 4 + 1 + 1 + 2
99 };
100
101 #define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
102
103 enum {
104 // FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
105 // name field in savedFiles. Raising it to 256 as a preliminary fix.
106 FNAMELEN = 256 // 8.3
107 };
108
109 struct SFILES {
110 char name[FNAMELEN];
111 char desc[SG_DESC_LEN + 2];
112 TimeDate dateTime;
113 };
114
115 //----------------- LOCAL GLOBAL DATA --------------------
116
117 // FIXME: Avoid non-const global vars
118
119 static int g_numSfiles = 0;
120 static SFILES g_savedFiles[MAX_SAVED_FILES];
121
122 static bool g_NeedLoad = true;
123
124 static SAVED_DATA *g_srsd = 0;
125 static int g_RestoreGameNumber = 0;
126 static char *g_SaveSceneName = 0;
127 static const char *g_SaveSceneDesc = 0;
128 static int *g_SaveSceneSsCount = 0;
129 static SAVED_DATA *g_SaveSceneSsData = 0; // points to 'SAVED_DATA ssdata[MAX_NEST]'
130
131 //------------- SAVE/LOAD SUPPORT METHODS ----------------
132
setNeedLoad()133 void setNeedLoad() {
134 g_NeedLoad = true;
135 }
136
syncTime(Common::Serializer & s,TimeDate & t)137 static void syncTime(Common::Serializer &s, TimeDate &t) {
138 s.syncAsUint16LE(t.tm_year);
139 s.syncAsByte(t.tm_mon);
140 s.syncAsByte(t.tm_mday);
141 s.syncAsByte(t.tm_hour);
142 s.syncAsByte(t.tm_min);
143 s.syncAsByte(t.tm_sec);
144 }
145
syncSaveGameHeader(Common::Serializer & s,SaveGameHeader & hdr)146 static bool syncSaveGameHeader(Common::Serializer &s, SaveGameHeader &hdr) {
147 s.syncAsUint32LE(hdr.id);
148 s.syncAsUint32LE(hdr.size);
149 s.syncAsUint32LE(hdr.ver);
150
151 s.syncBytes((byte *)hdr.desc, SG_DESC_LEN);
152 hdr.desc[SG_DESC_LEN - 1] = 0;
153
154 syncTime(s, hdr.dateTime);
155
156 if (hdr.ver >= 3)
157 s.syncAsUint32LE(hdr.playTime);
158 else
159 hdr.playTime = 0;
160
161 int tmp = hdr.size - s.bytesSynced();
162
163 // NOTE: We can't use SAVEGAME_ID here when attempting to remove a saved game from the launcher,
164 // as there is no TinselEngine initialized then. This means that we can't check if this is a DW1
165 // or DW2 savegame in this case, but it doesn't really matter, as the saved game is about to be
166 // deleted anyway. Refer to bug #3387551.
167 bool correctID = _vm ? (hdr.id == SAVEGAME_ID) : (hdr.id == DW1_SAVEGAME_ID || hdr.id == DW2_SAVEGAME_ID);
168
169 // Perform sanity check
170 if (tmp < 0 || !correctID || hdr.ver > CURRENT_VER || hdr.size > 1024)
171 return false;
172
173 if (tmp > 0) {
174 // If there's header space left, handling syncing the Scn flag and game language
175 s.syncAsByte(hdr.scnFlag);
176 s.syncAsByte(hdr.language);
177 tmp -= 2;
178
179 if (_vm && s.isLoading()) {
180 // If the engine is loaded, ensure the Scn/Gra usage is correct, and it's the correct language
181 if ((hdr.scnFlag != ((_vm->getFeatures() & GF_SCNFILES) != 0)) ||
182 (hdr.language != _vm->_config->_language))
183 return false;
184 }
185 }
186
187 // Handle the number of interpreter contexts that will be saved in the savegame
188 if (tmp >= 2) {
189 tmp -= 2;
190 hdr.numInterpreters = NUM_INTERPRET;
191 s.syncAsUint16LE(hdr.numInterpreters);
192 } else {
193 if(_vm) // See comment above about bug #3387551
194 hdr.numInterpreters = (TinselV2 ? 70 : 64) - 20;
195 else
196 hdr.numInterpreters = 50; // This value doesn't matter since the saved game is being deleted.
197 }
198
199 // Skip over any extra bytes
200 s.skip(tmp);
201 return true;
202 }
203
syncSavedMover(Common::Serializer & s,SAVED_MOVER & sm)204 static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) {
205 int i, j;
206
207 s.syncAsUint32LE(sm.bActive);
208 s.syncAsSint32LE(sm.actorID);
209 s.syncAsSint32LE(sm.objX);
210 s.syncAsSint32LE(sm.objY);
211 s.syncAsUint32LE(sm.hLastfilm);
212
213 // Sync walk reels
214 for (i = 0; i < TOTAL_SCALES; ++i)
215 for (j = 0; j < 4; ++j)
216 s.syncAsUint32LE(sm.walkReels[i][j]);
217
218 // Sync stand reels
219 for (i = 0; i < TOTAL_SCALES; ++i)
220 for (j = 0; j < 4; ++j)
221 s.syncAsUint32LE(sm.standReels[i][j]);
222
223 // Sync talk reels
224 for (i = 0; i < TOTAL_SCALES; ++i)
225 for (j = 0; j < 4; ++j)
226 s.syncAsUint32LE(sm.talkReels[i][j]);
227
228
229 if (TinselV2) {
230 s.syncAsByte(sm.bHidden);
231
232 s.syncAsSint32LE(sm.brightness);
233 s.syncAsSint32LE(sm.startColor);
234 s.syncAsSint32LE(sm.paletteLength);
235 }
236 }
237
syncSavedActor(Common::Serializer & s,SAVED_ACTOR & sa)238 static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) {
239 s.syncAsUint16LE(sa.actorID);
240 s.syncAsUint16LE(sa.zFactor);
241 s.syncAsUint16LE(sa.bAlive);
242 s.syncAsUint16LE(sa.bHidden);
243 s.syncAsUint32LE(sa.presFilm);
244 s.syncAsUint16LE(sa.presRnum);
245 s.syncAsUint16LE(sa.presPlayX);
246 s.syncAsUint16LE(sa.presPlayY);
247 }
248
249 extern void syncAllActorsAlive(Common::Serializer &s);
250
syncNoScrollB(Common::Serializer & s,NOSCROLLB & ns)251 static void syncNoScrollB(Common::Serializer &s, NOSCROLLB &ns) {
252 s.syncAsSint32LE(ns.ln);
253 s.syncAsSint32LE(ns.c1);
254 s.syncAsSint32LE(ns.c2);
255 }
256
syncZPosition(Common::Serializer & s,Z_POSITIONS & zp)257 static void syncZPosition(Common::Serializer &s, Z_POSITIONS &zp) {
258 s.syncAsSint16LE(zp.actor);
259 s.syncAsSint16LE(zp.column);
260 s.syncAsSint32LE(zp.z);
261 }
262
syncPolyVolatile(Common::Serializer & s,POLY_VOLATILE & p)263 static void syncPolyVolatile(Common::Serializer &s, POLY_VOLATILE &p) {
264 s.syncAsByte(p.bDead);
265 s.syncAsSint16LE(p.xoff);
266 s.syncAsSint16LE(p.yoff);
267 }
268
syncSoundReel(Common::Serializer & s,SOUNDREELS & sr)269 static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) {
270 s.syncAsUint32LE(sr.hFilm);
271 s.syncAsSint32LE(sr.column);
272 s.syncAsSint32LE(sr.actorCol);
273 }
274
syncSavedData(Common::Serializer & s,SAVED_DATA & sd,int numInterp)275 static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp) {
276 s.syncAsUint32LE(sd.SavedSceneHandle);
277 s.syncAsUint32LE(sd.SavedBgroundHandle);
278 for (int i = 0; i < MAX_MOVERS; ++i)
279 syncSavedMover(s, sd.SavedMoverInfo[i]);
280 for (int i = 0; i < MAX_SAVED_ACTORS; ++i)
281 syncSavedActor(s, sd.SavedActorInfo[i]);
282
283 s.syncAsSint32LE(sd.NumSavedActors);
284 s.syncAsSint32LE(sd.SavedLoffset);
285 s.syncAsSint32LE(sd.SavedToffset);
286 for (int i = 0; i < numInterp; ++i)
287 sd.SavedICInfo[i].syncWithSerializer(s);
288 for (int i = 0; i < MAX_POLY; ++i)
289 s.syncAsUint32LE(sd.SavedDeadPolys[i]);
290 s.syncAsUint32LE(sd.SavedControl);
291 s.syncAsUint32LE(sd.SavedMidi);
292 s.syncAsUint32LE(sd.SavedLoop);
293 s.syncAsUint32LE(sd.SavedNoBlocking);
294
295 // SavedNoScrollData
296 for (int i = 0; i < MAX_VNOSCROLL; ++i)
297 syncNoScrollB(s, sd.SavedNoScrollData.NoVScroll[i]);
298 for (int i = 0; i < MAX_HNOSCROLL; ++i)
299 syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
300 s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
301 s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH);
302
303 // Tinsel 2 fields
304 if (TinselV2) {
305 // SavedNoScrollData
306 s.syncAsUint32LE(sd.SavedNoScrollData.xTrigger);
307 s.syncAsUint32LE(sd.SavedNoScrollData.xDistance);
308 s.syncAsUint32LE(sd.SavedNoScrollData.xSpeed);
309 s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerTop);
310 s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerBottom);
311 s.syncAsUint32LE(sd.SavedNoScrollData.yDistance);
312 s.syncAsUint32LE(sd.SavedNoScrollData.ySpeed);
313
314 for (int i = 0; i < NUM_ZPOSITIONS; ++i)
315 syncZPosition(s, sd.zPositions[i]);
316 s.syncBytes(sd.savedActorZ, MAX_SAVED_ACTOR_Z);
317 for (int i = 0; i < MAX_POLY; ++i)
318 syncPolyVolatile(s, sd.SavedPolygonStuff[i]);
319 for (int i = 0; i < 3; ++i)
320 s.syncAsUint32LE(sd.SavedTune[i]);
321 s.syncAsByte(sd.bTinselDim);
322 s.syncAsSint32LE(sd.SavedScrollFocus);
323 for (int i = 0; i < SV_TOPVALID; ++i)
324 s.syncAsSint32LE(sd.SavedSystemVars[i]);
325 for (int i = 0; i < MAX_SOUNDREELS; ++i)
326 syncSoundReel(s, sd.SavedSoundReels[i]);
327 }
328 }
329
330 /**
331 * Compare two TimeDate structs to see which one was earlier.
332 * Returns 0 if they are equal, a negative value if a is lower / first, and
333 * a positive value if b is lower / first.
334 */
cmpTimeDate(const TimeDate & a,const TimeDate & b)335 static int cmpTimeDate(const TimeDate &a, const TimeDate &b) {
336 int tmp;
337
338 #define CMP_ENTRY(x) tmp = a.x - b.x; if (tmp != 0) return tmp
339
340 CMP_ENTRY(tm_year);
341 CMP_ENTRY(tm_mon);
342 CMP_ENTRY(tm_mday);
343 CMP_ENTRY(tm_hour);
344 CMP_ENTRY(tm_min);
345 CMP_ENTRY(tm_sec);
346
347 #undef CMP_ENTRY
348
349 return 0;
350 }
351
352 /**
353 * Compute a list of all available saved game files.
354 * Store the file details, ordered by time, in savedFiles[] and return
355 * the number of files found.
356 */
getList(Common::SaveFileManager * saveFileMan,const Common::String & target)357 int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) {
358 // No change since last call?
359 // TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
360 if (!g_NeedLoad)
361 return g_numSfiles;
362
363 int i;
364
365 const Common::String pattern = target + ".???";
366 Common::StringArray files = saveFileMan->listSavefiles(pattern);
367
368 g_numSfiles = 0;
369
370 for (Common::StringArray::const_iterator file = files.begin(); file != files.end(); ++file) {
371 if (g_numSfiles >= MAX_SAVED_FILES)
372 break;
373
374 const Common::String &fname = *file;
375 Common::InSaveFile *f = saveFileMan->openForLoading(fname);
376 if (f == NULL) {
377 continue;
378 }
379
380 // Try to load save game header
381 Common::Serializer s(f, 0);
382 SaveGameHeader hdr;
383 bool validHeader = syncSaveGameHeader(s, hdr);
384 delete f;
385 if (!validHeader) {
386 continue; // Invalid header, or savegame too new -> skip it
387 // TODO: In SCUMM, we still show an entry for the save, but with description
388 // "incompatible version".
389 }
390
391 i = g_numSfiles;
392 #ifndef DISABLE_SAVEGAME_SORTING
393 for (i = 0; i < g_numSfiles; i++) {
394 if (cmpTimeDate(hdr.dateTime, g_savedFiles[i].dateTime) > 0) {
395 Common::copy_backward(&g_savedFiles[i], &g_savedFiles[g_numSfiles], &g_savedFiles[g_numSfiles + 1]);
396 break;
397 }
398 }
399 #endif
400
401 Common::strlcpy(g_savedFiles[i].name, fname.c_str(), FNAMELEN);
402 Common::strlcpy(g_savedFiles[i].desc, hdr.desc, SG_DESC_LEN);
403 g_savedFiles[i].dateTime = hdr.dateTime;
404
405 ++g_numSfiles;
406 }
407
408 // Next getList() needn't do its stuff again
409 g_NeedLoad = false;
410
411 return g_numSfiles;
412 }
413
getList()414 int getList() {
415 // No change since last call?
416 // TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
417 if (!g_NeedLoad)
418 return g_numSfiles;
419
420 return getList(_vm->getSaveFileMan(), _vm->getTargetName());
421 }
422
ListEntry(int i,letype which)423 char *ListEntry(int i, letype which) {
424 if (i == -1)
425 i = g_numSfiles;
426
427 assert(i >= 0);
428
429 if (i < g_numSfiles)
430 return which == LE_NAME ? g_savedFiles[i].name : g_savedFiles[i].desc;
431 else
432 return NULL;
433 }
434
DoSync(Common::Serializer & s,int numInterp)435 static bool DoSync(Common::Serializer &s, int numInterp) {
436 int sg = 0;
437
438 if (TinselV2) {
439 if (s.isSaving())
440 g_restoreCD = GetCurrentCD();
441 s.syncAsSint16LE(g_restoreCD);
442 }
443
444 if (TinselV2 && s.isLoading())
445 HoldItem(INV_NOICON);
446
447 syncSavedData(s, *g_srsd, numInterp);
448 syncGlobInfo(s); // Glitter globals
449 syncInvInfo(s); // Inventory data
450
451 // Held object
452 if (s.isSaving())
453 sg = WhichItemHeld();
454 s.syncAsSint32LE(sg);
455 if (s.isLoading()) {
456 if (sg != -1 && !GetIsInvObject(sg))
457 // Not a valid inventory object, so return false
458 return false;
459
460 if (TinselV2)
461 g_thingHeld = sg;
462 else
463 HoldItem(sg);
464 }
465
466 syncTimerInfo(s); // Timer data
467 if (!TinselV2)
468 syncPolyInfo(s); // Dead polygon data
469 syncSCdata(s); // Hook Scene and delayed scene
470
471 s.syncAsSint32LE(*g_SaveSceneSsCount);
472
473 if (*g_SaveSceneSsCount != 0) {
474 SAVED_DATA *sdPtr = g_SaveSceneSsData;
475 for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr)
476 syncSavedData(s, *sdPtr, numInterp);
477
478 // Flag that there is a saved scene to return to. Note that in this context 'saved scene'
479 // is a stored scene to return to from another scene, such as from the Summoning Book close-up
480 // in Discworld 1 to whatever scene Rincewind was in prior to that
481 g_ASceneIsSaved = true;
482 }
483
484 if (!TinselV2)
485 syncAllActorsAlive(s);
486
487 return true;
488 }
489
490 /**
491 * DoRestore
492 */
DoRestore()493 static bool DoRestore() {
494 Common::InSaveFile *f = _vm->getSaveFileMan()->openForLoading(g_savedFiles[g_RestoreGameNumber].name);
495
496 if (f == NULL) {
497 return false;
498 }
499
500 Common::Serializer s(f, 0);
501 SaveGameHeader hdr;
502 if (!syncSaveGameHeader(s, hdr)) {
503 delete f; // Invalid header, or savegame too new -> skip it
504 return false;
505 }
506
507 if (hdr.ver >= 3)
508 _vm->setTotalPlayTime(hdr.playTime);
509 else
510 _vm->setTotalPlayTime(0);
511
512 // Load in the data. For older savegame versions, we potentially need to load the data twice, once
513 // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
514 int numInterpreters = hdr.numInterpreters;
515 int32 currentPos = f->pos();
516 for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) {
517 // If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
518 if (tryNumber == 1) {
519 f->seek(currentPos);
520 numInterpreters = 80;
521 }
522
523 // Load the savegame data
524 if (DoSync(s, numInterpreters))
525 // Data load was successful (or likely), so break out of loop
526 break;
527 }
528
529 uint32 id = f->readSint32LE();
530 if (id != (uint32)0xFEEDFACE)
531 error("Incompatible saved game");
532
533 bool failed = (f->eos() || f->err());
534
535 delete f;
536
537 if (failed) {
538 GUI::MessageDialog dialog(_("Failed to load saved game from file."));
539 dialog.runModal();
540 }
541
542 return !failed;
543 }
544
SaveFailure(Common::OutSaveFile * f)545 static void SaveFailure(Common::OutSaveFile *f) {
546 if (f) {
547 delete f;
548 _vm->getSaveFileMan()->removeSavefile(g_SaveSceneName);
549 }
550 g_SaveSceneName = NULL; // Invalidate save name
551 GUI::MessageDialog dialog(_("Failed to save game to file."));
552 dialog.runModal();
553 }
554
555 /**
556 * DoSave
557 */
DoSave()558 static void DoSave() {
559 Common::OutSaveFile *f;
560 char tmpName[FNAMELEN];
561
562 // Next getList() must do its stuff again
563 g_NeedLoad = true;
564
565 if (g_SaveSceneName == NULL) {
566 // Generate a new unique save name
567 int i;
568 int ano = 1; // Allocated number
569
570 while (1) {
571 Common::String fname = _vm->getSavegameFilename(ano);
572 Common::strlcpy(tmpName, fname.c_str(), FNAMELEN);
573
574 for (i = 0; i < g_numSfiles; i++)
575 if (!strcmp(g_savedFiles[i].name, tmpName))
576 break;
577
578 if (i == g_numSfiles)
579 break;
580 ano++;
581 }
582
583 g_SaveSceneName = tmpName;
584 }
585
586
587 if (g_SaveSceneDesc[0] == 0)
588 g_SaveSceneDesc = "unnamed";
589
590 f = _vm->getSaveFileMan()->openForSaving(g_SaveSceneName);
591 Common::Serializer s(0, f);
592
593 if (f == NULL) {
594 SaveFailure(f);
595 return;
596 }
597
598 // Write out a savegame header
599 SaveGameHeader hdr;
600 hdr.id = SAVEGAME_ID;
601 hdr.size = SAVEGAME_HEADER_SIZE;
602 hdr.ver = CURRENT_VER;
603 memset(hdr.desc, 0, SG_DESC_LEN);
604 Common::strlcpy(hdr.desc, g_SaveSceneDesc, SG_DESC_LEN);
605 g_system->getTimeAndDate(hdr.dateTime);
606 hdr.playTime = _vm->getTotalPlayTime();
607 hdr.scnFlag = _vm->getFeatures() & GF_SCNFILES;
608 hdr.language = _vm->_config->_language;
609
610 if (!syncSaveGameHeader(s, hdr) || f->err()) {
611 SaveFailure(f);
612 return;
613 }
614
615 DoSync(s, hdr.numInterpreters);
616
617 // Write out the special Id for Discworld savegames
618 f->writeUint32LE(0xFEEDFACE);
619 if (f->err()) {
620 SaveFailure(f);
621 return;
622 }
623
624 f->finalize();
625 delete f;
626 g_SaveSceneName = NULL; // Invalidate save name
627 }
628
629 /**
630 * ProcessSRQueue
631 */
ProcessSRQueue()632 void ProcessSRQueue() {
633 switch (g_SRstate) {
634 case SR_DORESTORE:
635 // If a load has been done directly from title screens, set a larger value for scene ctr so the
636 // code used to skip the title screens in Discworld 1 gets properly disabled
637 if (g_sceneCtr < 10)
638 g_sceneCtr = 10;
639
640 if (DoRestore()) {
641 DoRestoreScene(g_srsd, false);
642 }
643 g_SRstate = SR_IDLE;
644 break;
645
646 case SR_DOSAVE:
647 DoSave();
648 g_SRstate = SR_IDLE;
649 break;
650 default:
651 break;
652 }
653 }
654
655
RequestSaveGame(char * name,char * desc,SAVED_DATA * sd,int * pSsCount,SAVED_DATA * pSsData)656 void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
657 assert(g_SRstate == SR_IDLE);
658
659 g_SaveSceneName = name;
660 g_SaveSceneDesc = desc;
661 g_SaveSceneSsCount = pSsCount;
662 g_SaveSceneSsData = pSsData;
663 g_srsd = sd;
664 g_SRstate = SR_DOSAVE;
665 }
666
RequestRestoreGame(int num,SAVED_DATA * sd,int * pSsCount,SAVED_DATA * pSsData)667 void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
668 if (TinselV2) {
669 if (num == -1)
670 return;
671 else if (num == -2) {
672 // From CD change for restore
673 num = g_RestoreGameNumber;
674 }
675 }
676
677 assert(num >= 0);
678
679 g_RestoreGameNumber = num;
680 g_SaveSceneSsCount = pSsCount;
681 g_SaveSceneSsData = pSsData;
682 g_srsd = sd;
683 g_SRstate = SR_DORESTORE;
684 }
685
686 /**
687 * Returns the index of the most recently saved savegame. This will always be
688 * the file at the first index, since the list is sorted by date/time
689 */
NewestSavedGame()690 int NewestSavedGame() {
691 int numFiles = getList();
692
693 return (numFiles == 0) ? -1 : 0;
694 }
695
696 } // End of namespace Tinsel
697