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.cpp269
1 files changed, 182 insertions, 87 deletions
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 0972aec4a4..720f6783ee 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -48,6 +48,7 @@
#include "sci/sound/music.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/cursor32.h"
#include "sci/graphics/frameout.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
@@ -101,71 +102,12 @@ void syncWithSerializer(Common::Serializer &s, Node &obj) {
syncWithSerializer(s, obj.value);
}
-#ifdef ENABLE_SCI32
-void syncWithSerializer(Common::Serializer &s, SciArray<reg_t> &obj) {
- byte type = 0;
- uint32 size = 0;
-
- if (s.isSaving()) {
- type = (byte)obj.getType();
- size = obj.getSize();
- }
- s.syncAsByte(type);
- s.syncAsUint32LE(size);
- if (s.isLoading()) {
- obj.setType((int8)type);
-
- // HACK: Skip arrays that have a negative type
- if ((int8)type < 0)
- return;
-
- obj.setSize(size);
- }
-
- for (uint32 i = 0; i < size; i++) {
- reg_t value;
-
- if (s.isSaving())
- value = obj.getValue(i);
-
- syncWithSerializer(s, value);
-
- if (s.isLoading())
- obj.setValue(i, value);
- }
-}
-
-void syncWithSerializer(Common::Serializer &s, SciString &obj) {
- uint32 size = 0;
-
- if (s.isSaving()) {
- size = obj.getSize();
- s.syncAsUint32LE(size);
- } else {
- s.syncAsUint32LE(size);
- obj.setSize(size);
- }
-
- for (uint32 i = 0; i < size; i++) {
- char value = 0;
-
- if (s.isSaving())
- value = obj.getValue(i);
-
- s.syncAsByte(value);
-
- if (s.isLoading())
- obj.setValue(i, value);
- }
-}
-#endif
-
#pragma mark -
// By default, sync using syncWithSerializer, which in turn can easily be overloaded.
template<typename T>
struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
- void operator()(Common::Serializer &s, T &obj) const {
+ void operator()(Common::Serializer &s, T &obj, int) const {
syncWithSerializer(s, obj);
}
};
@@ -173,10 +115,31 @@ struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
// Syncer for entries in a segment obj table
template<typename T>
struct SegmentObjTableEntrySyncer : Common::BinaryFunction<Common::Serializer, typename T::Entry &, void> {
- void operator()(Common::Serializer &s, typename T::Entry &entry) const {
+ void operator()(Common::Serializer &s, typename T::Entry &entry, int index) const {
s.syncAsSint32LE(entry.next_free);
- syncWithSerializer(s, entry.data);
+ bool hasData;
+ if (s.getVersion() >= 37) {
+ if (s.isSaving()) {
+ hasData = entry.data != nullptr;
+ }
+ s.syncAsByte(hasData);
+ } else {
+ hasData = (entry.next_free == index);
+ }
+
+ if (hasData) {
+ if (s.isLoading()) {
+ entry.data = new typename T::value_type;
+ }
+ syncWithSerializer(s, *entry.data);
+ } else if (s.isLoading()) {
+ if (s.getVersion() < 37) {
+ typename T::value_type dummy;
+ syncWithSerializer(s, dummy);
+ }
+ entry.data = nullptr;
+ }
}
};
@@ -204,9 +167,8 @@ struct ArraySyncer : Common::BinaryFunction<Common::Serializer, T, void> {
if (s.isLoading())
arr.resize(len);
- typename Common::Array<T>::iterator i;
- for (i = arr.begin(); i != arr.end(); ++i) {
- sync(s, *i);
+ for (uint i = 0; i < len; ++i) {
+ sync(s, arr[i], i);
}
}
};
@@ -270,9 +232,8 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
} else if (type == SEG_TYPE_ARRAY) {
// Set the correct segment for SCI32 arrays
_arraysSegId = i;
- } else if (type == SEG_TYPE_STRING) {
- // Set the correct segment for SCI32 strings
- _stringSegId = i;
+ } else if (s.getVersion() >= 36 && type == SEG_TYPE_BITMAP) {
+ _bitmapSegId = i;
#endif
}
@@ -369,6 +330,28 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
}
s.syncAsUint32LE(obj.playTime);
}
+
+ // Some games require additional metadata to display their restore screens
+ // correctly
+ if (s.getVersion() >= 39) {
+ if (s.isSaving()) {
+ const reg_t *globals = g_sci->getEngineState()->variables[VAR_GLOBAL];
+ if (g_sci->getGameId() == GID_SHIVERS) {
+ obj.lowScore = globals[kGlobalVarScore].toUint16();
+ obj.highScore = globals[kGlobalVarShivers1Score].toUint16();
+ obj.avatarId = 0;
+ } else if (g_sci->getGameId() == GID_MOTHERGOOSEHIRES) {
+ obj.lowScore = obj.highScore = 0;
+ obj.avatarId = readSelectorValue(g_sci->getEngineState()->_segMan, globals[kGlobalVarEgo], SELECTOR(view));
+ } else {
+ obj.lowScore = obj.highScore = obj.avatarId = 0;
+ }
+ }
+
+ s.syncAsUint16LE(obj.lowScore);
+ s.syncAsUint16LE(obj.highScore);
+ s.syncAsByte(obj.avatarId);
+ }
}
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
@@ -398,8 +381,15 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
_segMan->saveLoadWithSerializer(s);
g_sci->_soundCmd->syncPlayList(s);
- // NOTE: This will be GfxPalette32 for SCI32 engine games
- g_sci->_gfxPalette16->saveLoadWithSerializer(s);
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_sci->_gfxPalette32->saveLoadWithSerializer(s);
+ g_sci->_gfxRemap32->saveLoadWithSerializer(s);
+ g_sci->_gfxCursor32->saveLoadWithSerializer(s);
+ } else
+#endif
+ g_sci->_gfxPalette16->saveLoadWithSerializer(s);
}
void Vocabulary::saveLoadWithSerializer(Common::Serializer &s) {
@@ -642,28 +632,44 @@ void SoundCommandParser::syncPlayList(Common::Serializer &s) {
}
void SoundCommandParser::reconstructPlayList() {
- Common::StackLock lock(_music->_mutex);
+ _music->_mutex.lock();
// 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);
+ // Done with main playlist, so release lock
+ _music->_mutex.unlock();
+
for (MusicList::iterator i = songs.begin(); i != songs.end(); ++i) {
- initSoundResource(*i);
+ MusicEntry *entry = *i;
+ initSoundResource(entry);
+
+#ifdef ENABLE_SCI32
+ if (_soundVersion >= SCI_VERSION_2_1_EARLY && entry->isSample) {
+ const reg_t &soundObj = entry->soundObj;
- if ((*i)->status == kSoundPlaying) {
+ if ((int)readSelectorValue(_segMan, soundObj, SELECTOR(loop)) != -1 &&
+ readSelector(_segMan, soundObj, SELECTOR(handle)) != NULL_REG) {
+
+ writeSelector(_segMan, soundObj, SELECTOR(handle), NULL_REG);
+ processPlaySound(soundObj, entry->playBed);
+ }
+ } else
+#endif
+ if (entry->status == kSoundPlaying) {
// 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);
+ writeSelectorValue(_segMan, entry->soundObj, SELECTOR(loop), entry->loop);
+ writeSelectorValue(_segMan, entry->soundObj, SELECTOR(priority), entry->priority);
if (_soundVersion >= SCI_VERSION_1_EARLY)
- writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol), (*i)->volume);
+ writeSelectorValue(_segMan, entry->soundObj, SELECTOR(vol), entry->volume);
- processPlaySound((*i)->soundObj, (*i)->playBed);
+ processPlaySound(entry->soundObj, entry->playBed);
}
}
}
@@ -676,11 +682,60 @@ void ArrayTable::saveLoadWithSerializer(Common::Serializer &ser) {
sync_Table<ArrayTable>(ser, *this);
}
-void StringTable::saveLoadWithSerializer(Common::Serializer &ser) {
- if (ser.getVersion() < 18)
+void SciArray::saveLoadWithSerializer(Common::Serializer &s) {
+ uint16 savedSize;
+
+ if (s.isSaving()) {
+ savedSize = _size;
+ }
+
+ s.syncAsByte(_type);
+ s.syncAsByte(_elementSize);
+ s.syncAsUint16LE(savedSize);
+
+ if (s.isLoading()) {
+ resize(savedSize);
+ }
+
+ switch (_type) {
+ case kArrayTypeInt16:
+ case kArrayTypeID:
+ for (int i = 0; i < savedSize; ++i) {
+ syncWithSerializer(s, ((reg_t *)_data)[i]);
+ }
+ break;
+ case kArrayTypeByte:
+ case kArrayTypeString:
+ s.syncBytes((byte *)_data, savedSize);
+ break;
+ default:
+ error("Attempt to sync invalid SciArray type %d", _type);
+ }
+}
+
+void BitmapTable::saveLoadWithSerializer(Common::Serializer &ser) {
+ if (ser.getVersion() < 36) {
return;
+ }
- sync_Table<StringTable>(ser, *this);
+ sync_Table(ser, *this);
+}
+
+void SciBitmap::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 36) {
+ return;
+ }
+
+ s.syncAsByte(_gc);
+ s.syncAsUint32LE(_dataSize);
+ if (s.isLoading()) {
+ _data = (byte *)malloc(_dataSize);
+ }
+ s.syncBytes(_data, _dataSize);
+
+ if (s.isLoading()) {
+ _buffer = Buffer(getWidth(), getHeight(), getPixels());
+ }
}
#endif
@@ -728,7 +783,7 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
#ifdef ENABLE_SCI32
-void saveLoadPalette32(Common::Serializer &s, Palette *const palette) {
+static void saveLoadPalette32(Common::Serializer &s, Palette *const palette) {
s.syncAsUint32LE(palette->timestamp);
for (int i = 0; i < ARRAYSIZE(palette->colors); ++i) {
s.syncAsByte(palette->colors[i].used);
@@ -738,7 +793,7 @@ void saveLoadPalette32(Common::Serializer &s, Palette *const palette) {
}
}
-void saveLoadOptionalPalette32(Common::Serializer &s, Palette **const palette) {
+static void saveLoadOptionalPalette32(Common::Serializer &s, Palette **const palette) {
bool hasPalette;
if (s.isSaving()) {
hasPalette = (*palette != nullptr);
@@ -759,6 +814,16 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
if (s.isLoading()) {
++_version;
+
+ for (int i = 0; i < kNumCyclers; ++i) {
+ delete _cyclers[i];
+ _cyclers[i] = nullptr;
+ }
+
+ delete _varyTargetPalette;
+ _varyTargetPalette = nullptr;
+ delete _varyStartPalette;
+ _varyStartPalette = nullptr;
}
s.syncAsSint16LE(_varyDirection);
@@ -767,7 +832,7 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint16LE(_varyFromColor);
s.syncAsSint16LE(_varyToColor);
s.syncAsUint16LE(_varyNumTimesPaused);
- s.syncAsByte(_versionUpdated);
+ s.syncAsByte(_needsUpdate);
s.syncAsSint32LE(_varyTime);
s.syncAsUint32LE(_varyLastTick);
@@ -835,6 +900,35 @@ void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
_needsUpdate = true;
}
}
+
+void GfxCursor32::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 38) {
+ return;
+ }
+
+ int32 hideCount;
+ if (s.isSaving()) {
+ hideCount = _hideCount;
+ }
+ s.syncAsSint32LE(hideCount);
+ s.syncAsSint16LE(_restrictedArea.left);
+ s.syncAsSint16LE(_restrictedArea.top);
+ s.syncAsSint16LE(_restrictedArea.right);
+ s.syncAsSint16LE(_restrictedArea.bottom);
+ s.syncAsUint16LE(_cursorInfo.resourceId);
+ s.syncAsUint16LE(_cursorInfo.loopNo);
+ s.syncAsUint16LE(_cursorInfo.celNo);
+
+ if (s.isLoading()) {
+ hide();
+ setView(_cursorInfo.resourceId, _cursorInfo.loopNo, _cursorInfo.celNo);
+ if (!hideCount) {
+ show();
+ } else {
+ _hideCount = hideCount;
+ }
+ }
+}
#endif
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
@@ -959,10 +1053,11 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
meta.gameObjectOffset = g_sci->getGameObject().getOffset();
// Checking here again
- if (s->executionStackBase) {
- warning("Cannot save from below kernel function");
- return false;
- }
+// TODO: This breaks Torin autosave, is there actually any reason for it?
+// if (s->executionStackBase) {
+// warning("Cannot save from below kernel function");
+// return false;
+// }
Common::Serializer ser(0, fh);
sync_SavegameMetadata(ser, meta);