diff options
Diffstat (limited to 'engines/sci/engine/savegame.cpp')
-rw-r--r-- | engines/sci/engine/savegame.cpp | 274 |
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; } |