scummvm/engines/saga2/loadsave.cpp

539 lines
13 KiB
C++
Raw Normal View History

2021-05-17 18:47:39 +00:00
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* aint32 with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* Based on the original sources
* Faery Tale II -- The Halls of the Dead
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
*/
#define FORBIDDEN_SYMBOL_ALLOW_ALL // FIXME: Remove
2021-06-23 12:41:01 +00:00
#include "saga2/saga2.h"
2021-05-17 18:47:39 +00:00
#include "saga2/loadsave.h"
#include "saga2/savefile.h"
#include "saga2/objects.h"
#include "saga2/tile.h"
#include "saga2/script.h"
#include "saga2/motion.h"
#include "saga2/task.h"
#include "saga2/speech.h"
#include "saga2/timers.h"
#include "saga2/sensor.h"
#include "saga2/band.h"
#include "saga2/mission.h"
#include "saga2/tilemode.h"
#include "saga2/magic.h"
#include "saga2/intrface.h"
#include "saga2/vpal.h"
2021-05-17 18:47:39 +00:00
#include "saga2/palette.h"
#include "saga2/contain.h"
2021-06-30 20:40:12 +00:00
#include "saga2/imagcach.h"
2021-05-17 18:47:39 +00:00
namespace Saga2 {
/* ===================================================================== *
Functions
* ===================================================================== */
// The following resources are included in the save file
// GLOB - miscellaneous globals
// TIME - game timer
// CALE - game calender
// WRLD - worlds
// ACTR - actors
// OBJS - objects
// BAND - actor bands
// PLYR - player actors
// CNTR - center actor ID and view object ID
// TAGS - active item instance state arrays
// CYCL - tile cycling states
// SDTA - SAGA data segment
// SAGA - SAGA threads
// MOTN - motion tasks
// TSTK - actor task stacks
// TASK - actor tasks
// TACT - tile activity tasks
// SPCH - speech tasks
// AREG - active regions
// TIMR - actor timers
// SENS - actor sensors
// ACNT - temporary actor count array
// MISS - missions
// FACT - faction tallies
// TMST - TileMode state
// SPEL - Active Spell List
// AMAP - auto map data
// UIST - user interface state
// PALE - palette state
// CONT - container nodes
//----------------------------------------------------------------------
// Load initial game state
void initGameState(void) {
pauseTimer();
initGlobals();
initTimer();
initCalender();
initWorlds();
initActors();
initObjects();
initBands();
initPlayerActors();
initCenterActor();
initActiveItemStates();
initTileCyclingStates();
initSAGADataSeg();
initSAGAThreads();
initMotionTasks();
initTaskStacks();
initTasks();
initTileTasks();
initSpeechTasks();
initActiveRegions();
initTimers();
initSensors();
initTempActorCount();
initMissions();
initFactionTallies();
initTileModeState();
initSpellState();
initAutoMap();
initUIState();
initPaletteState();
initContainerNodes();
resumeTimer();
}
//----------------------------------------------------------------------
// Save the current game state
2021-07-05 11:22:18 +00:00
Common::Error saveGameState(int16 saveNo, char *saveName) {
2021-05-17 18:47:39 +00:00
pauseTimer();
debugC(1, kDebugSaveload, "Saving game");
Common::OutSaveFile *out = g_vm->getSaveFileManager()->openForSaving(getSaveFileName(saveNo), false);
2021-07-05 11:22:18 +00:00
if (!out)
return Common::kCreatingFileFailed;
SaveFileHeader header;
header.gameID = gameID;
header.saveName = saveName;
2021-07-05 11:22:18 +00:00
header.write(out);
2021-05-17 18:47:39 +00:00
saveGlobals(out);
saveTimer(out);
saveCalender(out);
2021-07-06 15:12:52 +00:00
saveWorlds(out);
2021-07-07 00:25:04 +00:00
saveActors(out);
2021-07-07 22:09:21 +00:00
saveObjects(out);
2021-07-08 14:04:27 +00:00
saveBands(out);
savePlayerActors(out);
saveCenterActor(out);
saveActiveItemStates(out);
saveTileCyclingStates(out);
saveSAGADataSeg(out);
2021-07-05 11:22:18 +00:00
#if 0
2021-05-17 18:47:39 +00:00
saveSAGAThreads(saveGame);
saveMotionTasks(saveGame);
saveTaskStacks(saveGame);
saveTasks(saveGame);
saveTileTasks(saveGame);
saveSpeechTasks(saveGame);
saveActiveRegions(saveGame);
saveTimers(saveGame);
saveSensors(saveGame);
saveTempActorCount(saveGame);
saveMissions(saveGame);
saveFactionTallies(saveGame);
saveTileModeState(saveGame);
saveSpellState(saveGame);
saveAutoMap(saveGame);
saveUIState(saveGame);
savePaletteState(saveGame);
saveContainerNodes(saveGame);
2021-07-05 11:22:18 +00:00
#endif
out->finalize();
delete out;
2021-05-17 18:47:39 +00:00
resumeTimer();
2021-07-05 11:22:18 +00:00
return Common::kNoError;
2021-05-17 18:47:39 +00:00
}
//----------------------------------------------------------------------
// Load a previously saved game state
void loadSavedGameState(int16 saveNo) {
enum {
loadGlobalsFlag = (1 << 0),
loadTimerFlag = (1 << 1),
loadCalenderFlag = (1 << 2),
loadWorldsFlag = (1 << 3),
loadActorsFlag = (1 << 4),
loadObjectsFlag = (1 << 5),
loadBandsFlag = (1 << 6),
loadPlayerActorsFlag = (1 << 7),
loadCenterActorFlag = (1 << 8),
loadActiveItemStatesFlag = (1 << 9),
loadTileCyclingStatesFlag = (1 << 10),
loadSAGADataSegFlag = (1 << 11),
loadSAGAThreadsFlag = (1 << 12),
loadMotionTasksFlag = (1 << 13),
loadTaskStacksFlag = (1 << 14),
loadTasksFlag = (1 << 15),
loadTileTasksFlag = (1 << 16),
loadSpeechTasksFlag = (1 << 17),
loadActiveRegionsFlag = (1 << 18),
loadTimersFlag = (1 << 19),
loadSensorsFlag = (1 << 20),
loadTempActorCountFlag = (1 << 21),
loadMissionsFlag = (1 << 22),
loadFactionTalliesFlag = (1 << 23),
loadTileModeStateFlag = (1 << 24),
loadSpellStateFlag = (1 << 25),
loadAutoMapFlag = (1 << 26),
loadUIStateFlag = (1 << 27),
loadPaletteStateFlag = (1 << 28),
2021-06-29 21:38:51 +00:00
loadContainerNodesFlag = (1 << 29)
2021-05-17 18:47:39 +00:00
};
uint32 loadFlags = 0;
pauseTimer();
Common::InSaveFile *in = g_vm->getSaveFileManager()->openForLoading(getSaveFileName(saveNo));
//SaveFileReader saveGame(saveNo);
2021-05-17 18:47:39 +00:00
ChunkID id;
int32 chunkSize;
bool notEOF;
notEOF = firstChunk(in, id, chunkSize);
2021-05-17 18:47:39 +00:00
while (notEOF) {
switch (id) {
case MKTAG('G', 'L', 'O', 'B'):
loadGlobals(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadGlobalsFlag;
break;
case MKTAG('T', 'I', 'M', 'E'):
loadTimer(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadTimerFlag;
break;
case MKTAG('C', 'A', 'L', 'E'):
loadCalender(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadCalenderFlag;
break;
case MKTAG('W', 'R', 'L', 'D'):
2021-07-06 15:12:52 +00:00
loadWorlds(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadWorldsFlag;
break;
case MKTAG('A', 'C', 'T', 'R'):
loadActors(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadActorsFlag;
break;
case MKTAG('O', 'B', 'J', 'S'):
2021-07-07 22:09:21 +00:00
loadObjects(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadObjectsFlag;
break;
case MKTAG('B', 'A', 'N', 'D'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadActorsFlag) {
2021-07-08 14:04:27 +00:00
loadBands(in, chunkSize);
2021-05-17 18:47:39 +00:00
loadFlags |= loadBandsFlag;
} else
error("Bands loaded prematurely");
break;
case MKTAG('P', 'L', 'Y', 'R'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadBandsFlag) {
loadPlayerActors(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadPlayerActorsFlag;
} else
error("PlayerActors loaded prematurely");
break;
case MKTAG('C', 'N', 'T', 'R'):
loadCenterActor(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadCenterActorFlag;
break;
case MKTAG('T', 'A', 'G', 'S'):
loadActiveItemStates(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadActiveItemStatesFlag;
break;
case MKTAG('C', 'Y', 'C', 'L'):
loadTileCyclingStates(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadTileCyclingStatesFlag;
break;
case MKTAG('S', 'D', 'T', 'A'):
loadSAGADataSeg(in);
2021-05-17 18:47:39 +00:00
loadFlags |= loadSAGADataSegFlag;
break;
#if 0
2021-05-17 18:47:39 +00:00
case MKTAG('S', 'A', 'G', 'A'):
2021-05-17 18:47:39 +00:00
loadSAGAThreads(saveGame);
loadFlags |= loadSAGAThreadsFlag;
break;
case MKTAG('M', 'O', 'T', 'N'):
2021-05-17 18:47:39 +00:00
if (!(~loadFlags & (loadActorsFlag | loadObjectsFlag))) {
loadMotionTasks(saveGame);
loadFlags |= loadMotionTasksFlag;
} else
error("MotionTasks loaded prematurely");
break;
case MKTAG('T', 'S', 'T', 'K'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadActorsFlag) {
loadTaskStacks(saveGame);
loadFlags |= loadTaskStacksFlag;
} else
error("TaskStacks loaded prematurely");
break;
case MKTAG('T', 'A', 'S', 'K'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadTaskStacksFlag) {
loadTasks(saveGame);
loadFlags |= loadTasksFlag;
} else
error("Tasks loaded prematurely");
break;
case MKTAG('T', 'A', 'C', 'T'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadWorldsFlag) {
loadTileTasks(saveGame);
loadFlags |= loadTileTasksFlag;
} else
error("TileActivityTasks loaded prematurely");
break;
case MKTAG('S', 'P', 'C', 'H'):
2021-05-17 18:47:39 +00:00
if (!(~loadFlags & (loadActorsFlag | loadObjectsFlag))) {
loadSpeechTasks(saveGame);
loadFlags |= loadSpeechTasksFlag;
} else
error("SpeechTasks loaded prematurely");
break;
case MKTAG('A', 'R', 'E', 'G'):
2021-05-17 18:47:39 +00:00
loadActiveRegions(saveGame);
loadFlags |= loadActiveRegionsFlag;
break;
case MKTAG('T', 'I', 'M', 'R'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadActorsFlag) {
loadTimers(saveGame);
loadFlags |= loadTimersFlag;
} else
error("Timers loaded prematurely");
break;
case MKTAG('S', 'E', 'N', 'S'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadActorsFlag) {
loadSensors(saveGame);
loadFlags |= loadSensorsFlag;
} else
error("Sensors loaded prematurely");
break;
case MKTAG('A', 'C', 'N', 'T'):
2021-05-17 18:47:39 +00:00
loadTempActorCount(saveGame);
loadFlags |= loadTempActorCountFlag;
break;
case MKTAG('M', 'I', 'S', 'S'):
2021-05-17 18:47:39 +00:00
loadMissions(saveGame);
loadFlags |= loadMissionsFlag;
break;
case MKTAG('F', 'A', 'C', 'T'):
2021-05-17 18:47:39 +00:00
loadFactionTallies(saveGame);
loadFlags |= loadFactionTalliesFlag;
break;
case MKTAG('T', 'M', 'S', 'T'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadActorsFlag) {
loadTileModeState(saveGame);
loadFlags |= loadTileModeStateFlag;
} else
error("TileMode state loaded prematurely");
break;
case MKTAG('S', 'P', 'E', 'L'):
2021-05-17 18:47:39 +00:00
loadSpellState(saveGame);
loadFlags |= loadSpellStateFlag;
break;
case MKTAG('A', 'M', 'A', 'P'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadWorldsFlag) {
loadAutoMap(saveGame);
loadFlags |= loadAutoMapFlag;
} else
error("Auto map loaded prematurely");
break;
case MKTAG('U', 'I', 'S', 'T'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadPlayerActorsFlag) {
loadUIState(saveGame);
loadFlags |= loadUIStateFlag;
} else
error("UI state loaded prematurely");
break;
case MKTAG('P', 'A', 'L', 'E'):
2021-05-17 18:47:39 +00:00
loadPaletteState(saveGame);
loadFlags |= loadPaletteStateFlag;
break;
case MKTAG('C', 'O', 'N', 'T'):
2021-05-17 18:47:39 +00:00
if (loadFlags & loadObjectsFlag) {
loadContainerNodes(saveGame);
loadFlags |= loadContainerNodesFlag;
} else
error("ContainerNodes loaded prematurely");
break;
#endif
2021-05-17 18:47:39 +00:00
}
notEOF = nextChunk(in, id, chunkSize);
2021-05-17 18:47:39 +00:00
}
delete in;
2021-05-17 18:47:39 +00:00
if (!(loadFlags & loadGlobalsFlag))
error("Globals not loaded");
if (!(loadFlags & loadTimerFlag))
error("Timer not loaded");
if (!(loadFlags & loadCalenderFlag))
error("Game calender not loaded");
if (!(loadFlags & loadWorldsFlag))
error("Worlds not loaded");
if (!(loadFlags & loadObjectsFlag))
error("Objects not loaded");
if (!(loadFlags & loadActorsFlag))
error("Actors not loaded");
if (!(loadFlags & loadPlayerActorsFlag))
error("Player actors not loaded");
if (!(loadFlags & loadCenterActorFlag))
error("Center actor not loaded");
if (!(loadFlags & loadActiveItemStatesFlag))
error("Active item states not loaded");
if (!(loadFlags & loadTileCyclingStatesFlag))
error("Tile cycling states not loaded");
if (!(loadFlags & loadSAGADataSegFlag))
error("SAGA data segment not loaded");
if (!(loadFlags & loadSAGAThreadsFlag))
error("SAGA threads not loaded");
if (!(loadFlags & loadActiveRegionsFlag))
error("Active Regions not loaded");
2021-05-17 18:47:39 +00:00
resumeTimer();
}
//----------------------------------------------------------------------
// Cleanup the game state
void cleanupGameState(void) {
cleanupContainerNodes();
cleanupPaletteState();
cleanupUIState();
cleanupAutoMap();
cleanupSpellState();
cleanupTileModeState();
cleanupFactionTallies();
cleanupMissions();
cleanupTempActorCount();
cleanupSensors();
cleanupTimers();
cleanupActiveRegions();
cleanupSpeechTasks();
cleanupTileTasks();
cleanupTasks();
cleanupTaskStacks();
cleanupMotionTasks();
cleanupSAGAThreads();
cleanupSAGADataSeg();
cleanupTileCyclingStates();
cleanupActiveItemStates();
cleanupCenterActor();
cleanupPlayerActors();
cleanupBands();
cleanupObjects();
cleanupActors();
cleanupWorlds();
2021-06-27 20:59:41 +00:00
cleanupAudio();
2021-05-17 18:47:39 +00:00
cleanupTimer();
cleanupGlobals();
}
//#define DEBUG_FILETIME
#ifdef DEBUG_FILETIME
#include <time.h>
#endif
2021-06-08 17:00:13 +00:00
void checkRestartGame(const char *exeName) {
#if 0
2021-05-17 18:47:39 +00:00
char saveRestart[260];
getSaveFileName(999, saveRestart);
if (!fileExists(saveRestart) ||
(getFileDate(exeName) > getFileDate(saveRestart)))
saveGameState(999, saveRestart);
2021-06-08 17:00:13 +00:00
#endif
2021-05-17 18:47:39 +00:00
2021-06-08 17:00:13 +00:00
warning("STUB: checkRestartGame()");
2021-05-17 18:47:39 +00:00
}
void loadRestartGame(void) {
loadSavedGameState(999);
}
} // end of namespace Saga2