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 * aint32 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 * Based on the original sources
23 * Faery Tale II -- The Halls of the Dead
24 * (c) 1993-1996 The Wyrmkeep Entertainment Co.
25 */
26
27 #include "saga2/saga2.h"
28 #include "saga2/saveload.h"
29 #include "saga2/objects.h"
30 #include "saga2/tile.h"
31 #include "saga2/script.h"
32 #include "saga2/motion.h"
33 #include "saga2/task.h"
34 #include "saga2/speech.h"
35 #include "saga2/timers.h"
36 #include "saga2/sensor.h"
37 #include "saga2/band.h"
38 #include "saga2/mission.h"
39 #include "saga2/tilemode.h"
40 #include "saga2/magic.h"
41 #include "saga2/intrface.h"
42 #include "saga2/vpal.h"
43 #include "saga2/palette.h"
44 #include "saga2/contain.h"
45 #include "saga2/imagcach.h"
46
47 namespace Saga2 {
48
49 void updateMainDisplay(void);
50 void fadeUp();
51 void fadeDown();
52 void enablePaletteChanges();
53
54 const ChunkID gameID = MKTAG('F', 'T', 'A', '2');
55
read(Common::InSaveFile * in)56 void SaveFileHeader::read(Common::InSaveFile *in) {
57 char fileName[SaveFileHeader::kSaveNameSize];
58 gameID = in->readUint32BE();;
59 in->read(fileName, SaveFileHeader::kSaveNameSize);
60 saveName = fileName;
61 }
62
write(Common::OutSaveFile * out)63 void SaveFileHeader::write(Common::OutSaveFile *out) {
64 out->writeUint32BE(gameID);
65 out->write(saveName.c_str(), saveName.size());
66
67 int remainingBytes = SaveFileHeader::kHeaderSize - saveName.size() - 4;
68
69 for (int i = 0; i < remainingBytes; ++i)
70 out->writeByte(0);
71
72 debugC(1, kDebugSaveload, "Writing game header: gameID = %s, saveName = %s", tag2str(gameID), saveName.c_str());
73 }
74
75 /* ===================================================================== *
76 Functions
77 * ===================================================================== */
78
79 // The following resources are included in the save file
80 // GLOB - miscellaneous globals
81 // TIME - game timer
82 // CALE - game calender
83 // WRLD - worlds
84 // ACTR - actors
85 // OBJS - objects
86 // BAND - actor bands
87 // PLYR - player actors
88 // CNTR - center actor ID and view object ID
89 // TAGS - active item instance state arrays
90 // CYCL - tile cycling states
91 // SDTA - SAGA data segment
92 // SAGA - SAGA threads
93 // MOTN - motion tasks
94 // TSTK - actor task stacks
95 // TASK - actor tasks
96 // TACT - tile activity tasks
97 // SPCH - speech tasks
98 // AREG - active regions
99 // TIMR - actor timers
100 // SENS - actor sensors
101 // ACNT - temporary actor count array
102 // MISS - missions
103 // FACT - faction tallies
104 // TMST - TileMode state
105 // SPEL - Active Spell List
106 // AMAP - auto map data
107 // UIST - user interface state
108 // PALE - palette state
109 // CONT - container nodes
110
111 //----------------------------------------------------------------------
112 // Load initial game state
113
initGameState(void)114 void initGameState(void) {
115 pauseTimer();
116
117 initGlobals();
118 initCalender();
119 initWorlds();
120 initActors();
121 initObjects();
122 initBands();
123 initPlayerActors();
124 initCenterActor();
125 initActiveItemStates();
126 initTileCyclingStates();
127 initSAGADataSeg();
128 initSAGAThreads();
129 initMotionTasks();
130 initTaskStacks();
131 initTasks();
132 initTileTasks();
133 initSpeechTasks();
134 initActiveRegions();
135 initSensors();
136 initTempActorCount();
137 initMissions();
138 initFactionTallies();
139 initTileModeState();
140 initSpellState();
141 initAutoMap();
142 initUIState();
143 initPaletteState();
144 initContainerNodes();
145
146 resumeTimer();
147 }
148
149 //----------------------------------------------------------------------
150 // Save the current game state
151
saveGame(Common::OutSaveFile * out,Common::String saveName)152 void saveGame(Common::OutSaveFile *out, Common::String saveName) {
153 debugC(1, kDebugSaveload, "Saving game");
154
155 SaveFileHeader header;
156 header.gameID = gameID;
157 header.saveName = saveName;
158
159 header.write(out);
160
161 saveGlobals(out);
162 saveTimer(out);
163 saveCalender(out);
164 saveWorlds(out);
165 saveActors(out);
166 saveObjects(out);
167 saveBands(out);
168 savePlayerActors(out);
169 saveCenterActor(out);
170 saveActiveItemStates(out);
171 saveTileCyclingStates(out);
172 saveSAGADataSeg(out);
173 saveSAGAThreads(out);
174 saveMotionTasks(out);
175 saveTaskStacks(out);
176 saveTasks(out);
177 saveTileTasks(out);
178 saveSpeechTasks(out);
179 saveActiveRegions(out);
180 saveTimers(out);
181 saveSensors(out);
182 saveTempActorCount(out);
183 saveMissions(out);
184 saveFactionTallies(out);
185 saveTileModeState(out);
186 saveSpellState(out);
187 saveAutoMap(out);
188 saveUIState(out);
189 savePaletteState(out);
190 saveContainerNodes(out);
191 }
192
firstChunk(Common::InSaveFile * in,ChunkID & chunk,int32 & size)193 bool firstChunk(Common::InSaveFile *in, ChunkID &chunk, int32 &size) {
194 if (!in->seek(SaveFileHeader::kHeaderSize, SEEK_SET))
195 error("Error seeking first save game chunk");
196
197 if (in->size() - in->pos() < 8) {
198 debugC(1, kDebugSaveload, "Reached EOF on first Chunk %s", tag2str(chunk));
199 return false;
200 }
201
202 chunk = in->readUint32BE();
203 size = in->readUint32LE();
204
205 debugC(1, kDebugSaveload, "First Chunk loaded: chunkID = %s, size = %d", tag2str(chunk), size);
206
207 return true;
208 }
209
nextChunk(Common::InSaveFile * in,ChunkID & chunk,int32 & size)210 bool nextChunk(Common::InSaveFile *in, ChunkID &chunk, int32 &size) {
211 if (in->size() - in->pos() < 8) {
212 debugC(1, kDebugSaveload, "Reached EOF at %s", tag2str(chunk));
213 return false;
214 }
215
216 chunk = in->readUint32BE();
217 size = in->readUint32LE();
218
219 debugC(1, kDebugSaveload, "Next Chunk loaded: chunkID = %s, size = %d", tag2str(chunk), size);
220
221 // Return success
222 return true;
223 }
224
225 //----------------------------------------------------------------------
226 // Load a previously saved game state
227
loadSavedGameState(int16 saveNo)228 void loadSavedGameState(int16 saveNo) {
229 enum {
230 loadGlobalsFlag = (1 << 0),
231 loadTimerFlag = (1 << 1),
232 loadCalenderFlag = (1 << 2),
233 loadWorldsFlag = (1 << 3),
234 loadActorsFlag = (1 << 4),
235 loadObjectsFlag = (1 << 5),
236 loadBandsFlag = (1 << 6),
237 loadPlayerActorsFlag = (1 << 7),
238 loadCenterActorFlag = (1 << 8),
239 loadActiveItemStatesFlag = (1 << 9),
240 loadTileCyclingStatesFlag = (1 << 10),
241 loadSAGADataSegFlag = (1 << 11),
242 loadSAGAThreadsFlag = (1 << 12),
243 loadMotionTasksFlag = (1 << 13),
244 loadTaskStacksFlag = (1 << 14),
245 loadTasksFlag = (1 << 15),
246 loadTileTasksFlag = (1 << 16),
247 loadSpeechTasksFlag = (1 << 17),
248 loadActiveRegionsFlag = (1 << 18),
249 loadTimersFlag = (1 << 19),
250 loadSensorsFlag = (1 << 20),
251 loadTempActorCountFlag = (1 << 21),
252 loadMissionsFlag = (1 << 22),
253 loadFactionTalliesFlag = (1 << 23),
254 loadTileModeStateFlag = (1 << 24),
255 loadSpellStateFlag = (1 << 25),
256 loadAutoMapFlag = (1 << 26),
257 loadUIStateFlag = (1 << 27),
258 loadPaletteStateFlag = (1 << 28),
259 loadContainerNodesFlag = (1 << 29)
260 };
261
262 uint32 loadFlags = 0;
263
264 pauseTimer();
265
266 Common::InSaveFile *in = g_vm->getSaveFileManager()->openForLoading(g_vm->getSavegameFile(saveNo));
267
268 ChunkID id;
269 int32 chunkSize;
270 bool notEOF;
271
272 notEOF = firstChunk(in, id, chunkSize);
273 while (notEOF) {
274 switch (id) {
275 case MKTAG('G', 'L', 'O', 'B'):
276 loadGlobals(in);
277 loadFlags |= loadGlobalsFlag;
278 break;
279
280 case MKTAG('T', 'I', 'M', 'E'):
281 loadTimer(in);
282 loadFlags |= loadTimerFlag;
283 break;
284
285 case MKTAG('C', 'A', 'L', 'E'):
286 loadCalender(in);
287 loadFlags |= loadCalenderFlag;
288 break;
289
290 case MKTAG('W', 'R', 'L', 'D'):
291 loadWorlds(in);
292 loadFlags |= loadWorldsFlag;
293 break;
294
295 case MKTAG('A', 'C', 'T', 'R'):
296 loadActors(in);
297 loadFlags |= loadActorsFlag;
298 break;
299
300 case MKTAG('O', 'B', 'J', 'S'):
301 loadObjects(in);
302 loadFlags |= loadObjectsFlag;
303 break;
304
305 case MKTAG('B', 'A', 'N', 'D'):
306 if (loadFlags & loadActorsFlag) {
307 loadBands(in, chunkSize);
308 loadFlags |= loadBandsFlag;
309 } else
310 error("Bands loaded prematurely");
311 break;
312
313 case MKTAG('P', 'L', 'Y', 'R'):
314 if (loadFlags & loadBandsFlag) {
315 loadPlayerActors(in);
316 loadFlags |= loadPlayerActorsFlag;
317 } else
318 error("PlayerActors loaded prematurely");
319 break;
320
321 case MKTAG('C', 'N', 'T', 'R'):
322 loadCenterActor(in);
323 loadFlags |= loadCenterActorFlag;
324 break;
325
326 case MKTAG('T', 'A', 'G', 'S'):
327 loadActiveItemStates(in);
328 loadFlags |= loadActiveItemStatesFlag;
329 break;
330
331 case MKTAG('C', 'Y', 'C', 'L'):
332 loadTileCyclingStates(in);
333 loadFlags |= loadTileCyclingStatesFlag;
334 break;
335
336 case MKTAG('S', 'D', 'T', 'A'):
337 loadSAGADataSeg(in);
338 loadFlags |= loadSAGADataSegFlag;
339 break;
340
341 case MKTAG('S', 'A', 'G', 'A'):
342 loadSAGAThreads(in, chunkSize);
343 loadFlags |= loadSAGAThreadsFlag;
344 break;
345
346 case MKTAG('M', 'O', 'T', 'N'):
347 if (!(~loadFlags & (loadActorsFlag | loadObjectsFlag))) {
348 loadMotionTasks(in, chunkSize);
349 loadFlags |= loadMotionTasksFlag;
350 } else
351 error("MotionTasks loaded prematurely");
352 break;
353
354 case MKTAG('T', 'S', 'T', 'K'):
355 if (loadFlags & loadActorsFlag) {
356 loadTaskStacks(in, chunkSize);
357 loadFlags |= loadTaskStacksFlag;
358 } else
359 error("TaskStacks loaded prematurely");
360 break;
361
362 case MKTAG('T', 'A', 'S', 'K'):
363 if (loadFlags & loadTaskStacksFlag) {
364 loadTasks(in, chunkSize);
365 loadFlags |= loadTasksFlag;
366 } else
367 error("Tasks loaded prematurely");
368 break;
369
370 case MKTAG('T', 'A', 'C', 'T'):
371 if (loadFlags & loadWorldsFlag) {
372 loadTileTasks(in, chunkSize);
373 loadFlags |= loadTileTasksFlag;
374 } else
375 error("TileActivityTasks loaded prematurely");
376 break;
377
378 case MKTAG('S', 'P', 'C', 'H'):
379 if (!(~loadFlags & (loadActorsFlag | loadObjectsFlag))) {
380 loadSpeechTasks(in, chunkSize);
381 loadFlags |= loadSpeechTasksFlag;
382 } else
383 error("SpeechTasks loaded prematurely");
384 break;
385
386 case MKTAG('A', 'R', 'E', 'G'):
387 loadActiveRegions(in);
388 loadFlags |= loadActiveRegionsFlag;
389 break;
390
391 case MKTAG('T', 'I', 'M', 'R'):
392 if (loadFlags & loadActorsFlag) {
393 loadTimers(in);
394 loadFlags |= loadTimersFlag;
395 } else
396 error("Timers loaded prematurely");
397 break;
398
399 case MKTAG('S', 'E', 'N', 'S'):
400 if (loadFlags & loadActorsFlag) {
401 loadSensors(in);
402 loadFlags |= loadSensorsFlag;
403 } else
404 error("Sensors loaded prematurely");
405 break;
406
407 case MKTAG('A', 'C', 'N', 'T'):
408 loadTempActorCount(in, chunkSize);
409 loadFlags |= loadTempActorCountFlag;
410 break;
411
412 case MKTAG('M', 'I', 'S', 'S'):
413 loadMissions(in);
414 loadFlags |= loadMissionsFlag;
415 break;
416
417 case MKTAG('F', 'A', 'C', 'T'):
418 loadFactionTallies(in);
419 loadFlags |= loadFactionTalliesFlag;
420 break;
421
422 case MKTAG('T', 'M', 'S', 'T'):
423 if (loadFlags & loadActorsFlag) {
424 loadTileModeState(in);
425 loadFlags |= loadTileModeStateFlag;
426 } else
427 error("TileMode state loaded prematurely");
428 break;
429
430 case MKTAG('S', 'P', 'E', 'L'):
431 loadSpellState(in);
432 loadFlags |= loadSpellStateFlag;
433 break;
434
435 case MKTAG('A', 'M', 'A', 'P'):
436 if (loadFlags & loadWorldsFlag) {
437 loadAutoMap(in, chunkSize);
438 loadFlags |= loadAutoMapFlag;
439 } else
440 error("Auto map loaded prematurely");
441 break;
442
443 case MKTAG('U', 'I', 'S', 'T'):
444 if (loadFlags & loadPlayerActorsFlag) {
445 loadUIState(in);
446 loadFlags |= loadUIStateFlag;
447 } else
448 error("UI state loaded prematurely");
449 break;
450
451 case MKTAG('P', 'A', 'L', 'E'):
452 loadPaletteState(in);
453 loadFlags |= loadPaletteStateFlag;
454 break;
455
456 case MKTAG('C', 'O', 'N', 'T'):
457 if (loadFlags & loadObjectsFlag) {
458 loadContainerNodes(in);
459 loadFlags |= loadContainerNodesFlag;
460 } else
461 error("ContainerNodes loaded prematurely");
462 break;
463 }
464
465 if (loadFlags & loadContainerNodesFlag)
466 break;
467 notEOF = nextChunk(in, id, chunkSize);
468 }
469
470 if (!(loadFlags & loadGlobalsFlag))
471 error("Globals not loaded");
472
473 if (!(loadFlags & loadTimerFlag))
474 error("Timer not loaded");
475
476 if (!(loadFlags & loadCalenderFlag))
477 error("Game calender not loaded");
478
479 if (!(loadFlags & loadWorldsFlag))
480 error("Worlds not loaded");
481
482 if (!(loadFlags & loadObjectsFlag))
483 error("Objects not loaded");
484
485 if (!(loadFlags & loadActorsFlag))
486 error("Actors not loaded");
487
488 if (!(loadFlags & loadPlayerActorsFlag))
489 error("Player actors not loaded");
490
491 if (!(loadFlags & loadCenterActorFlag))
492 error("Center actor not loaded");
493
494 if (!(loadFlags & loadActiveItemStatesFlag))
495 error("Active item states not loaded");
496
497 if (!(loadFlags & loadTileCyclingStatesFlag))
498 error("Tile cycling states not loaded");
499
500 if (!(loadFlags & loadSAGADataSegFlag))
501 error("SAGA data segment not loaded");
502
503 if (!(loadFlags & loadSAGAThreadsFlag))
504 error("SAGA threads not loaded");
505
506 if (!(loadFlags & loadActiveRegionsFlag))
507 error("Active Regions not loaded");
508
509 ExtendedSavegameHeader header;
510 if (MetaEngine::readSavegameHeader(in, &header)) {
511 g_vm->setTotalPlayTime(header.playtime * 1000);
512 }
513
514 delete in;
515
516 resumeTimer();
517 }
518
loadGame(int16 saveNo)519 void loadGame(int16 saveNo) {
520 disableUserControls();
521 cleanupGameState();
522 fadeDown();
523 loadSavedGameState(saveNo);
524 if (GameMode::newmodeFlag)
525 GameMode::update();
526 updateActiveRegions();
527 enableUserControls();
528 updateMainDisplay();
529 drawMainDisplay();
530 enablePaletteChanges();
531 updateAllUserControls();
532 fadeUp();
533 reDrawScreen();
534 }
535
536 //----------------------------------------------------------------------
537 // Cleanup the game state
538
cleanupGameState(void)539 void cleanupGameState(void) {
540 cleanupContainerNodes();
541 cleanupPaletteState();
542 cleanupUIState();
543 cleanupAutoMap();
544 cleanupSpellState();
545 cleanupTileModeState();
546 cleanupFactionTallies();
547 cleanupMissions();
548 cleanupTempActorCount();
549 cleanupSensors();
550 cleanupTimers();
551 cleanupActiveRegions();
552 cleanupSpeechTasks();
553 cleanupTileTasks();
554 cleanupTasks();
555 cleanupTaskStacks();
556 cleanupMotionTasks();
557 cleanupSAGAThreads();
558 cleanupSAGADataSeg();
559 cleanupTileCyclingStates();
560 cleanupActiveItemStates();
561 cleanupCenterActor();
562 cleanupPlayerActors();
563 cleanupBands();
564 cleanupObjects();
565 cleanupActors();
566 cleanupWorlds();
567 cleanupGlobals();
568 }
569
570 //#define DEBUG_FILETIME
571
572 #ifdef DEBUG_FILETIME
573 #include <time.h>
574 #endif
575
checkRestartGame(const char * exeName)576 void checkRestartGame(const char *exeName) {
577 Common::String saveRestart = g_vm->getSavegameFile(999);
578 g_vm->saveGameState(999, saveRestart, true);
579 }
580
581
loadRestartGame(void)582 void loadRestartGame(void) {
583 loadSavedGameState(999);
584 }
585
586 } // end of namespace Saga2
587