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.cpp155
1 files changed, 102 insertions, 53 deletions
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index c8076ec819..93b3a997cc 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -8,18 +8,19 @@
* 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
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
+#include "common/savefile.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/func.h"
@@ -40,6 +41,7 @@
#include "sci/graphics/helpers.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/screen.h"
#include "sci/parser/vocabulary.h"
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
@@ -132,13 +134,6 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Reset _scriptSegMap, to be restored below
_scriptSegMap.clear();
-
-#ifdef ENABLE_SCI32
- // Clear any planes/screen items currently showing so they
- // don't show up after the load.
- if (getSciVersion() >= SCI_VERSION_2)
- g_sci->_gfxFrameout->clear();
-#endif
}
s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide
@@ -219,25 +214,30 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
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;
+ // Now that all scripts are loaded, init their objects.
+ // Just like in Script::initializeObjectsSci0, we do two passes
+ // in case an object is loaded before its base.
+ int passes = getSciVersion() < SCI_VERSION_1_1 ? 2 : 1;
+ for (int pass = 1; pass <= passes; ++pass) {
+ for (uint i = 0; i < _heap.size(); i++) {
+ if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT)
+ continue;
- Script *scr = (Script *)_heap[i];
- scr->syncLocalsBlock(this);
+ Script *scr = (Script *)_heap[i];
+ scr->syncLocalsBlock(this);
- ObjMap objects = scr->getObjectMap();
- for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it) {
- reg_t addr = it->_value.getPos();
- Object *obj = scr->scriptObjInit(addr, false);
+ ObjMap objects = scr->getObjectMap();
+ for (ObjMap::iterator it = objects.begin(); it != 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));
- objects.erase(addr.toUint16());
+ if (pass == 2) {
+ 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));
+ objects.erase(addr.toUint16());
+ }
}
}
}
@@ -465,7 +465,7 @@ void Script::syncStringHeap(Common::Serializer &s) {
break;
} while (1);
- } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1){
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1){
// Strings in SCI1.1 come after the object instances
byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
@@ -484,7 +484,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
if (s.isLoading())
- load(_nr, g_sci->getResMan());
+ load(_nr, g_sci->getResMan(), g_sci->getScriptPatcher());
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _bufSize
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _scriptSize
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _heapSize
@@ -600,7 +600,10 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint16LE(dataInc);
s.syncAsSint16LE(ticker);
s.syncAsSint16LE(signal, VER(17));
- s.syncAsByte(priority);
+ if (s.getVersion() >= 31) // FE sound/music.h -> priority
+ s.syncAsSint16LE(priority);
+ else
+ s.syncAsByte(priority);
s.syncAsSint16LE(loop, VER(17));
s.syncAsByte(volume);
s.syncAsByte(hold, VER(17));
@@ -609,6 +612,14 @@ void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(fadeTicker);
s.syncAsSint32LE(fadeTickerStep);
s.syncAsByte(status);
+ if (s.getVersion() >= 32)
+ s.syncAsByte(playBed);
+ else if (s.isLoading())
+ playBed = false;
+ if (s.getVersion() >= 33)
+ s.syncAsByte(overridePriority);
+ else if (s.isLoading())
+ overridePriority = false;
// pMidiParser and pStreamAud will be initialized when the
// sound list is reconstructed in gamestate_restore()
@@ -627,20 +638,26 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) {
void SoundCommandParser::reconstructPlayList() {
Common::StackLock lock(_music->_mutex);
- const MusicList::iterator end = _music->getPlayListEnd();
- for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
+ // We store all songs here because starting songs may re-shuffle their order
+ MusicList songs;
+ for (MusicList::iterator i = _music->getPlayListStart(); i != _music->getPlayListEnd(); ++i)
+ songs.push_back(*i);
+
+ for (MusicList::iterator i = songs.begin(); i != songs.end(); ++i) {
initSoundResource(*i);
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.
+ // WORKAROUND: PQ3 (German?) scripts can set volume negative in the
+ // sound object directly without going through DoSound.
+ // Since we re-read this selector when re-playing the sound after loading,
+ // this will lead to unexpected behaviour. As a workaround we
+ // sync the sound object's selectors here. (See bug #5501)
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);
+ processPlaySound((*i)->soundObj, (*i)->playBed);
}
}
}
@@ -705,9 +722,7 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
- if (s.isLoading())
- reset(); // remove all script generated windows
-
+ // reset() is called directly way earlier in gamestate_restore()
if (s.getVersion() >= 27) {
uint windowCount = 0;
uint id = PORTS_FIRSTSCRIPTWINDOWID;
@@ -750,10 +765,17 @@ void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
if (window->counterTillFree) {
_freeCounter++;
} else {
- if (window->wndStyle & SCI_WINDOWMGR_STYLE_TOPMOST)
- _windowList.push_front(window);
- else
- _windowList.push_back(window);
+ // we don't put the saved script windows into _windowList[], so that they aren't used
+ // by kernel functions. This is important and would cause issues otherwise.
+ // see Conquests of Camelot - bug #6744 - when saving on the map screen (room 103),
+ // restoring would result in a black window in place
+ // where the area name was displayed before
+ // In Sierra's SCI the behaviour is identical to us
+ // Sierra's SCI won't show those windows after restoring
+ // If this should cause issues in another game, we would have to add a flag to simply
+ // avoid any drawing operations for such windows
+ // We still have to restore script windows, because for example Conquests of Camelot
+ // will immediately delete windows, that were created before saving the game.
}
windowCount--;
@@ -836,11 +858,29 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
if (voc)
voc->saveLoadWithSerializer(ser);
+ // TODO: SSCI (at least JonesCD, presumably more) also stores the Menu state
+
return true;
}
extern void showScummVMDialog(const Common::String &message);
+void gamestate_delayedrestore(EngineState *s) {
+ Common::String fileName = g_sci->getSavegameName(s->_delayedRestoreGameId);
+ Common::SeekableReadStream *in = g_sci->getSaveFileManager()->openForLoading(fileName);
+
+ if (in) {
+ // found a savegame file
+ gamestate_restore(s, in);
+ delete in;
+ if (s->r_acc != make_reg(0, 1)) {
+ return;
+ }
+ }
+
+ error("Restoring gamestate '%s' failed", fileName.c_str());
+}
+
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
SavegameMetadata meta;
@@ -852,16 +892,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
return;
}
- if ((meta.version < MINIMUM_SAVEGAME_VERSION) ||
- (meta.version > CURRENT_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.version, CURRENT_SAVEGAME_VERSION);
- */
-
- showScummVMDialog("The format of this saved game is obsolete, unable to load it");
+ if ((meta.version < MINIMUM_SAVEGAME_VERSION) || (meta.version > CURRENT_SAVEGAME_VERSION)) {
+ if (meta.version < MINIMUM_SAVEGAME_VERSION) {
+ showScummVMDialog("The format of this saved game is obsolete, unable to load it");
+ } else {
+ Common::String msg = Common::String::format("Savegame version is %d, maximum supported is %0d", meta.version, CURRENT_SAVEGAME_VERSION);
+ showScummVMDialog(msg);
+ }
s->r_acc = TRUE_REG; // signal failure
return;
@@ -870,8 +907,6 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
if (script0->size != meta.script0Size || g_sci->getGameObject().getOffset() != 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");
s->r_acc = TRUE_REG; // signal failure
@@ -882,6 +917,20 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
// We don't need the thumbnail here, so just read it and discard it
Graphics::skipThumbnail(*fh);
+ // reset ports is one of the first things we do, because that may free() some hunk memory
+ // and we don't want to do that after we read in the saved game hunk memory
+ if (g_sci->_gfxPorts)
+ g_sci->_gfxPorts->reset();
+ // clear screen
+ if (g_sci->_gfxScreen)
+ g_sci->_gfxScreen->clearForRestoreGame();
+#ifdef ENABLE_SCI32
+ // Also clear any SCI32 planes/screen items currently showing so they
+ // don't show up after the load.
+ if (getSciVersion() >= SCI_VERSION_2)
+ g_sci->_gfxFrameout->clear();
+#endif
+
s->reset(true);
s->saveLoadWithSerializer(ser); // FIXME: Error handling?