aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/savegame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/savegame.cpp')
-rw-r--r--engines/sci/engine/savegame.cpp274
1 files changed, 196 insertions, 78 deletions
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 87e328592f..715b3b5127 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -40,6 +40,7 @@
#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
+#include "sci/graphics/helpers.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
#include "sci/sound/audio.h"
@@ -155,9 +156,26 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Let the object sync custom data
mobj->saveLoadWithSerializer(s);
- // If we are loading a script, hook it up in the script->segment map.
- if (s.isLoading() && type == SEG_TYPE_SCRIPT)
- _scriptSegMap[((Script *)mobj)->getScriptNumber()] = i;
+ // If we are saving a script, save its string heap space too
+ if (s.isSaving() && type == SEG_TYPE_SCRIPT)
+ ((Script *)mobj)->syncStringHeap(s);
+
+ // If we are loading a script, perform some extra steps
+ if (s.isLoading() && type == SEG_TYPE_SCRIPT) {
+ Script *scr = (Script *)mobj;
+ // Hook the script up in the script->segment map
+ _scriptSegMap[scr->getScriptNumber()] = i;
+
+ // Now, load the script itself
+ scr->load(g_sci->getResMan());
+
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
+ it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().offset));
+
+ // Load the script's string heap
+ if (s.getVersion() >= 28)
+ scr->syncStringHeap(s);
+ }
}
s.syncAsSint32LE(_clonesSegId);
@@ -165,6 +183,29 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nodesSegId);
syncArray<Class>(s, _classTable);
+
+ // Now that all scripts are loaded, init their objects
+ for (uint i = 0; i < _heap.size(); i++) {
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
+ continue;
+
+ Script *scr = (Script *)_heap[i];
+ scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
+
+ for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ reg_t addr = it->_value.getPos();
+ Object *obj = scr->scriptObjInit(addr, false);
+
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ if (!obj->initBaseObject(this, addr, false)) {
+ // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
+ // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
+ warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
+ scr->_objects.erase(addr.toUint16());
+ }
+ }
+ }
+ }
}
@@ -175,27 +216,34 @@ void syncWithSerializer(Common::Serializer &s, Class &obj) {
}
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
- // TODO: It would be a good idea to store a magic number & a header size here,
- // so that we can implement backward compatibility if the savegame format changes.
-
- s.syncString(obj.savegame_name);
+ s.syncString(obj.name);
s.syncVersion(CURRENT_SAVEGAME_VERSION);
- obj.savegame_version = s.getVersion();
- s.syncString(obj.game_version);
- s.syncAsSint32LE(obj.savegame_date);
- s.syncAsSint32LE(obj.savegame_time);
+ obj.version = s.getVersion();
+ s.syncString(obj.gameVersion);
+ s.syncAsSint32LE(obj.saveDate);
+ s.syncAsSint32LE(obj.saveTime);
if (s.getVersion() < 22) {
- obj.game_object_offset = 0;
- obj.script0_size = 0;
+ obj.gameObjectOffset = 0;
+ obj.script0Size = 0;
} else {
- s.syncAsUint16LE(obj.game_object_offset);
- s.syncAsUint16LE(obj.script0_size);
+ s.syncAsUint16LE(obj.gameObjectOffset);
+ s.syncAsUint16LE(obj.script0Size);
+ }
+
+ // Playtime
+ obj.playTime = 0;
+ if (s.isLoading()) {
+ if (s.getVersion() >= 26)
+ s.syncAsUint32LE(obj.playTime);
+ } else {
+ obj.playTime = g_engine->getTotalPlayTime() / 1000;
+ s.syncAsUint32LE(obj.playTime);
}
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
Common::String tmp;
- s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version
+ s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be gameVersion
if (getSciVersion() <= SCI_VERSION_1_1) {
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
@@ -352,6 +400,46 @@ void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
// Do nothing, hunk tables are not actually saved nor loaded.
}
+void Script::syncStringHeap(Common::Serializer &s) {
+ if (getSciVersion() < SCI_VERSION_1_1) {
+ // Sync all of the SCI_OBJ_STRINGS blocks
+ byte *buf = _buf;
+ bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+
+ if (oldScriptHeader)
+ buf += 2;
+
+ do {
+ int blockType = READ_LE_UINT16(buf);
+ int blockSize;
+ if (blockType == 0)
+ break;
+
+ blockSize = READ_LE_UINT16(buf + 2);
+ assert(blockSize > 0);
+
+ if (blockType == SCI_OBJ_STRINGS)
+ s.syncBytes(buf, blockSize);
+
+ buf += blockSize;
+
+ if (_buf - buf == 0)
+ break;
+ } while (1);
+
+ } else {
+ // Strings in SCI1.1 come after the object instances
+ byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
+
+ // Skip all of the objects
+ while (READ_SCI11ENDIAN_UINT16(buf) == SCRIPT_OBJECT_MAGIC_NUMBER)
+ buf += READ_SCI11ENDIAN_UINT16(buf + 2) * 2;
+
+ // Now, sync everything till the end of the buffer
+ s.syncBytes(buf, _heapSize - (buf - _heapStart));
+ }
+}
+
void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
@@ -469,7 +557,7 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
soundSetSoundOn(_soundOn);
soundSetMasterVolume(masterVolume);
- setReverb(reverb);
+ setGlobalReverb(reverb);
}
if (s.isSaving())
@@ -513,6 +601,7 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
soundRes = 0;
pMidiParser = 0;
pStreamAud = 0;
+ reverb = -1; // invalid reverb, will be initialized in processInitSound()
}
}
@@ -520,7 +609,7 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) {
_music->saveLoadWithSerializer(s);
}
-void SoundCommandParser::reconstructPlayList(int savegame_version) {
+void SoundCommandParser::reconstructPlayList() {
Common::StackLock lock(_music->_mutex);
const MusicList::iterator end = _music->getPlayListEnd();
@@ -532,6 +621,14 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) {
(*i)->soundRes = 0;
}
if ((*i)->status == kSoundPlaying) {
+ // Sync the sound object's selectors related to playing with the stored
+ // ones in the playlist, as they may have been invalidated when loading.
+ // Refer to bug #3104624.
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(loop), (*i)->loop);
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(priority), (*i)->priority);
+ if (_soundVersion >= SCI_VERSION_1_EARLY)
+ writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol), (*i)->volume);
+
processPlaySound((*i)->soundObj);
}
}
@@ -595,50 +692,70 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
}
-void SegManager::reconstructStack(EngineState *s) {
- DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
- s->stack_base = stack->_entries;
- s->stack_top = s->stack_base + stack->_capacity;
-}
-
-// TODO: Move this function to a more appropriate place, such as vm.cpp or script.cpp
-void SegManager::reconstructScripts(EngineState *s) {
- uint i;
-
- for (i = 0; i < _heap.size(); i++) {
- if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
- continue;
-
- Script *scr = (Script *)_heap[i];
- scr->load(g_sci->getResMan());
- scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
-
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
- it->_value._baseObj = scr->getBuf(it->_value.getPos().offset);
- }
-
- for (i = 0; i < _heap.size(); i++) {
- if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
- continue;
-
- Script *scr = (Script *)_heap[i];
-
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
- reg_t addr = it->_value.getPos();
- Object *obj = scr->scriptObjInit(addr, false);
-
- if (getSciVersion() < SCI_VERSION_1_1) {
- if (!obj->initBaseObject(this, addr, false)) {
- // TODO/FIXME: This should not be happening at all. It might indicate a possible issue
- // with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
- warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
- scr->scriptObjRemove(addr);
+void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.isLoading())
+ reset(); // remove all script generated windows
+
+ if (s.getVersion() >= 27) {
+ uint windowCount = 0;
+ uint id = PORTS_FIRSTSCRIPTWINDOWID;
+ if (s.isSaving()) {
+ while (id < _windowsById.size()) {
+ if (_windowsById[id])
+ windowCount++;
+ id++;
+ }
+ }
+ // Save/Restore window count
+ s.syncAsUint32LE(windowCount);
+
+ if (s.isSaving()) {
+ id = PORTS_FIRSTSCRIPTWINDOWID;
+ while (id < _windowsById.size()) {
+ if (_windowsById[id]) {
+ Window *window = (Window *)_windowsById[id];
+ window->saveLoadWithSerializer(s);
+ }
+ id++;
+ }
+ } else {
+ id = PORTS_FIRSTSCRIPTWINDOWID;
+ while (windowCount) {
+ Window *window = new Window(0);
+ window->saveLoadWithSerializer(s);
+
+ // add enough entries inside _windowsById as needed
+ while (id <= window->id) {
+ _windowsById.push_back(0);
+ id++;
}
+ _windowsById[window->id] = window;
+ // _windowList may not be 100% correct using that way of restoring
+ // saving/restoring ports won't work perfectly anyway, because the contents
+ // of the window can only get repainted by the scripts and they dont do that
+ // so we will get empty, transparent windows instead. So perfect window order
+ // shouldn't really matter
+ if (window->counterTillFree) {
+ _freeCounter++;
+ } else {
+ if (window->wndStyle & SCI_WINDOWMGR_STYLE_TOPMOST)
+ _windowList.push_front(window);
+ else
+ _windowList.push_back(window);
+ }
+
+ windowCount--;
}
}
}
}
+void SegManager::reconstructStack(EngineState *s) {
+ DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
+ s->stack_base = stack->_entries;
+ s->stack_top = s->stack_base + stack->_capacity;
+}
+
void SegManager::reconstructClones() {
for (uint i = 0; i < _heap.size(); i++) {
SegmentObj *mobj = _heap[i];
@@ -681,15 +798,15 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
g_system->getTimeAndDate(curTime);
SavegameMetadata meta;
- meta.savegame_version = CURRENT_SAVEGAME_VERSION;
- meta.savegame_name = savename;
- meta.game_version = version;
- meta.savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
- meta.savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+ meta.version = CURRENT_SAVEGAME_VERSION;
+ meta.name = savename;
+ meta.gameVersion = version;
+ meta.saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta.saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- meta.script0_size = script0->size;
- meta.game_object_offset = g_sci->getGameObject().offset;
+ meta.script0Size = script0->size;
+ meta.gameObjectOffset = g_sci->getGameObject().offset;
// Checking here again
if (s->executionStackBase) {
@@ -701,6 +818,8 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
sync_SavegameMetadata(ser, meta);
Graphics::saveThumbnail(*fh);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+ if (g_sci->_gfxPorts)
+ g_sci->_gfxPorts->saveLoadWithSerializer(ser);
return true;
}
@@ -718,13 +837,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if ((meta.savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta.savegame_version > CURRENT_SAVEGAME_VERSION)) {
+ if ((meta.version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta.version > CURRENT_SAVEGAME_VERSION)) {
/*
- if (meta.savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if (meta.version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected, unable to load it");
else
- warning("Savegame version is %d, maximum supported is %0d", meta.savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d, maximum supported is %0d", meta.version, CURRENT_SAVEGAME_VERSION);
*/
showScummVMDialog("The format of this saved game is obsolete, unable to load it");
@@ -733,9 +852,9 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if (meta.game_object_offset > 0 && meta.script0_size > 0) {
+ if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- if (script0->size != meta.script0_size || g_sci->getGameObject().offset != meta.game_object_offset) {
+ if (script0->size != meta.script0Size || g_sci->getGameObject().offset != meta.gameObjectOffset) {
//warning("This saved game was created with a different version of the game, unable to load it");
showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
@@ -751,23 +870,22 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
s->reset(true);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
+
// Now copy all current state information
s->_segMan->reconstructStack(s);
- s->_segMan->reconstructScripts(s);
s->_segMan->reconstructClones();
s->initGlobals();
s->gcCountDown = GC_INTERVAL - 1;
// Time state:
s->lastWaitTime = g_system->getMillis();
- s->gameStartTime = g_system->getMillis();
s->_screenUpdateTime = g_system->getMillis();
+ g_engine->setTotalPlayTime(meta.playTime * 1000);
if (g_sci->_gfxPorts)
- g_sci->_gfxPorts->reset();
-
- g_sci->_soundCmd->reconstructPlayList(meta.savegame_version);
+ g_sci->_gfxPorts->saveLoadWithSerializer(ser);
+ g_sci->_soundCmd->reconstructPlayList();
// Message state:
delete s->_msgState;
@@ -789,12 +907,12 @@ bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata
if (stream->eos())
return false;
- if ((meta->savegame_version < MINIMUM_SAVEGAME_VERSION) ||
- (meta->savegame_version > CURRENT_SAVEGAME_VERSION)) {
- if (meta->savegame_version < MINIMUM_SAVEGAME_VERSION)
+ if ((meta->version < MINIMUM_SAVEGAME_VERSION) ||
+ (meta->version > CURRENT_SAVEGAME_VERSION)) {
+ if (meta->version < MINIMUM_SAVEGAME_VERSION)
warning("Old savegame version detected- can't load");
else
- warning("Savegame version is %d- maximum supported is %0d", meta->savegame_version, CURRENT_SAVEGAME_VERSION);
+ warning("Savegame version is %d- maximum supported is %0d", meta->version, CURRENT_SAVEGAME_VERSION);
return false;
}