2009-02-17 15:02:16 +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.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2009-02-17 15:02:16 +00:00
|
|
|
* 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.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2009-02-17 15:02:16 +00:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2015-04-26 09:08:46 +02:00
|
|
|
#include "common/savefile.h"
|
2009-02-21 22:50:35 +00:00
|
|
|
#include "common/stream.h"
|
|
|
|
#include "common/system.h"
|
2009-04-27 12:31:27 +00:00
|
|
|
#include "common/func.h"
|
2009-03-15 20:31:29 +00:00
|
|
|
#include "common/serializer.h"
|
2009-10-11 15:51:43 +00:00
|
|
|
#include "graphics/thumbnail.h"
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2009-05-13 16:52:41 +00:00
|
|
|
#include "sci/sci.h"
|
2009-12-04 17:42:32 +00:00
|
|
|
#include "sci/event.h"
|
2009-11-12 09:24:46 +00:00
|
|
|
|
2010-02-13 17:44:58 +00:00
|
|
|
#include "sci/engine/features.h"
|
2010-06-23 15:23:37 +00:00
|
|
|
#include "sci/engine/kernel.h"
|
2009-02-27 02:23:40 +00:00
|
|
|
#include "sci/engine/state.h"
|
2009-10-10 02:16:23 +00:00
|
|
|
#include "sci/engine/message.h"
|
2009-03-12 03:26:21 +00:00
|
|
|
#include "sci/engine/savegame.h"
|
2010-06-15 07:20:53 +00:00
|
|
|
#include "sci/engine/selector.h"
|
2009-11-12 09:24:46 +00:00
|
|
|
#include "sci/engine/vm_types.h"
|
2010-01-29 11:05:06 +00:00
|
|
|
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
|
2010-11-01 16:09:33 +00:00
|
|
|
#include "sci/graphics/helpers.h"
|
2016-02-09 01:28:08 +01:00
|
|
|
#include "sci/graphics/menu.h"
|
2010-07-23 07:36:55 +00:00
|
|
|
#include "sci/graphics/palette.h"
|
2010-01-31 12:35:15 +00:00
|
|
|
#include "sci/graphics/ports.h"
|
2015-04-26 15:50:12 +02:00
|
|
|
#include "sci/graphics/screen.h"
|
2010-12-24 14:47:47 +00:00
|
|
|
#include "sci/parser/vocabulary.h"
|
2010-01-05 01:22:16 +00:00
|
|
|
#include "sci/sound/audio.h"
|
|
|
|
#include "sci/sound/music.h"
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2011-02-14 22:38:12 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
2017-02-05 12:08:56 -06:00
|
|
|
#include "sci/engine/guest_additions.h"
|
2016-07-31 13:41:05 -05:00
|
|
|
#include "sci/graphics/cursor32.h"
|
2011-02-14 22:38:12 -05:00
|
|
|
#include "sci/graphics/frameout.h"
|
2016-06-25 14:19:47 -05:00
|
|
|
#include "sci/graphics/palette32.h"
|
|
|
|
#include "sci/graphics/remap32.h"
|
2017-02-25 18:13:55 -06:00
|
|
|
#include "sci/graphics/video32.h"
|
2011-02-14 22:38:12 -05:00
|
|
|
#endif
|
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
namespace Sci {
|
|
|
|
|
2009-09-06 12:59:56 +00:00
|
|
|
|
|
|
|
#define VER(x) Common::Serializer::Version(x)
|
|
|
|
|
2009-09-21 21:38:43 +00:00
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
#pragma mark -
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2016-03-23 19:16:11 +01:00
|
|
|
// These are serialization functions for various objects.
|
|
|
|
|
|
|
|
void syncWithSerializer(Common::Serializer &s, Common::Serializable &obj) {
|
2009-05-04 15:05:11 +00:00
|
|
|
obj.saveLoadWithSerializer(s);
|
|
|
|
}
|
|
|
|
|
2016-03-23 19:16:11 +01:00
|
|
|
// FIXME: Object could implement Serializable to make use of the function
|
|
|
|
// above.
|
|
|
|
void syncWithSerializer(Common::Serializer &s, Object &obj) {
|
|
|
|
obj.saveLoadWithSerializer(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
|
|
|
|
// Segment and offset are accessed directly here
|
|
|
|
s.syncAsUint16LE(obj._segment);
|
|
|
|
s.syncAsUint16LE(obj._offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void syncWithSerializer(Common::Serializer &s, synonym_t &obj) {
|
|
|
|
s.syncAsUint16LE(obj.replaceant);
|
|
|
|
s.syncAsUint16LE(obj.replacement);
|
|
|
|
}
|
|
|
|
|
|
|
|
void syncWithSerializer(Common::Serializer &s, Class &obj) {
|
|
|
|
s.syncAsSint32LE(obj.script);
|
|
|
|
syncWithSerializer(s, obj.reg);
|
|
|
|
}
|
|
|
|
|
2016-03-23 20:41:48 +01:00
|
|
|
void syncWithSerializer(Common::Serializer &s, List &obj) {
|
2016-03-23 19:16:11 +01:00
|
|
|
syncWithSerializer(s, obj.first);
|
|
|
|
syncWithSerializer(s, obj.last);
|
|
|
|
}
|
|
|
|
|
2016-03-23 20:41:48 +01:00
|
|
|
void syncWithSerializer(Common::Serializer &s, Node &obj) {
|
2016-03-23 19:16:11 +01:00
|
|
|
syncWithSerializer(s, obj.pred);
|
|
|
|
syncWithSerializer(s, obj.succ);
|
|
|
|
syncWithSerializer(s, obj.key);
|
|
|
|
syncWithSerializer(s, obj.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
2009-05-04 15:05:11 +00:00
|
|
|
// By default, sync using syncWithSerializer, which in turn can easily be overloaded.
|
2012-02-10 21:14:48 -06:00
|
|
|
template<typename T>
|
2009-05-04 15:05:11 +00:00
|
|
|
struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
|
2016-08-13 09:35:39 -05:00
|
|
|
void operator()(Common::Serializer &s, T &obj, int) const {
|
2009-05-04 15:05:11 +00:00
|
|
|
syncWithSerializer(s, obj);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-03-23 20:41:48 +01:00
|
|
|
// Syncer for entries in a segment obj table
|
|
|
|
template<typename T>
|
|
|
|
struct SegmentObjTableEntrySyncer : Common::BinaryFunction<Common::Serializer, typename T::Entry &, void> {
|
2016-08-13 09:35:39 -05:00
|
|
|
void operator()(Common::Serializer &s, typename T::Entry &entry, int index) const {
|
2016-03-23 20:41:48 +01:00
|
|
|
s.syncAsSint32LE(entry.next_free);
|
|
|
|
|
2017-01-16 07:22:32 +00:00
|
|
|
bool hasData = false;
|
2016-08-13 09:35:39 -05:00
|
|
|
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;
|
|
|
|
}
|
2016-03-23 20:41:48 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-03-24 11:31:16 +00:00
|
|
|
/**
|
|
|
|
* Sync a Common::Array using a Common::Serializer.
|
|
|
|
* When saving, this writes the length of the array, then syncs (writes) all entries.
|
|
|
|
* When loading, it loads the length of the array, then resizes it accordingly, before
|
|
|
|
* syncing all entries.
|
|
|
|
*
|
2009-05-04 15:05:11 +00:00
|
|
|
* Note: This shouldn't be in common/array.h nor in common/serializer.h, after
|
|
|
|
* all, not all code using arrays wants to use the serializer, and vice versa.
|
|
|
|
* But we could put this into a separate header file in common/ at some point.
|
|
|
|
* Something like common/serializer-extras.h or so.
|
2009-03-24 11:31:16 +00:00
|
|
|
*
|
|
|
|
* TODO: Add something like this for lists, queues....
|
|
|
|
*/
|
2012-02-10 21:14:48 -06:00
|
|
|
template<typename T, class Syncer = DefaultSyncer<T> >
|
2009-05-04 15:05:11 +00:00
|
|
|
struct ArraySyncer : Common::BinaryFunction<Common::Serializer, T, void> {
|
|
|
|
void operator()(Common::Serializer &s, Common::Array<T> &arr) const {
|
|
|
|
uint len = arr.size();
|
|
|
|
s.syncAsUint32LE(len);
|
|
|
|
Syncer sync;
|
|
|
|
|
|
|
|
// Resize the array if loading.
|
|
|
|
if (s.isLoading())
|
|
|
|
arr.resize(len);
|
|
|
|
|
2016-08-13 16:09:49 -05:00
|
|
|
for (uint i = 0; i < len; ++i) {
|
2016-08-13 09:35:39 -05:00
|
|
|
sync(s, arr[i], i);
|
2009-05-04 15:05:11 +00:00
|
|
|
}
|
2009-03-24 11:31:16 +00:00
|
|
|
}
|
2009-05-04 15:05:11 +00:00
|
|
|
};
|
2009-02-28 11:07:36 +00:00
|
|
|
|
2009-05-04 15:05:11 +00:00
|
|
|
// Convenience wrapper
|
2016-04-06 23:22:44 +02:00
|
|
|
template<typename T>
|
|
|
|
void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
|
|
|
|
ArraySyncer<T> sync;
|
|
|
|
sync(s, arr);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T, class Syncer>
|
2009-05-04 15:05:11 +00:00
|
|
|
void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
|
2016-03-23 20:41:48 +01:00
|
|
|
ArraySyncer<T, Syncer> sync;
|
2009-05-04 15:05:11 +00:00
|
|
|
sync(s, arr);
|
2009-04-27 12:31:27 +00:00
|
|
|
}
|
|
|
|
|
2009-05-03 09:25:15 +00:00
|
|
|
void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
|
2011-02-11 17:05:27 +00:00
|
|
|
if (s.isLoading()) {
|
2010-07-01 16:06:04 +00:00
|
|
|
resetSegMan();
|
|
|
|
|
2009-09-06 13:00:30 +00:00
|
|
|
// Reset _scriptSegMap, to be restored below
|
|
|
|
_scriptSegMap.clear();
|
|
|
|
}
|
|
|
|
|
2011-02-11 17:05:27 +00:00
|
|
|
s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide
|
2009-05-03 09:25:15 +00:00
|
|
|
|
2009-05-11 13:32:00 +00:00
|
|
|
uint sync_heap_size = _heap.size();
|
|
|
|
s.syncAsUint32LE(sync_heap_size);
|
2009-05-03 22:46:11 +00:00
|
|
|
_heap.resize(sync_heap_size);
|
2009-09-06 13:00:30 +00:00
|
|
|
for (uint i = 0; i < sync_heap_size; ++i) {
|
2009-09-17 00:45:12 +00:00
|
|
|
SegmentObj *&mobj = _heap[i];
|
2009-09-06 13:00:30 +00:00
|
|
|
|
2009-09-17 13:22:46 +00:00
|
|
|
// Sync the segment type
|
2009-09-17 00:45:12 +00:00
|
|
|
SegmentType type = (s.isSaving() && mobj) ? mobj->getType() : SEG_TYPE_INVALID;
|
2009-09-06 13:00:30 +00:00
|
|
|
s.syncAsUint32LE(type);
|
|
|
|
|
2011-02-15 01:30:33 +02:00
|
|
|
if (type == SEG_TYPE_HUNK) {
|
|
|
|
// Don't save or load HunkTable segments
|
2009-09-06 13:00:30 +00:00
|
|
|
continue;
|
2011-02-15 01:30:33 +02:00
|
|
|
} else if (type == SEG_TYPE_INVALID) {
|
|
|
|
// If we were saving and mobj == 0, or if we are loading and this is an
|
|
|
|
// entry marked as empty -> skip to next
|
2009-10-13 18:54:20 +00:00
|
|
|
continue;
|
2011-02-15 01:30:33 +02:00
|
|
|
} else if (type == 5) {
|
|
|
|
// Don't save or load the obsolete system string segments
|
2010-12-07 00:47:05 +00:00
|
|
|
if (s.isSaving()) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
// Old saved game. Skip the data.
|
|
|
|
Common::String tmp;
|
2010-12-07 01:12:48 +00:00
|
|
|
for (int j = 0; j < 4; j++) {
|
2010-12-07 00:47:05 +00:00
|
|
|
s.syncString(tmp); // OBSOLETE: name
|
|
|
|
s.skip(4); // OBSOLETE: maxSize
|
|
|
|
s.syncString(tmp); // OBSOLETE: value
|
|
|
|
}
|
|
|
|
_heap[i] = NULL; // set as freed
|
|
|
|
continue;
|
|
|
|
}
|
2011-02-15 01:30:33 +02:00
|
|
|
#ifdef ENABLE_SCI32
|
2011-02-14 22:38:12 -05:00
|
|
|
} else if (type == SEG_TYPE_ARRAY) {
|
2011-02-15 01:30:33 +02:00
|
|
|
// Set the correct segment for SCI32 arrays
|
|
|
|
_arraysSegId = i;
|
2016-07-29 15:48:14 -05:00
|
|
|
} else if (s.getVersion() >= 36 && type == SEG_TYPE_BITMAP) {
|
|
|
|
_bitmapSegId = i;
|
2011-02-15 01:30:33 +02:00
|
|
|
#endif
|
2011-02-14 22:38:12 -05:00
|
|
|
}
|
2010-12-07 00:47:05 +00:00
|
|
|
|
2010-07-07 20:12:41 +00:00
|
|
|
if (s.isLoading())
|
2009-09-17 00:45:12 +00:00
|
|
|
mobj = SegmentObj::createSegmentObj(type);
|
2010-07-07 20:12:41 +00:00
|
|
|
|
2009-09-06 13:00:30 +00:00
|
|
|
assert(mobj);
|
|
|
|
|
2012-06-15 12:53:17 +03:00
|
|
|
// Let the object sync custom data. Scripts are loaded at this point.
|
2009-09-06 13:00:30 +00:00
|
|
|
mobj->saveLoadWithSerializer(s);
|
|
|
|
|
2011-02-14 16:01:04 -05:00
|
|
|
if (type == SEG_TYPE_SCRIPT) {
|
2010-11-08 00:18:34 +00:00
|
|
|
Script *scr = (Script *)mobj;
|
|
|
|
|
2011-02-11 17:05:27 +00:00
|
|
|
// If we are loading a script, perform some extra steps
|
|
|
|
if (s.isLoading()) {
|
|
|
|
// Hook the script up in the script->segment map
|
|
|
|
_scriptSegMap[scr->getScriptNumber()] = i;
|
|
|
|
|
2011-11-05 10:07:03 +02:00
|
|
|
ObjMap objects = scr->getObjectMap();
|
2017-04-30 13:44:34 -05:00
|
|
|
for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it) {
|
|
|
|
it->_value.syncBaseObject(scr->getSpan(it->_value.getPos().getOffset()));
|
|
|
|
}
|
2011-02-11 17:05:27 +00:00
|
|
|
}
|
2010-11-08 00:18:34 +00:00
|
|
|
|
2011-02-11 17:05:27 +00:00
|
|
|
// Sync the script's string heap
|
2011-02-14 16:01:04 -05:00
|
|
|
if (s.getVersion() >= 28)
|
|
|
|
scr->syncStringHeap(s);
|
2010-11-08 00:18:34 +00:00
|
|
|
}
|
2009-09-06 13:00:30 +00:00
|
|
|
}
|
2009-05-03 09:25:15 +00:00
|
|
|
|
2010-07-01 16:04:29 +00:00
|
|
|
s.syncAsSint32LE(_clonesSegId);
|
|
|
|
s.syncAsSint32LE(_listsSegId);
|
|
|
|
s.syncAsSint32LE(_nodesSegId);
|
2010-07-01 16:05:10 +00:00
|
|
|
|
|
|
|
syncArray<Class>(s, _classTable);
|
2010-11-08 00:18:34 +00:00
|
|
|
|
2017-02-08 18:52:20 -06:00
|
|
|
if (s.isLoading()) {
|
|
|
|
// 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);
|
2013-10-27 01:17:28 +02:00
|
|
|
|
2017-02-08 18:52:20 -06:00
|
|
|
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 (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());
|
|
|
|
}
|
2013-10-27 01:17:28 +02:00
|
|
|
}
|
2010-11-08 00:18:34 +00:00
|
|
|
}
|
2017-02-05 12:08:56 -06:00
|
|
|
|
2017-02-08 21:45:13 -06:00
|
|
|
#ifdef ENABLE_SCI32
|
2017-02-08 18:52:20 -06:00
|
|
|
if (pass == passes) {
|
|
|
|
g_sci->_guestAdditions->segManSaveLoadScriptHook(*scr);
|
|
|
|
}
|
2017-02-08 21:45:13 -06:00
|
|
|
#endif
|
2017-02-05 12:08:56 -06:00
|
|
|
}
|
2010-11-08 00:18:34 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-28 11:12:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj) {
|
2010-10-31 01:45:24 +00:00
|
|
|
s.syncString(obj.name);
|
2009-09-06 12:59:56 +00:00
|
|
|
s.syncVersion(CURRENT_SAVEGAME_VERSION);
|
2010-10-31 01:45:24 +00:00
|
|
|
obj.version = s.getVersion();
|
|
|
|
s.syncString(obj.gameVersion);
|
|
|
|
s.syncAsSint32LE(obj.saveDate);
|
|
|
|
s.syncAsSint32LE(obj.saveTime);
|
2010-06-15 08:25:51 +00:00
|
|
|
if (s.getVersion() < 22) {
|
2010-10-31 01:45:24 +00:00
|
|
|
obj.gameObjectOffset = 0;
|
|
|
|
obj.script0Size = 0;
|
2010-06-15 08:25:51 +00:00
|
|
|
} else {
|
2010-10-31 01:45:24 +00:00
|
|
|
s.syncAsUint16LE(obj.gameObjectOffset);
|
|
|
|
s.syncAsUint16LE(obj.script0Size);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Playtime
|
|
|
|
obj.playTime = 0;
|
|
|
|
if (s.isLoading()) {
|
|
|
|
if (s.getVersion() >= 26)
|
|
|
|
s.syncAsUint32LE(obj.playTime);
|
|
|
|
} else {
|
2016-01-14 10:54:27 -06:00
|
|
|
if (s.getVersion() >= 34) {
|
|
|
|
obj.playTime = g_sci->getTickCount();
|
|
|
|
} else {
|
|
|
|
obj.playTime = g_engine->getTotalPlayTime() / 1000;
|
|
|
|
}
|
2010-10-31 01:45:24 +00:00
|
|
|
s.syncAsUint32LE(obj.playTime);
|
2010-06-15 08:25:51 +00:00
|
|
|
}
|
2016-09-15 21:08:44 -05:00
|
|
|
|
2016-09-21 16:16:21 -05:00
|
|
|
// Some games require additional metadata to display their restore screens
|
2016-09-21 12:10:06 -05:00
|
|
|
// correctly
|
2016-09-30 19:05:14 -05:00
|
|
|
if (s.getVersion() >= 39) {
|
2016-09-15 21:08:44 -05:00
|
|
|
if (s.isSaving()) {
|
2016-09-21 12:10:06 -05:00
|
|
|
const reg_t *globals = g_sci->getEngineState()->variables[VAR_GLOBAL];
|
2016-09-15 21:08:44 -05:00
|
|
|
if (g_sci->getGameId() == GID_SHIVERS) {
|
2016-09-26 19:28:51 -05:00
|
|
|
obj.lowScore = globals[kGlobalVarScore].toUint16();
|
|
|
|
obj.highScore = globals[kGlobalVarShivers1Score].toUint16();
|
2016-09-21 16:16:21 -05:00
|
|
|
obj.avatarId = 0;
|
2016-09-21 12:10:06 -05:00
|
|
|
} else if (g_sci->getGameId() == GID_MOTHERGOOSEHIRES) {
|
2016-09-21 16:16:21 -05:00
|
|
|
obj.lowScore = obj.highScore = 0;
|
2016-09-26 19:28:51 -05:00
|
|
|
obj.avatarId = readSelectorValue(g_sci->getEngineState()->_segMan, globals[kGlobalVarEgo], SELECTOR(view));
|
2016-09-21 16:16:21 -05:00
|
|
|
} else {
|
|
|
|
obj.lowScore = obj.highScore = obj.avatarId = 0;
|
2016-09-15 21:08:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-21 16:16:21 -05:00
|
|
|
s.syncAsUint16LE(obj.lowScore);
|
|
|
|
s.syncAsUint16LE(obj.highScore);
|
|
|
|
s.syncAsByte(obj.avatarId);
|
2016-09-15 21:08:44 -05:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
|
2009-09-25 13:02:11 +00:00
|
|
|
Common::String tmp;
|
2010-10-31 01:45:24 +00:00
|
|
|
s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be gameVersion
|
2009-02-15 22:28:12 +00:00
|
|
|
|
2010-07-23 08:22:13 +00:00
|
|
|
if (getSciVersion() <= SCI_VERSION_1_1) {
|
2010-06-10 10:27:13 +00:00
|
|
|
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
|
|
|
|
// as the picPort has been changed when loading during the intro
|
2009-12-08 20:54:18 +00:00
|
|
|
int16 picPortTop, picPortLeft;
|
|
|
|
Common::Rect picPortRect;
|
2010-01-30 02:03:59 +00:00
|
|
|
|
2010-06-10 10:27:13 +00:00
|
|
|
if (s.isSaving())
|
2010-02-13 17:43:31 +00:00
|
|
|
picPortRect = g_sci->_gfxPorts->kernelGetPicWindow(picPortTop, picPortLeft);
|
2009-12-08 20:54:18 +00:00
|
|
|
|
2009-12-09 07:28:04 +00:00
|
|
|
s.syncAsSint16LE(picPortRect.top);
|
|
|
|
s.syncAsSint16LE(picPortRect.left);
|
|
|
|
s.syncAsSint16LE(picPortRect.bottom);
|
|
|
|
s.syncAsSint16LE(picPortRect.right);
|
2009-12-08 20:54:18 +00:00
|
|
|
s.syncAsSint16LE(picPortTop);
|
|
|
|
s.syncAsSint16LE(picPortLeft);
|
2010-06-10 10:15:32 +00:00
|
|
|
|
2010-06-10 10:27:13 +00:00
|
|
|
if (s.isLoading())
|
2010-06-10 10:15:32 +00:00
|
|
|
g_sci->_gfxPorts->kernelSetPicWindow(picPortRect, picPortTop, picPortLeft, false);
|
2009-12-08 20:54:18 +00:00
|
|
|
}
|
|
|
|
|
2017-02-25 18:13:55 -06:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
|
|
g_sci->_video32->beforeSaveLoadWithSerializer(s);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-06-08 18:23:38 +00:00
|
|
|
_segMan->saveLoadWithSerializer(s);
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2010-06-29 09:00:08 +00:00
|
|
|
g_sci->_soundCmd->syncPlayList(s);
|
2016-07-09 12:41:12 -05:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (getSciVersion() >= SCI_VERSION_2) {
|
|
|
|
g_sci->_gfxPalette32->saveLoadWithSerializer(s);
|
2016-08-06 10:02:02 -05:00
|
|
|
g_sci->_gfxRemap32->saveLoadWithSerializer(s);
|
2016-08-28 13:04:02 -05:00
|
|
|
g_sci->_gfxCursor32->saveLoadWithSerializer(s);
|
2017-02-25 18:13:55 -06:00
|
|
|
g_sci->_video32->saveLoadWithSerializer(s);
|
2016-07-09 12:41:12 -05:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
g_sci->_gfxPalette16->saveLoadWithSerializer(s);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2010-12-24 14:47:47 +00:00
|
|
|
void Vocabulary::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
syncArray<synonym_t>(s, _synonyms);
|
|
|
|
}
|
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
s.syncAsSint32LE(script_id);
|
|
|
|
syncArray<reg_t>(s, _locals);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-10-10 15:58:51 +00:00
|
|
|
void Object::saveLoadWithSerializer(Common::Serializer &s) {
|
2017-02-17 11:50:33 -06:00
|
|
|
s.syncAsSint32LE(_isFreed);
|
2010-08-04 08:26:09 +00:00
|
|
|
syncWithSerializer(s, _pos);
|
2009-10-10 15:58:51 +00:00
|
|
|
s.syncAsSint32LE(_methodCount); // that's actually a uint16
|
|
|
|
|
|
|
|
syncArray<reg_t>(s, _variables);
|
2017-04-17 11:25:31 -05:00
|
|
|
|
|
|
|
#ifdef ENABLE_SCI32
|
2017-02-09 19:11:58 -06:00
|
|
|
if (s.getVersion() >= 42 && getSciVersion() == SCI_VERSION_3) {
|
2017-04-17 11:25:31 -05:00
|
|
|
// Obsolete mustSetViewVisible array
|
|
|
|
if (s.getVersion() == 42 && s.isLoading()) {
|
|
|
|
uint32 len;
|
|
|
|
s.syncAsUint32LE(len);
|
|
|
|
s.skip(len);
|
|
|
|
}
|
2017-02-09 19:11:58 -06:00
|
|
|
syncWithSerializer(s, _superClassPosSci3);
|
|
|
|
syncWithSerializer(s, _speciesSelectorSci3);
|
|
|
|
syncWithSerializer(s, _infoSelectorSci3);
|
|
|
|
}
|
2017-04-17 11:25:31 -05:00
|
|
|
#endif
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2010-01-30 02:03:59 +00:00
|
|
|
|
2012-02-10 21:14:48 -06:00
|
|
|
template<typename T>
|
2009-05-11 13:32:00 +00:00
|
|
|
void sync_Table(Common::Serializer &s, T &obj) {
|
2009-03-15 20:31:29 +00:00
|
|
|
s.syncAsSint32LE(obj.first_free);
|
|
|
|
s.syncAsSint32LE(obj.entries_used);
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2016-03-23 20:41:48 +01:00
|
|
|
syncArray<typename T::Entry, SegmentObjTableEntrySyncer<T> >(s, obj._table);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
void CloneTable::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
sync_Table<CloneTable>(s, *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NodeTable::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
sync_Table<NodeTable>(s, *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ListTable::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
sync_Table<ListTable>(s, *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void HunkTable::saveLoadWithSerializer(Common::Serializer &s) {
|
2009-10-13 18:54:20 +00:00
|
|
|
// Do nothing, hunk tables are not actually saved nor loaded.
|
2009-05-19 00:34:10 +00:00
|
|
|
}
|
|
|
|
|
2010-11-08 00:18:34 +00:00
|
|
|
void Script::syncStringHeap(Common::Serializer &s) {
|
|
|
|
if (getSciVersion() < SCI_VERSION_1_1) {
|
2010-11-09 11:57:54 +00:00
|
|
|
// Sync all of the SCI_OBJ_STRINGS blocks
|
2017-04-30 12:38:50 -05:00
|
|
|
SciSpan<byte> buf = *_buf;
|
2010-11-08 00:18:34 +00:00
|
|
|
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
|
|
|
|
|
|
|
|
if (oldScriptHeader)
|
|
|
|
buf += 2;
|
|
|
|
|
2016-12-31 20:39:57 -06:00
|
|
|
for (;;) {
|
|
|
|
int blockType = buf.getUint16LEAt(0);
|
2010-11-08 11:02:43 +00:00
|
|
|
int blockSize;
|
2010-11-08 00:18:34 +00:00
|
|
|
if (blockType == 0)
|
|
|
|
break;
|
2010-11-08 11:02:43 +00:00
|
|
|
|
2016-12-31 20:39:57 -06:00
|
|
|
blockSize = buf.getUint16LEAt(2);
|
2010-11-08 11:02:43 +00:00
|
|
|
assert(blockSize > 0);
|
|
|
|
|
2010-11-08 00:18:34 +00:00
|
|
|
if (blockType == SCI_OBJ_STRINGS)
|
2016-12-31 20:39:57 -06:00
|
|
|
s.syncBytes(buf.getUnsafeDataAt(0, blockSize), blockSize);
|
2010-11-08 00:18:34 +00:00
|
|
|
|
|
|
|
buf += blockSize;
|
2016-12-31 20:39:57 -06:00
|
|
|
}
|
2010-11-08 00:18:34 +00:00
|
|
|
|
2015-12-29 01:44:11 +01:00
|
|
|
} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE){
|
2010-11-08 00:18:34 +00:00
|
|
|
// Strings in SCI1.1 come after the object instances
|
2017-04-30 12:38:50 -05:00
|
|
|
SciSpan<byte> buf = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
|
2010-11-08 00:18:34 +00:00
|
|
|
|
|
|
|
// Skip all of the objects
|
2016-12-31 20:39:57 -06:00
|
|
|
while (buf.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER)
|
|
|
|
buf += buf.getUint16SEAt(2) * 2;
|
2010-11-08 00:18:34 +00:00
|
|
|
|
|
|
|
// Now, sync everything till the end of the buffer
|
2016-12-31 20:39:57 -06:00
|
|
|
const int length = _heap.size() - (buf - _heap);
|
|
|
|
s.syncBytes(buf.getUnsafeDataAt(0, length), length);
|
2012-06-14 12:12:30 +03:00
|
|
|
} else if (getSciVersion() == SCI_VERSION_3) {
|
2017-04-22 20:44:51 -05:00
|
|
|
const int stringOffset = _buf->getInt32SEAt(4);
|
|
|
|
const int length = _buf->getInt32SEAt(8) - stringOffset;
|
2017-04-30 12:38:50 -05:00
|
|
|
s.syncBytes(_buf->getUnsafeDataAt(stringOffset, length), length);
|
2010-11-08 00:18:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
void Script::saveLoadWithSerializer(Common::Serializer &s) {
|
2009-09-16 23:32:27 +00:00
|
|
|
s.syncAsSint32LE(_nr);
|
2010-06-22 08:57:25 +00:00
|
|
|
|
|
|
|
if (s.isLoading())
|
2013-12-04 20:42:16 +01:00
|
|
|
load(_nr, g_sci->getResMan(), g_sci->getScriptPatcher());
|
2010-07-23 08:22:13 +00:00
|
|
|
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
|
2009-05-03 09:25:15 +00:00
|
|
|
|
2010-07-23 08:22:13 +00:00
|
|
|
s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numExports
|
|
|
|
s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numSynonyms
|
2009-09-16 23:32:27 +00:00
|
|
|
s.syncAsSint32LE(_lockers);
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2009-09-21 21:38:43 +00:00
|
|
|
// Sync _objects. This is a hashmap, and we use the following on disk format:
|
|
|
|
// First we store the number of items in the hashmap, then we store each
|
|
|
|
// object (which is an 'Object' instance). For loading, we take advantage
|
|
|
|
// of the fact that the key of each Object obj is just obj._pos.offset !
|
|
|
|
// By "chance" this format is identical to the format used to sync Common::Array<>,
|
|
|
|
// hence we can still old savegames with identical code :).
|
|
|
|
|
|
|
|
uint numObjs = _objects.size();
|
|
|
|
s.syncAsUint32LE(numObjs);
|
|
|
|
|
|
|
|
if (s.isLoading()) {
|
|
|
|
_objects.clear();
|
|
|
|
Object tmp;
|
|
|
|
for (uint i = 0; i < numObjs; ++i) {
|
2010-08-04 08:26:09 +00:00
|
|
|
syncWithSerializer(s, tmp);
|
2012-06-18 05:21:59 +03:00
|
|
|
_objects[tmp.getPos().getOffset()] = tmp;
|
2009-09-21 21:38:43 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ObjMap::iterator it;
|
|
|
|
const ObjMap::iterator end = _objects.end();
|
|
|
|
for (it = _objects.begin(); it != end; ++it) {
|
2010-08-04 08:26:09 +00:00
|
|
|
syncWithSerializer(s, it->_value);
|
2009-09-21 21:38:43 +00:00
|
|
|
}
|
|
|
|
}
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2010-07-23 08:22:13 +00:00
|
|
|
s.skip(4, VER(14), VER(20)); // OBSOLETE: Used to be _localsOffset
|
2009-09-16 23:32:27 +00:00
|
|
|
s.syncAsSint32LE(_localsSegment);
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
s.syncAsSint32LE(_markedAsDeleted);
|
2009-02-24 02:59:50 +00:00
|
|
|
}
|
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
void DynMem::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
s.syncAsSint32LE(_size);
|
2009-09-14 22:34:53 +00:00
|
|
|
s.syncString(_description);
|
2009-05-19 00:34:10 +00:00
|
|
|
if (!_buf && _size) {
|
|
|
|
_buf = (byte *)calloc(_size, 1);
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-05-19 00:34:10 +00:00
|
|
|
if (_size)
|
|
|
|
s.syncBytes(_buf, _size);
|
2009-03-15 20:31:29 +00:00
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-05-19 00:34:10 +00:00
|
|
|
void DataStack::saveLoadWithSerializer(Common::Serializer &s) {
|
2009-09-22 00:35:46 +00:00
|
|
|
s.syncAsUint32LE(_capacity);
|
2009-05-11 13:32:00 +00:00
|
|
|
if (s.isLoading()) {
|
2010-06-06 23:00:33 +00:00
|
|
|
free(_entries);
|
2009-09-22 00:35:46 +00:00
|
|
|
_entries = (reg_t *)calloc(_capacity, sizeof(reg_t));
|
2009-05-11 13:32:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
#pragma mark -
|
|
|
|
|
2009-12-27 11:43:34 +00:00
|
|
|
void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
|
2009-12-26 00:22:21 +00:00
|
|
|
// Sync song lib data. When loading, the actual song lib will be initialized
|
|
|
|
// afterwards in gamestate_restore()
|
2009-12-25 13:18:48 +00:00
|
|
|
int songcount = 0;
|
2009-12-27 14:11:26 +00:00
|
|
|
byte masterVolume = soundGetMasterVolume();
|
2010-01-22 12:26:12 +00:00
|
|
|
byte reverb = _pMidiDrv->getReverb();
|
2009-12-27 14:11:26 +00:00
|
|
|
|
|
|
|
if (s.isSaving()) {
|
|
|
|
s.syncAsByte(_soundOn);
|
|
|
|
s.syncAsByte(masterVolume);
|
2010-01-22 12:26:12 +00:00
|
|
|
s.syncAsByte(reverb, VER(17));
|
2009-12-27 14:11:26 +00:00
|
|
|
} else if (s.isLoading()) {
|
2010-01-01 14:22:07 +00:00
|
|
|
if (s.getVersion() >= 15) {
|
2009-12-27 14:11:26 +00:00
|
|
|
s.syncAsByte(_soundOn);
|
|
|
|
s.syncAsByte(masterVolume);
|
2010-01-22 12:26:12 +00:00
|
|
|
reverb = 0;
|
|
|
|
s.syncAsByte(reverb, VER(17));
|
2009-12-27 14:11:26 +00:00
|
|
|
} else {
|
|
|
|
_soundOn = true;
|
|
|
|
masterVolume = 15;
|
2010-01-22 12:26:12 +00:00
|
|
|
reverb = 0;
|
2009-12-27 14:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
soundSetSoundOn(_soundOn);
|
|
|
|
soundSetMasterVolume(masterVolume);
|
2010-11-24 16:01:30 +00:00
|
|
|
setGlobalReverb(reverb);
|
2009-12-27 14:11:26 +00:00
|
|
|
}
|
|
|
|
|
2009-12-25 13:18:48 +00:00
|
|
|
if (s.isSaving())
|
2009-12-27 11:43:34 +00:00
|
|
|
songcount = _playList.size();
|
2009-12-25 13:18:48 +00:00
|
|
|
s.syncAsUint32LE(songcount);
|
2009-12-26 00:22:21 +00:00
|
|
|
|
2011-10-21 22:24:59 +02:00
|
|
|
if (s.isLoading())
|
2009-12-27 23:46:11 +00:00
|
|
|
clearPlayList();
|
2009-12-25 13:18:48 +00:00
|
|
|
|
2011-10-21 22:24:59 +02:00
|
|
|
Common::StackLock lock(_mutex);
|
|
|
|
|
|
|
|
if (s.isLoading()) {
|
2009-12-25 13:18:48 +00:00
|
|
|
for (int i = 0; i < songcount; i++) {
|
|
|
|
MusicEntry *curSong = new MusicEntry();
|
2009-12-28 21:03:13 +00:00
|
|
|
curSong->saveLoadWithSerializer(s);
|
2009-12-27 11:43:34 +00:00
|
|
|
_playList.push_back(curSong);
|
2009-12-25 13:18:48 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int i = 0; i < songcount; i++) {
|
2009-12-28 21:03:13 +00:00
|
|
|
_playList[i]->saveLoadWithSerializer(s);
|
2009-12-25 13:18:48 +00:00
|
|
|
}
|
|
|
|
}
|
2009-12-27 12:12:14 +00:00
|
|
|
}
|
2009-03-15 20:31:29 +00:00
|
|
|
|
2010-08-04 08:25:39 +00:00
|
|
|
void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
|
2010-08-04 08:26:09 +00:00
|
|
|
syncWithSerializer(s, soundObj);
|
2010-08-04 08:25:39 +00:00
|
|
|
s.syncAsSint16LE(resourceId);
|
|
|
|
s.syncAsSint16LE(dataInc);
|
|
|
|
s.syncAsSint16LE(ticker);
|
|
|
|
s.syncAsSint16LE(signal, VER(17));
|
2013-09-21 14:34:42 +02:00
|
|
|
if (s.getVersion() >= 31) // FE sound/music.h -> priority
|
2013-09-21 14:27:16 +02:00
|
|
|
s.syncAsSint16LE(priority);
|
|
|
|
else
|
|
|
|
s.syncAsByte(priority);
|
2010-08-04 08:25:39 +00:00
|
|
|
s.syncAsSint16LE(loop, VER(17));
|
|
|
|
s.syncAsByte(volume);
|
|
|
|
s.syncAsByte(hold, VER(17));
|
|
|
|
s.syncAsByte(fadeTo);
|
|
|
|
s.syncAsSint16LE(fadeStep);
|
|
|
|
s.syncAsSint32LE(fadeTicker);
|
|
|
|
s.syncAsSint32LE(fadeTickerStep);
|
|
|
|
s.syncAsByte(status);
|
2014-10-07 19:48:18 +02:00
|
|
|
if (s.getVersion() >= 32)
|
|
|
|
s.syncAsByte(playBed);
|
|
|
|
else if (s.isLoading())
|
|
|
|
playBed = false;
|
2015-02-14 15:20:23 +01:00
|
|
|
if (s.getVersion() >= 33)
|
|
|
|
s.syncAsByte(overridePriority);
|
|
|
|
else if (s.isLoading())
|
|
|
|
overridePriority = false;
|
2010-08-04 08:25:39 +00:00
|
|
|
|
|
|
|
// pMidiParser and pStreamAud will be initialized when the
|
|
|
|
// sound list is reconstructed in gamestate_restore()
|
|
|
|
if (s.isLoading()) {
|
|
|
|
soundRes = 0;
|
|
|
|
pMidiParser = 0;
|
|
|
|
pStreamAud = 0;
|
2010-11-25 16:09:45 +00:00
|
|
|
reverb = -1; // invalid reverb, will be initialized in processInitSound()
|
2010-08-04 08:25:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-15 07:20:53 +00:00
|
|
|
void SoundCommandParser::syncPlayList(Common::Serializer &s) {
|
|
|
|
_music->saveLoadWithSerializer(s);
|
|
|
|
}
|
|
|
|
|
2010-10-31 20:57:50 +00:00
|
|
|
void SoundCommandParser::reconstructPlayList() {
|
2016-10-08 22:32:13 +02:00
|
|
|
_music->_mutex.lock();
|
2010-06-15 07:20:53 +00:00
|
|
|
|
2014-10-07 21:24:49 +02:00
|
|
|
// 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);
|
|
|
|
|
2016-10-08 22:32:13 +02:00
|
|
|
// Done with main playlist, so release lock
|
|
|
|
_music->_mutex.unlock();
|
|
|
|
|
2014-10-07 21:24:49 +02:00
|
|
|
for (MusicList::iterator i = songs.begin(); i != songs.end(); ++i) {
|
2016-11-02 14:30:23 -05:00
|
|
|
MusicEntry *entry = *i;
|
|
|
|
initSoundResource(entry);
|
2011-09-26 19:57:50 +03:00
|
|
|
|
2016-11-02 14:30:23 -05:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
if (_soundVersion >= SCI_VERSION_2_1_EARLY && entry->isSample) {
|
|
|
|
const reg_t &soundObj = entry->soundObj;
|
|
|
|
|
|
|
|
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) {
|
2014-12-13 14:08:34 +01:00
|
|
|
// 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)
|
2016-11-02 14:30:23 -05:00
|
|
|
writeSelectorValue(_segMan, entry->soundObj, SELECTOR(loop), entry->loop);
|
|
|
|
writeSelectorValue(_segMan, entry->soundObj, SELECTOR(priority), entry->priority);
|
2010-11-11 10:09:57 +00:00
|
|
|
if (_soundVersion >= SCI_VERSION_1_EARLY)
|
2016-11-02 14:30:23 -05:00
|
|
|
writeSelectorValue(_segMan, entry->soundObj, SELECTOR(vol), entry->volume);
|
2010-11-11 10:09:57 +00:00
|
|
|
|
2016-11-02 14:30:23 -05:00
|
|
|
processPlaySound(entry->soundObj, entry->playBed);
|
2010-06-15 07:20:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-30 02:03:59 +00:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
void ArrayTable::saveLoadWithSerializer(Common::Serializer &ser) {
|
|
|
|
if (ser.getVersion() < 18)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sync_Table<ArrayTable>(ser, *this);
|
|
|
|
}
|
|
|
|
|
2016-09-04 16:30:47 -05:00
|
|
|
void SciArray::saveLoadWithSerializer(Common::Serializer &s) {
|
2016-09-30 13:09:12 -05:00
|
|
|
uint16 savedSize;
|
2016-09-04 16:30:47 -05:00
|
|
|
|
|
|
|
if (s.isSaving()) {
|
2016-09-30 13:09:12 -05:00
|
|
|
savedSize = _size;
|
2016-09-04 16:30:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
s.syncAsByte(_type);
|
|
|
|
s.syncAsByte(_elementSize);
|
2016-09-30 13:09:12 -05:00
|
|
|
s.syncAsUint16LE(savedSize);
|
2010-01-30 02:03:59 +00:00
|
|
|
|
2016-09-04 16:30:47 -05:00
|
|
|
if (s.isLoading()) {
|
2016-09-30 13:09:12 -05:00
|
|
|
resize(savedSize);
|
2016-09-04 16:30:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (_type) {
|
|
|
|
case kArrayTypeInt16:
|
|
|
|
case kArrayTypeID:
|
2016-09-30 13:09:12 -05:00
|
|
|
for (int i = 0; i < savedSize; ++i) {
|
2016-09-04 16:30:47 -05:00
|
|
|
syncWithSerializer(s, ((reg_t *)_data)[i]);
|
|
|
|
}
|
|
|
|
break;
|
2016-10-03 16:02:59 -05:00
|
|
|
case kArrayTypeByte:
|
|
|
|
case kArrayTypeString:
|
|
|
|
s.syncBytes((byte *)_data, savedSize);
|
|
|
|
break;
|
2016-09-04 16:30:47 -05:00
|
|
|
default:
|
|
|
|
error("Attempt to sync invalid SciArray type %d", _type);
|
|
|
|
}
|
2010-01-30 02:03:59 +00:00
|
|
|
}
|
2016-07-29 15:48:14 -05:00
|
|
|
|
|
|
|
void BitmapTable::saveLoadWithSerializer(Common::Serializer &ser) {
|
2016-08-01 09:52:12 -05:00
|
|
|
if (ser.getVersion() < 36) {
|
2016-07-29 15:48:14 -05:00
|
|
|
return;
|
2016-08-01 09:52:12 -05:00
|
|
|
}
|
2016-07-29 15:48:14 -05:00
|
|
|
|
|
|
|
sync_Table(ser, *this);
|
|
|
|
}
|
2016-08-01 09:52:12 -05:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
2010-01-30 02:03:59 +00:00
|
|
|
#endif
|
|
|
|
|
2010-07-27 17:51:44 +00:00
|
|
|
void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette) {
|
|
|
|
s.syncBytes(palette->mapping, 256);
|
|
|
|
s.syncAsUint32LE(palette->timestamp);
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
|
|
s.syncAsByte(palette->colors[i].used);
|
|
|
|
s.syncAsByte(palette->colors[i].r);
|
|
|
|
s.syncAsByte(palette->colors[i].g);
|
|
|
|
s.syncAsByte(palette->colors[i].b);
|
|
|
|
}
|
|
|
|
s.syncBytes(palette->intensity, 256);
|
|
|
|
}
|
|
|
|
|
2010-07-23 07:36:55 +00:00
|
|
|
void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
|
2010-09-24 09:56:50 +00:00
|
|
|
if (s.getVersion() >= 25) {
|
|
|
|
// We need to save intensity of the _sysPalette at least for kq6 when entering the dark cave (room 390)
|
|
|
|
// from room 340. scripts will set intensity to 60 for this room and restore them when leaving.
|
|
|
|
// Sierra SCI is also doing this (although obviously not for SCI0->SCI01 games, still it doesn't hurt
|
2013-04-27 14:02:49 +03:00
|
|
|
// to save it everywhere). Refer to bug #3072868
|
2010-09-24 09:56:50 +00:00
|
|
|
s.syncBytes(_sysPalette.intensity, 256);
|
2010-07-23 08:08:27 +00:00
|
|
|
}
|
2010-09-24 09:56:50 +00:00
|
|
|
if (s.getVersion() >= 24) {
|
|
|
|
if (s.isLoading() && _palVaryResourceId != -1)
|
|
|
|
palVaryRemoveTimer();
|
|
|
|
|
|
|
|
s.syncAsSint32LE(_palVaryResourceId);
|
2016-12-18 20:14:12 +01:00
|
|
|
if (_palVaryResourceId != -1 || s.getVersion() >= 40) {
|
|
|
|
if (_palVaryResourceId != -1) {
|
|
|
|
palVarySaveLoadPalette(s, &_palVaryOriginPalette);
|
|
|
|
palVarySaveLoadPalette(s, &_palVaryTargetPalette);
|
|
|
|
}
|
2010-09-24 09:56:50 +00:00
|
|
|
s.syncAsSint16LE(_palVaryStep);
|
|
|
|
s.syncAsSint16LE(_palVaryStepStop);
|
|
|
|
s.syncAsSint16LE(_palVaryDirection);
|
|
|
|
s.syncAsUint16LE(_palVaryTicks);
|
|
|
|
s.syncAsSint32LE(_palVaryPaused);
|
2016-12-18 20:14:12 +01:00
|
|
|
if (s.getVersion() >= 40)
|
|
|
|
s.syncAsSint32LE(_palVarySignal);
|
2010-09-24 09:56:50 +00:00
|
|
|
}
|
2010-07-23 07:36:55 +00:00
|
|
|
|
2016-12-18 20:14:12 +01:00
|
|
|
if (s.isLoading() && s.getVersion() < 40) {
|
|
|
|
// Reset _palVaryPaused to 0 when loading an old savegame.
|
|
|
|
// Before version 40, we didn't restore or reset _palVaryPaused.
|
|
|
|
// In QfG3 this could get it stuck at positive values (bug #9674).
|
|
|
|
//
|
|
|
|
// Other SCI11 games don't appear to use palVaryPaused at all.
|
|
|
|
// (Looked at eq2, freddy, kq6, lb2, mgoose11, pq1, qg1, sq4, sq5)
|
|
|
|
_palVaryPaused = 0;
|
|
|
|
|
|
|
|
// Clear any pending updates, since _palVarySignal also wasn't saved
|
|
|
|
// before version 40.
|
|
|
|
_palVarySignal = 0;
|
|
|
|
}
|
2012-11-15 14:57:55 +02:00
|
|
|
|
2010-09-24 09:56:50 +00:00
|
|
|
if (s.isLoading() && _palVaryResourceId != -1) {
|
|
|
|
palVaryInstallTimer();
|
|
|
|
}
|
2010-07-23 08:08:27 +00:00
|
|
|
}
|
2010-07-23 07:36:55 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 10:50:41 -06:00
|
|
|
#ifdef ENABLE_SCI32
|
2016-09-30 13:10:24 -05:00
|
|
|
static void saveLoadPalette32(Common::Serializer &s, Palette *const palette) {
|
2016-01-14 10:50:41 -06:00
|
|
|
s.syncAsUint32LE(palette->timestamp);
|
|
|
|
for (int i = 0; i < ARRAYSIZE(palette->colors); ++i) {
|
|
|
|
s.syncAsByte(palette->colors[i].used);
|
|
|
|
s.syncAsByte(palette->colors[i].r);
|
|
|
|
s.syncAsByte(palette->colors[i].g);
|
|
|
|
s.syncAsByte(palette->colors[i].b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 13:10:24 -05:00
|
|
|
static void saveLoadOptionalPalette32(Common::Serializer &s, Palette **const palette) {
|
2017-01-16 07:22:32 +00:00
|
|
|
bool hasPalette = false;
|
2016-01-14 10:50:41 -06:00
|
|
|
if (s.isSaving()) {
|
|
|
|
hasPalette = (*palette != nullptr);
|
|
|
|
}
|
|
|
|
s.syncAsByte(hasPalette);
|
|
|
|
if (hasPalette) {
|
|
|
|
if (s.isLoading()) {
|
|
|
|
*palette = new Palette;
|
|
|
|
}
|
|
|
|
saveLoadPalette32(s, *palette);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
if (s.getVersion() < 34) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s.isLoading()) {
|
|
|
|
++_version;
|
2016-09-30 13:09:43 -05:00
|
|
|
|
|
|
|
for (int i = 0; i < kNumCyclers; ++i) {
|
|
|
|
delete _cyclers[i];
|
|
|
|
_cyclers[i] = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete _varyTargetPalette;
|
|
|
|
_varyTargetPalette = nullptr;
|
|
|
|
delete _varyStartPalette;
|
|
|
|
_varyStartPalette = nullptr;
|
2016-01-14 10:50:41 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
s.syncAsSint16LE(_varyDirection);
|
|
|
|
s.syncAsSint16LE(_varyPercent);
|
|
|
|
s.syncAsSint16LE(_varyTargetPercent);
|
|
|
|
s.syncAsSint16LE(_varyFromColor);
|
|
|
|
s.syncAsSint16LE(_varyToColor);
|
|
|
|
s.syncAsUint16LE(_varyNumTimesPaused);
|
2016-07-09 12:41:12 -05:00
|
|
|
s.syncAsByte(_needsUpdate);
|
2016-01-14 10:50:41 -06:00
|
|
|
s.syncAsSint32LE(_varyTime);
|
|
|
|
s.syncAsUint32LE(_varyLastTick);
|
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
|
|
|
|
s.syncAsByte(_fadeTable[i]);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_cycleMap); ++i) {
|
|
|
|
s.syncAsByte(_cycleMap[i]);
|
|
|
|
}
|
|
|
|
|
2017-02-24 12:15:57 -06:00
|
|
|
if (g_sci->_features->hasLatePaletteCode() && s.getVersion() >= 41) {
|
|
|
|
s.syncAsSint16LE(_gammaLevel);
|
|
|
|
saveLoadPalette32(s, &_sourcePalette);
|
|
|
|
++_version;
|
|
|
|
_needsUpdate = true;
|
|
|
|
_gammaChanged = true;
|
|
|
|
}
|
|
|
|
|
2016-01-14 10:50:41 -06:00
|
|
|
saveLoadOptionalPalette32(s, &_varyTargetPalette);
|
|
|
|
saveLoadOptionalPalette32(s, &_varyStartPalette);
|
2017-02-24 12:15:57 -06:00
|
|
|
|
|
|
|
// _nextPalette is not saved by SSCI
|
2016-01-14 10:50:41 -06:00
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_cyclers); ++i) {
|
2016-02-05 23:50:53 +02:00
|
|
|
PalCycler *cycler = nullptr;
|
2016-01-14 10:50:41 -06:00
|
|
|
|
2017-01-16 07:22:32 +00:00
|
|
|
bool hasCycler = false;
|
2016-01-14 10:50:41 -06:00
|
|
|
if (s.isSaving()) {
|
|
|
|
cycler = _cyclers[i];
|
|
|
|
hasCycler = (cycler != nullptr);
|
|
|
|
}
|
|
|
|
s.syncAsByte(hasCycler);
|
|
|
|
|
|
|
|
if (hasCycler) {
|
|
|
|
if (s.isLoading()) {
|
|
|
|
_cyclers[i] = cycler = new PalCycler;
|
|
|
|
}
|
|
|
|
|
|
|
|
s.syncAsByte(cycler->fromColor);
|
|
|
|
s.syncAsUint16LE(cycler->numColorsToCycle);
|
|
|
|
s.syncAsByte(cycler->currentCycle);
|
|
|
|
s.syncAsByte(cycler->direction);
|
|
|
|
s.syncAsUint32LE(cycler->lastUpdateTick);
|
|
|
|
s.syncAsSint16LE(cycler->delay);
|
|
|
|
s.syncAsUint16LE(cycler->numTimesPaused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-06-25 14:19:47 -05:00
|
|
|
|
|
|
|
void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
if (s.getVersion() < 35) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s.syncAsByte(_numActiveRemaps);
|
|
|
|
s.syncAsByte(_blockedRangeStart);
|
|
|
|
s.syncAsSint16LE(_blockedRangeCount);
|
|
|
|
|
|
|
|
for (uint i = 0; i < _remaps.size(); ++i) {
|
|
|
|
SingleRemap &singleRemap = _remaps[i];
|
|
|
|
s.syncAsByte(singleRemap._type);
|
|
|
|
if (s.isLoading() && singleRemap._type != kRemapNone) {
|
|
|
|
singleRemap.reset();
|
|
|
|
}
|
|
|
|
s.syncAsByte(singleRemap._from);
|
|
|
|
s.syncAsByte(singleRemap._to);
|
|
|
|
s.syncAsByte(singleRemap._delta);
|
|
|
|
s.syncAsByte(singleRemap._percent);
|
|
|
|
s.syncAsByte(singleRemap._gray);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (s.isLoading()) {
|
|
|
|
_needsUpdate = true;
|
|
|
|
}
|
|
|
|
}
|
2016-07-31 13:41:05 -05:00
|
|
|
|
|
|
|
void GfxCursor32::saveLoadWithSerializer(Common::Serializer &s) {
|
2016-08-28 13:04:02 -05:00
|
|
|
if (s.getVersion() < 38) {
|
2016-07-31 13:41:05 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-28 13:04:02 -05:00
|
|
|
int32 hideCount;
|
|
|
|
if (s.isSaving()) {
|
|
|
|
hideCount = _hideCount;
|
|
|
|
}
|
|
|
|
s.syncAsSint32LE(hideCount);
|
2016-07-31 13:41:05 -05:00
|
|
|
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);
|
2016-08-28 13:04:02 -05:00
|
|
|
if (!hideCount) {
|
2016-07-31 13:41:05 -05:00
|
|
|
show();
|
2016-08-28 13:04:02 -05:00
|
|
|
} else {
|
|
|
|
_hideCount = hideCount;
|
2016-07-31 13:41:05 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-25 18:13:55 -06:00
|
|
|
|
|
|
|
void Video32::beforeSaveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
if (getSciVersion() < SCI_VERSION_3 || s.isSaving()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_robotPlayer.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Video32::saveLoadWithSerializer(Common::Serializer &s) {
|
|
|
|
if (getSciVersion() < SCI_VERSION_3) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool robotExists = _robotPlayer.getStatus() != RobotDecoder::kRobotStatusUninitialized;
|
|
|
|
s.syncAsByte(robotExists);
|
|
|
|
if (robotExists) {
|
|
|
|
GuiResourceId robotId;
|
|
|
|
reg_t planeId;
|
|
|
|
Common::Point position;
|
|
|
|
int16 priority, scale;
|
|
|
|
int frameNo;
|
|
|
|
|
|
|
|
if (s.isSaving()) {
|
|
|
|
robotId = _robotPlayer.getResourceId();
|
|
|
|
planeId = _robotPlayer.getPlaneId();
|
|
|
|
priority = _robotPlayer.getPriority();
|
|
|
|
position = _robotPlayer.getPosition();
|
|
|
|
scale = _robotPlayer.getScale();
|
|
|
|
frameNo = _robotPlayer.getFrameNo();
|
|
|
|
}
|
|
|
|
|
|
|
|
s.syncAsUint16LE(robotId);
|
|
|
|
syncWithSerializer(s, planeId);
|
|
|
|
s.syncAsSint16LE(priority);
|
|
|
|
s.syncAsSint16LE(position.x);
|
|
|
|
s.syncAsSint16LE(position.y);
|
|
|
|
s.syncAsSint16LE(scale);
|
|
|
|
s.syncAsSint32LE(frameNo);
|
|
|
|
|
|
|
|
if (s.isLoading()) {
|
|
|
|
_robotPlayer.open(robotId, planeId, priority, position.x, position.y, scale);
|
|
|
|
_robotPlayer.showFrame(frameNo, position.x, position.y, priority);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-14 10:50:41 -06:00
|
|
|
#endif
|
|
|
|
|
2010-11-01 16:09:33 +00:00
|
|
|
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
|
2015-04-26 15:29:02 +02:00
|
|
|
// reset() is called directly way earlier in gamestate_restore()
|
2010-11-01 16:09:33 +00:00
|
|
|
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
|
2010-11-01 20:08:42 +00:00
|
|
|
if (window->counterTillFree) {
|
|
|
|
_freeCounter++;
|
|
|
|
} else {
|
2015-04-23 18:54:07 +02:00
|
|
|
// 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.
|
2010-11-01 20:08:42 +00:00
|
|
|
}
|
2010-11-01 16:09:33 +00:00
|
|
|
|
|
|
|
windowCount--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-15 07:20:53 +00:00
|
|
|
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];
|
|
|
|
if (mobj && mobj->getType() == SEG_TYPE_CLONES) {
|
|
|
|
CloneTable *ct = (CloneTable *)mobj;
|
|
|
|
|
|
|
|
for (uint j = 0; j < ct->_table.size(); j++) {
|
|
|
|
// Check if the clone entry is used
|
|
|
|
uint entryNum = (uint)ct->first_free;
|
|
|
|
bool isUsed = true;
|
|
|
|
while (entryNum != ((uint) CloneTable::HEAPENTRY_INVALID)) {
|
|
|
|
if (entryNum == j) {
|
|
|
|
isUsed = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
entryNum = ct->_table[entryNum].next_free;
|
|
|
|
}
|
2010-06-08 13:16:30 +00:00
|
|
|
|
2010-06-15 07:20:53 +00:00
|
|
|
if (!isUsed)
|
|
|
|
continue;
|
|
|
|
|
2016-03-23 20:41:48 +01:00
|
|
|
CloneTable::value_type &seeker = ct->at(j);
|
2010-06-15 07:20:53 +00:00
|
|
|
const Object *baseObj = getObject(seeker.getSpeciesSelector());
|
|
|
|
seeker.cloneFromObject(baseObj);
|
2010-08-07 00:22:57 +00:00
|
|
|
if (!baseObj) {
|
|
|
|
// Can happen when loading some KQ6 savegames
|
|
|
|
warning("Clone entry without a base class: %d", j);
|
|
|
|
}
|
2010-06-15 07:20:53 +00:00
|
|
|
} // end for
|
2010-07-06 23:24:36 +00:00
|
|
|
} // end if
|
2010-06-15 07:20:53 +00:00
|
|
|
} // end for
|
2010-06-08 13:16:30 +00:00
|
|
|
}
|
|
|
|
|
2010-06-15 07:25:09 +00:00
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
|
|
|
2011-06-02 14:11:38 +02:00
|
|
|
bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::String &savename, const Common::String &version) {
|
2010-06-15 07:25:09 +00:00
|
|
|
TimeDate curTime;
|
|
|
|
g_system->getTimeAndDate(curTime);
|
|
|
|
|
|
|
|
SavegameMetadata meta;
|
2010-10-31 01:45:24 +00:00
|
|
|
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);
|
2010-06-15 07:25:09 +00:00
|
|
|
|
2010-06-15 08:25:51 +00:00
|
|
|
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
|
2016-12-31 20:39:57 -06:00
|
|
|
meta.script0Size = script0->size();
|
2012-06-18 05:21:59 +03:00
|
|
|
meta.gameObjectOffset = g_sci->getGameObject().getOffset();
|
2010-06-15 08:25:51 +00:00
|
|
|
|
2010-07-17 17:58:50 +00:00
|
|
|
// Checking here again
|
2016-09-09 14:21:33 -05:00
|
|
|
// TODO: This breaks Torin autosave, is there actually any reason for it?
|
|
|
|
// if (s->executionStackBase) {
|
|
|
|
// warning("Cannot save from below kernel function");
|
|
|
|
// return false;
|
|
|
|
// }
|
2010-06-15 07:25:09 +00:00
|
|
|
|
|
|
|
Common::Serializer ser(0, fh);
|
|
|
|
sync_SavegameMetadata(ser, meta);
|
|
|
|
Graphics::saveThumbnail(*fh);
|
|
|
|
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
|
2010-11-01 16:09:33 +00:00
|
|
|
if (g_sci->_gfxPorts)
|
|
|
|
g_sci->_gfxPorts->saveLoadWithSerializer(ser);
|
2010-12-24 14:56:04 +00:00
|
|
|
Vocabulary *voc = g_sci->getVocabulary();
|
|
|
|
if (voc)
|
|
|
|
voc->saveLoadWithSerializer(ser);
|
2010-06-15 07:25:09 +00:00
|
|
|
|
2014-10-19 23:44:45 +02:00
|
|
|
// TODO: SSCI (at least JonesCD, presumably more) also stores the Menu state
|
|
|
|
|
2010-06-15 08:39:03 +00:00
|
|
|
return true;
|
2010-06-15 07:25:09 +00:00
|
|
|
}
|
|
|
|
|
2010-08-17 20:33:22 +00:00
|
|
|
extern void showScummVMDialog(const Common::String &message);
|
|
|
|
|
2016-02-09 01:28:08 +01:00
|
|
|
void gamestate_afterRestoreFixUp(EngineState *s, int savegameId) {
|
|
|
|
switch (g_sci->getGameId()) {
|
|
|
|
case GID_MOTHERGOOSE:
|
|
|
|
// WORKAROUND: Mother Goose SCI0
|
|
|
|
// Script 200 / rm200::newRoom will set global C5h directly right after creating a child to the
|
|
|
|
// current number of children plus 1.
|
|
|
|
// We can't trust that global, that's why we set the actual savedgame id right here directly after
|
|
|
|
// restoring a saved game.
|
|
|
|
// If we didn't, the game would always save to a new slot
|
|
|
|
s->variables[VAR_GLOBAL][0xC5].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
|
|
|
|
break;
|
|
|
|
case GID_MOTHERGOOSE256:
|
|
|
|
// WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
|
|
|
|
// saving a previously restored game.
|
|
|
|
// We set the current savedgame-id directly and remove the script
|
|
|
|
// code concerning this via script patch.
|
|
|
|
s->variables[VAR_GLOBAL][0xB3].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
|
|
|
|
break;
|
|
|
|
case GID_JONES:
|
|
|
|
// HACK: The code that enables certain menu items isn't called when a game is restored from the
|
|
|
|
// launcher, or the "Restore game" option in the game's main menu - bugs #6537 and #6723.
|
|
|
|
// These menu entries are disabled when the game is launched, and are enabled when a new game is
|
|
|
|
// started. The code for enabling these entries is is all in script 1, room1::init, but that code
|
|
|
|
// path is never followed in these two cases (restoring game from the menu, or restoring a game
|
|
|
|
// from the ScummVM launcher). Thus, we perform the calls to enable the menus ourselves here.
|
|
|
|
// These two are needed when restoring from the launcher
|
|
|
|
// FIXME: The original interpreter saves and restores the menu state, so these attributes
|
|
|
|
// are automatically reset there. We may want to do the same.
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(257 >> 8, 257 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> About Jones
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(258 >> 8, 258 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Sierra -> Help
|
|
|
|
// The rest are normally enabled from room1::init
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(769 >> 8, 769 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Options -> Delete current player
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(513 >> 8, 513 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(515 >> 8, 515 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Restore Game
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals
|
|
|
|
break;
|
2016-03-16 14:49:21 +01:00
|
|
|
case GID_KQ6:
|
|
|
|
if (g_sci->isCD()) {
|
|
|
|
// WORKAROUND:
|
|
|
|
// For the CD version of King's Quest 6, set global depending on current hires/lowres state
|
|
|
|
// The game sets a global at the start depending on it and some things check that global
|
|
|
|
// instead of checking platform like for example the game action menu.
|
|
|
|
// This never happened in the original interpreter, because the original DOS interpreter
|
|
|
|
// was only capable of lowres graphics and the original Windows 3.11 interpreter was only capable
|
|
|
|
// of hires graphics. Saved games were not compatible between those two.
|
|
|
|
// Which means saving during lowres mode, then going into hires mode and restoring that saved game,
|
|
|
|
// will result in some graphics being incorrect (lowres).
|
|
|
|
// That's why we are setting the global after restoring a saved game depending on hires/lowres state.
|
|
|
|
// The CD demo of KQ6 does the same and uses the exact same global.
|
|
|
|
if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) {
|
|
|
|
s->variables[VAR_GLOBAL][0xA9].setOffset(1);
|
|
|
|
} else {
|
|
|
|
s->variables[VAR_GLOBAL][0xA9].setOffset(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-02-09 01:28:08 +01:00
|
|
|
case GID_PQ2:
|
|
|
|
// HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875).
|
|
|
|
// It gets disabled in the game's death screen.
|
|
|
|
g_sci->_gfxMenu->kernelSetAttribute(2, 1, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Game -> Save Game
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-31 01:26:06 +00:00
|
|
|
void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
|
2009-02-28 11:07:36 +00:00
|
|
|
SavegameMetadata meta;
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
Common::Serializer ser(fh, 0);
|
|
|
|
sync_SavegameMetadata(ser, meta);
|
2009-02-27 19:50:22 +00:00
|
|
|
|
2010-01-31 01:26:06 +00:00
|
|
|
if (fh->eos()) {
|
2010-08-24 09:00:53 +00:00
|
|
|
s->r_acc = TRUE_REG; // signal failure
|
2010-01-31 01:26:06 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-02-28 11:07:36 +00:00
|
|
|
|
2014-02-17 11:46:24 +02:00
|
|
|
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);
|
|
|
|
}
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2010-08-24 09:00:53 +00:00
|
|
|
s->r_acc = TRUE_REG; // signal failure
|
2010-01-31 01:26:06 +00:00
|
|
|
return;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
|
|
|
|
2010-10-31 01:45:24 +00:00
|
|
|
if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
|
2010-06-15 08:25:51 +00:00
|
|
|
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
|
2016-12-31 20:39:57 -06:00
|
|
|
if (script0->size() != meta.script0Size || g_sci->getGameObject().getOffset() != meta.gameObjectOffset) {
|
2010-08-17 20:33:22 +00:00
|
|
|
showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
|
2010-06-15 08:25:51 +00:00
|
|
|
|
2010-08-24 09:00:53 +00:00
|
|
|
s->r_acc = TRUE_REG; // signal failure
|
2010-06-15 08:25:51 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-07 20:12:41 +00:00
|
|
|
// We don't need the thumbnail here, so just read it and discard it
|
2010-08-02 22:28:30 +00:00
|
|
|
Graphics::skipThumbnail(*fh);
|
2009-10-11 15:51:43 +00:00
|
|
|
|
2015-04-26 15:50:12 +02:00
|
|
|
// reset ports is one of the first things we do, because that may free() some hunk memory
|
2015-04-26 15:29:02 +02:00
|
|
|
// and we don't want to do that after we read in the saved game hunk memory
|
2015-04-27 20:50:37 +02:00
|
|
|
if (g_sci->_gfxPorts)
|
|
|
|
g_sci->_gfxPorts->reset();
|
|
|
|
// clear screen
|
2016-02-22 21:38:01 +01:00
|
|
|
if (getSciVersion() <= SCI_VERSION_1_1) {
|
|
|
|
// Only do clearing the screen for SCI16
|
|
|
|
// Both SCI16 + SCI32 did not clear the screen.
|
|
|
|
// We basically do it for SCI16, because of KQ6.
|
|
|
|
// When hires portraits are shown and the user restores during that time, the portraits
|
|
|
|
// wouldn't get fully removed. In original SCI, the user wasn't able to restore during that time,
|
|
|
|
// so this is basically a workaround, so that ScummVM features work properly.
|
|
|
|
// For SCI32, behavior was verified in DOSBox, that SCI32 does not clear and also not redraw the screen.
|
|
|
|
// It only redraws elements that have changed in comparison to the state before the restore.
|
|
|
|
// If we cleared the screen for SCI32, we would have issues because of this behavior.
|
|
|
|
if (g_sci->_gfxScreen)
|
|
|
|
g_sci->_gfxScreen->clearForRestoreGame();
|
|
|
|
}
|
2015-04-26 15:29:02 +02:00
|
|
|
|
2010-06-01 15:48:17 +00:00
|
|
|
s->reset(true);
|
|
|
|
s->saveLoadWithSerializer(ser); // FIXME: Error handling?
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 19:08:38 +00:00
|
|
|
// Now copy all current state information
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-06-08 13:16:30 +00:00
|
|
|
s->_segMan->reconstructStack(s);
|
2010-06-01 15:48:17 +00:00
|
|
|
s->_segMan->reconstructClones();
|
2010-06-09 09:17:48 +00:00
|
|
|
s->initGlobals();
|
2010-06-10 11:43:20 +00:00
|
|
|
s->gcCountDown = GC_INTERVAL - 1;
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-02-20 19:08:38 +00:00
|
|
|
// Time state:
|
2010-06-10 11:18:10 +00:00
|
|
|
s->lastWaitTime = g_system->getMillis();
|
2010-06-14 08:36:52 +00:00
|
|
|
s->_screenUpdateTime = g_system->getMillis();
|
2016-01-14 10:54:27 -06:00
|
|
|
if (meta.version >= 34) {
|
|
|
|
g_sci->setTickCount(meta.playTime);
|
|
|
|
} else {
|
|
|
|
g_engine->setTotalPlayTime(meta.playTime * 1000);
|
|
|
|
}
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2010-08-04 05:15:01 +00:00
|
|
|
if (g_sci->_gfxPorts)
|
2010-11-01 16:09:33 +00:00
|
|
|
g_sci->_gfxPorts->saveLoadWithSerializer(ser);
|
2010-12-24 14:56:04 +00:00
|
|
|
|
|
|
|
Vocabulary *voc = g_sci->getVocabulary();
|
|
|
|
if (ser.getVersion() >= 30 && voc)
|
|
|
|
voc->saveLoadWithSerializer(ser);
|
2010-12-24 14:47:47 +00:00
|
|
|
|
2010-10-31 20:57:50 +00:00
|
|
|
g_sci->_soundCmd->reconstructPlayList();
|
2009-02-15 06:10:59 +00:00
|
|
|
|
2009-05-26 11:33:18 +00:00
|
|
|
// Message state:
|
2010-08-02 23:03:04 +00:00
|
|
|
delete s->_msgState;
|
2010-06-01 15:48:17 +00:00
|
|
|
s->_msgState = new MessageState(s->_segMan);
|
2009-05-26 11:33:18 +00:00
|
|
|
|
2010-12-07 00:47:05 +00:00
|
|
|
// System strings:
|
|
|
|
s->_segMan->initSysStrings();
|
|
|
|
|
2010-06-08 21:05:46 +00:00
|
|
|
s->abortScriptProcessing = kAbortLoadGame;
|
2010-07-31 14:09:42 +00:00
|
|
|
|
|
|
|
// signal restored game to game scripts
|
|
|
|
s->gameIsRestarting = GAMEISRESTARTING_RESTORE;
|
2009-02-15 06:10:59 +00:00
|
|
|
}
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) {
|
|
|
|
assert(stream);
|
|
|
|
assert(meta);
|
|
|
|
|
|
|
|
Common::Serializer ser(stream, 0);
|
|
|
|
sync_SavegameMetadata(ser, *meta);
|
2009-02-20 23:41:15 +00:00
|
|
|
|
2009-03-15 20:31:29 +00:00
|
|
|
if (stream->eos())
|
2009-02-20 23:41:15 +00:00
|
|
|
return false;
|
|
|
|
|
2010-10-31 01:45:24 +00:00
|
|
|
if ((meta->version < MINIMUM_SAVEGAME_VERSION) ||
|
|
|
|
(meta->version > CURRENT_SAVEGAME_VERSION)) {
|
|
|
|
if (meta->version < MINIMUM_SAVEGAME_VERSION)
|
2009-07-06 10:39:22 +00:00
|
|
|
warning("Old savegame version detected- can't load");
|
2009-02-20 23:41:15 +00:00
|
|
|
else
|
2010-10-31 01:45:24 +00:00
|
|
|
warning("Savegame version is %d- maximum supported is %0d", meta->version, CURRENT_SAVEGAME_VERSION);
|
2009-02-20 23:41:15 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-02-21 10:23:36 +00:00
|
|
|
} // End of namespace Sci
|