aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2016-11-03 23:51:18 +0100
committerGitHub2016-11-03 23:51:18 +0100
commit6cad083f84ed3ab5b1cd1bc054308ce26930d6c4 (patch)
tree7223e6d939a839275e40b3cba862402b93e80efc
parent869354830fe8fe8fedd0829cdc7e99b1bd82d6b7 (diff)
parentec06c04faa7a14e3586849a670ff8db71d43a7ca (diff)
downloadscummvm-rg350-6cad083f84ed3ab5b1cd1bc054308ce26930d6c4.tar.gz
scummvm-rg350-6cad083f84ed3ab5b1cd1bc054308ce26930d6c4.tar.bz2
scummvm-rg350-6cad083f84ed3ab5b1cd1bc054308ce26930d6c4.zip
Merge pull request #851 from angstsmurf/composer-gmm-new
COMPOSER: Support GMM saving/loading and load from launcher
-rw-r--r--engines/composer/composer.cpp14
-rw-r--r--engines/composer/composer.h19
-rw-r--r--engines/composer/detection.cpp50
-rw-r--r--engines/composer/graphics.cpp65
-rw-r--r--engines/composer/graphics.h1
-rw-r--r--engines/composer/module.mk1
-rw-r--r--engines/composer/resource.cpp21
-rw-r--r--engines/composer/resource.h12
-rw-r--r--engines/composer/saveload.cpp428
9 files changed, 573 insertions, 38 deletions
diff --git a/engines/composer/composer.cpp b/engines/composer/composer.cpp
index 73d97e100d..13ba76191b 100644
--- a/engines/composer/composer.cpp
+++ b/engines/composer/composer.cpp
@@ -21,6 +21,7 @@
*/
#include "common/scummsys.h"
+#include "common/config-manager.h"
#include "common/events.h"
#include "common/random.h"
#include "common/keyboard.h"
@@ -120,6 +121,9 @@ Common::Error ComposerEngine::run() {
else
warning("FPS in book.ini is zero. Defaulting to 8...");
uint32 lastDrawTime = 0;
+ _lastSaveTime = _system->getMillis();
+
+ bool loadFromLauncher = ConfMan.hasKey("save_slot");
while (!shouldQuit()) {
for (uint i = 0; i < _pendingPageChanges.size(); i++) {
@@ -169,7 +173,12 @@ Common::Error ComposerEngine::run() {
} else if (_needsUpdate) {
redraw();
}
-
+ if (loadFromLauncher) {
+ loadGameState(ConfMan.getInt("save_slot"));
+ loadFromLauncher = false;
+ }
+ if (shouldPerformAutoSave(_lastSaveTime))
+ saveGameState(0, "Autosave");
while (_eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
@@ -378,7 +387,7 @@ void ComposerEngine::loadLibrary(uint id) {
}
Common::String filename;
-
+ Common::String oldGroup = _bookGroup;
if (getGameType() == GType_ComposerV1) {
if (!id || _bookGroup.empty())
filename = getStringFromConfig("Common", "StartPage");
@@ -412,6 +421,7 @@ void ComposerEngine::loadLibrary(uint id) {
Library library;
library._id = id;
+ library._group = oldGroup;
library._archive = new ComposerArchive();
if (!library._archive->openFile(filename))
error("failed to open '%s'", filename.c_str());
diff --git a/engines/composer/composer.h b/engines/composer/composer.h
index d1a85e975a..a4b421bfa0 100644
--- a/engines/composer/composer.h
+++ b/engines/composer/composer.h
@@ -29,6 +29,7 @@
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/error.h"
+#include "common/serializer.h"
#include "common/textconsole.h"
#include "common/rect.h"
@@ -114,6 +115,7 @@ struct Library {
uint _id;
Archive *_archive;
+ Common::String _group;
Common::List<Button> _buttons;
Common::List<KeyboardHandler> _keyboardHandlers;
};
@@ -150,6 +152,19 @@ class ComposerEngine : public Engine {
protected:
Common::Error run();
+ template <typename T>
+ void syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
+ template <typename T>
+ void syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
+ template <typename T>
+ void syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion = 0, Common::Serializer::Version maxVersion = Common::Serializer::kLastVersion);
+ template <typename T>
+ void sync(Common::Serializer &ser, T &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion);
+ bool canLoadGameStateCurrently() { return true; }
+ Common::Error loadGameState(int slot);
+ bool canSaveGameStateCurrently() { return true; }
+ Common::Error saveGameState(int slot, const Common::String &desc);
+
public:
ComposerEngine(OSystem *syst, const ComposerGameDescription *gameDesc);
virtual ~ComposerEngine();
@@ -173,7 +188,7 @@ private:
Audio::QueuingAudioStream *_audioStream;
uint16 _currSoundPriority;
- uint32 _currentTime, _lastTime;
+ uint32 _currentTime, _lastTime, _timeDelta, _lastSaveTime;
bool _needsUpdate;
Common::Array<Common::Rect> _dirtyRects;
@@ -210,6 +225,7 @@ private:
uint16 _mouseSpriteId;
Common::Point _mouseOffset;
+ Common::String makeSaveGameName(int slot);
Common::String getStringFromConfig(const Common::String &section, const Common::String &key);
Common::String getFilename(const Common::String &section, uint id);
Common::String mangleFilename(Common::String filename);
@@ -231,6 +247,7 @@ private:
void tickOldScripts();
bool tickOldScript(OldScript *script);
+ void loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size = 0);
void playAnimation(uint16 animId, int16 param1, int16 param2, int16 param3);
void stopAnimation(Animation *anim, bool localOnly = false, bool pipesOnly = false);
void playWaveForAnim(uint16 id, uint16 priority, bool bufferingOnly);
diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp
index 689a72a743..8de3b33134 100644
--- a/engines/composer/detection.cpp
+++ b/engines/composer/detection.cpp
@@ -21,6 +21,9 @@
*/
#include "base/plugins.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/str-array.h"
#include "engines/advancedDetector.h"
#include "composer/composer.h"
@@ -448,6 +451,8 @@ public:
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual SaveStateList listSaves(const char* target) const;
};
bool ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
@@ -459,11 +464,52 @@ bool ComposerMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
}
bool ComposerMetaEngine::hasFeature(MetaEngineFeature f) const {
- return false;
+ return ((f == kSupportsListSaves) || (f == kSupportsLoadingDuringStartup));
+}
+
+Common::String getSaveName(Common::InSaveFile *in) {
+ Common::Serializer ser(in, NULL);
+ Common::String name;
+ uint32 tmp;
+ ser.syncAsUint32LE(tmp);
+ ser.syncAsUint32LE(tmp);
+ ser.syncString(name);
+ return name;
+}
+int ComposerMetaEngine::getMaximumSaveSlot() const {
+ return 99;
+}
+SaveStateList ComposerMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = Common::String::format("%s.??", target);
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNum = atoi(file->c_str() + file->size() - 2);
+
+ if (slotNum >= 0 && slotNum <= 99) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(*file);
+ if (in) {
+ saveDesc = getSaveName(in);
+ saveList.push_back(SaveStateDescriptor(slotNum, saveDesc));
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
}
bool Composer::ComposerEngine::hasFeature(EngineFeature f) const {
- return (f == kSupportsRTL);
+ return (f == kSupportsRTL
+ || f == kSupportsSavingDuringRuntime
+ || f == kSupportsLoadingDuringRuntime);
}
#if PLUGIN_ENABLED_DYNAMIC(COMPOSER)
diff --git a/engines/composer/graphics.cpp b/engines/composer/graphics.cpp
index 87694636d8..32b9812f32 100644
--- a/engines/composer/graphics.cpp
+++ b/engines/composer/graphics.cpp
@@ -44,9 +44,9 @@ bool Sprite::contains(const Common::Point &pos) const {
}
enum {
- kAnimOpEvent = 1,
- kAnimOpPlayWave = 2,
- kAnimOpPlayAnim = 3,
+ kAnimOpEvent = 1,
+ kAnimOpPlayWave = 2,
+ kAnimOpPlayAnim = 3,
kAnimOpDrawSprite = 4
};
@@ -57,6 +57,7 @@ Animation::Animation(Common::SeekableReadStream *stream, uint16 id, Common::Poin
// probably total size?
uint32 unknown = _stream->readUint32LE();
+ _size = unknown;
debug(8, "anim: size %d, state %08x, unknown %08x", size, _state, unknown);
@@ -82,17 +83,7 @@ void Animation::seekToCurrPos() {
_stream->seek(_offset, SEEK_SET);
}
-void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) {
- // First, we check if this animation is already playing,
- // and if it is, we sabotage that running one first.
- for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
- Animation *anim = *i;
- if (anim->_id != animId)
- continue;
-
- stopAnimation(*i);
- }
-
+void ComposerEngine::loadAnimation(Animation *&anim, uint16 animId, int16 x, int16 y, int16 eventParam, int32 size) {
Common::SeekableReadStream *stream = NULL;
Pipe *newPipe = NULL;
@@ -102,7 +93,10 @@ void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventP
if (!pipe->hasResource(ID_ANIM, animId))
continue;
stream = pipe->getResource(ID_ANIM, animId, false);
- break;
+
+ // When loading from savegame, make sure we have the correct stream
+ if ((!size) || (stream->size() >= size)) break;
+ stream = NULL;
}
// If we didn't find it, try the libraries.
@@ -111,33 +105,50 @@ void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventP
warning("ignoring attempt to play invalid anim %d", animId);
return;
}
- stream = getResource(ID_ANIM, animId);
+ Common::List<Library>::iterator j;
+ for (j = _libraries.begin(); j != _libraries.end(); j++) {
+ stream = j->_archive->getResource(ID_ANIM, animId);
- uint32 type = 0;
- for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
- if (i->_archive->hasResource(ID_ANIM, animId)) {
- type = i->_archive->getResourceFlags(ID_ANIM, animId);
- break;
- }
+ // When loading from savegame, make sure we have the correct stream
+ if ((!size) || (stream->size() >= size)) break;
+ stream = NULL;
+ }
+
+ uint32 type = j->_archive->getResourceFlags(ID_ANIM, animId);
// If the resource is a pipe itself, then load the pipe
// and then fish the requested animation out of it.
if (type != 1) {
_pipeStreams.push_back(stream);
- newPipe = new Pipe(stream);
+ newPipe = new Pipe(stream, animId);
_pipes.push_front(newPipe);
newPipe->nextFrame();
stream = newPipe->getResource(ID_ANIM, animId, false);
}
}
- Animation *anim = new Animation(stream, animId, Common::Point(x, y), eventParam);
- _anims.push_back(anim);
- runEvent(kEventAnimStarted, animId, eventParam, 0);
+ anim = new Animation(stream, animId, Common::Point(x, y), eventParam);
if (newPipe)
newPipe->_anim = anim;
}
+void ComposerEngine::playAnimation(uint16 animId, int16 x, int16 y, int16 eventParam) {
+ // First, we check if this animation is already playing,
+ // and if it is, we sabotage that running one first.
+ for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
+ Animation *anim = *i;
+ if (anim->_id != animId)
+ continue;
+
+ stopAnimation(*i);
+ }
+
+ Animation *anim = NULL;
+ loadAnimation(anim, animId, x, y, eventParam);
+ _anims.push_back(anim);
+ runEvent(kEventAnimStarted, animId, eventParam, 0);
+}
+
void ComposerEngine::stopAnimation(Animation *anim, bool localOnly, bool pipesOnly) {
// disable the animation
anim->_state = 0;
@@ -376,7 +387,7 @@ void ComposerEngine::playPipe(uint16 id) {
}
Common::SeekableReadStream *stream = getResource(ID_PIPE, id);
- OldPipe *pipe = new OldPipe(stream);
+ OldPipe *pipe = new OldPipe(stream, id);
_pipes.push_front(pipe);
//pipe->nextFrame();
diff --git a/engines/composer/graphics.h b/engines/composer/graphics.h
index a8f37ddf60..4805e5017d 100644
--- a/engines/composer/graphics.h
+++ b/engines/composer/graphics.h
@@ -59,6 +59,7 @@ struct Animation {
uint32 _eventParam;
uint32 _state;
+ uint32 _size;
Common::Array<AnimationEntry> _entries;
diff --git a/engines/composer/module.mk b/engines/composer/module.mk
index c879d53630..74465cf156 100644
--- a/engines/composer/module.mk
+++ b/engines/composer/module.mk
@@ -6,6 +6,7 @@ MODULE_OBJS = \
detection.o \
graphics.o \
resource.o \
+ saveload.o \
scripting.o
# This module can be built as a plugin
diff --git a/engines/composer/resource.cpp b/engines/composer/resource.cpp
index d867f734a9..fa1811c05a 100644
--- a/engines/composer/resource.cpp
+++ b/engines/composer/resource.cpp
@@ -248,10 +248,11 @@ bool ComposerArchive::openStream(Common::SeekableReadStream *stream) {
return true;
}
-Pipe::Pipe(Common::SeekableReadStream *stream) {
+Pipe::Pipe(Common::SeekableReadStream *stream, uint16 id) {
_offset = 0;
_stream = stream;
_anim = NULL;
+ _pipeId = id;
}
Pipe::~Pipe() {
@@ -312,8 +313,14 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer
if (res.entries.size() == 1) {
Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(_stream,
res.entries[0].offset, res.entries[0].offset + res.entries[0].size);
- if (buffering)
+ if (buffering) {
_types[tag].erase(id);
+ bool found = false;
+ for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++)
+ if ((*i) == id) found = true;
+ if (!found)
+ _bufferedResources[tag].push_back(id);
+ }
return stream;
}
@@ -330,12 +337,18 @@ Common::SeekableReadStream *Pipe::getResource(uint32 tag, uint16 id, bool buffer
_stream->read(buffer + offset, res.entries[i].size);
offset += res.entries[i].size;
}
- if (buffering)
+ if (buffering) {
_types[tag].erase(id);
+ bool found = false;
+ for (Common::List<uint16>::const_iterator i = _bufferedResources[tag].begin(); !found && (i != _bufferedResources[tag].end()); i++)
+ if ((*i) == id) found = true;
+ if (!found)
+ _bufferedResources[tag].push_back(id);
+ }
return new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
}
-OldPipe::OldPipe(Common::SeekableReadStream *stream) : Pipe(stream), _currFrame(0) {
+OldPipe::OldPipe(Common::SeekableReadStream *stream, uint16 pipeId) : Pipe(stream, pipeId), _currFrame(0) {
uint32 tag = _stream->readUint32BE();
if (tag != ID_PIPE)
error("invalid tag for pipe (%08x)", tag);
diff --git a/engines/composer/resource.h b/engines/composer/resource.h
index b624da1776..fc4e20a2cd 100644
--- a/engines/composer/resource.h
+++ b/engines/composer/resource.h
@@ -106,7 +106,7 @@ struct PipeResource {
class Pipe {
public:
- Pipe(Common::SeekableReadStream *stream);
+ Pipe(Common::SeekableReadStream *stream, uint16 id);
virtual ~Pipe();
virtual void nextFrame();
@@ -116,6 +116,11 @@ public:
Common::SeekableReadStream *getResource(uint32 tag, uint16 id, bool buffering);
virtual const Common::Array<uint16> *getScripts() { return NULL; }
+ uint16 getPipeId() const { return _pipeId; }
+ virtual uint32 getOffset() const { return _offset; }
+ virtual void setOffset(uint32 offset) { while (_offset < offset) nextFrame(); }
+ typedef Common::HashMap<uint32, Common::List<uint16> > DelMap;
+ DelMap _bufferedResources;
protected:
Common::SeekableReadStream *_stream;
@@ -123,16 +128,19 @@ protected:
typedef Common::HashMap<uint16, PipeResource> ResourceMap;
typedef Common::HashMap<uint32, ResourceMap> TypeMap;
TypeMap _types;
+ uint16 _pipeId;
uint32 _offset;
};
class OldPipe : public Pipe {
public:
- OldPipe(Common::SeekableReadStream *stream);
+ OldPipe(Common::SeekableReadStream *stream, uint16 pipeId);
void nextFrame();
const Common::Array<uint16> *getScripts() { return &_scripts; }
+ uint32 getOffset() const { return _currFrame; }
+ void setOffset(uint32 offset) { while (_currFrame < offset) nextFrame(); }
protected:
uint32 _currFrame, _numFrames;
diff --git a/engines/composer/saveload.cpp b/engines/composer/saveload.cpp
new file mode 100644
index 0000000000..ea657a9dd4
--- /dev/null
+++ b/engines/composer/saveload.cpp
@@ -0,0 +1,428 @@
+/* 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.
+
+ * 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 "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+#include "common/config-manager.h"
+#include "common/memstream.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/system.h"
+#include "common/zlib.h"
+#include "graphics/palette.h"
+
+#include "composer/composer.h"
+#include "composer/graphics.h"
+
+namespace Composer {
+
+template<class T>
+void ComposerEngine::syncArray(Common::Serializer &ser, Common::Array<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ if (ser.isSaving()) {
+ uint32 size = data.size();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (typename Common::Array<T>::iterator i = data.begin(); i != data.end(); i++) {
+ sync<T>(ser, *i, minVersion, maxVersion);
+ }
+ } else {
+ uint32 size;
+ data.clear();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (uint32 i = 0; i < size; i++) {
+ T item;
+ sync<T>(ser, item, minVersion, maxVersion);
+ data.push_back(item);
+ }
+ }
+}
+template<class T>
+void ComposerEngine::syncList(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ if (ser.isSaving()) {
+ uint32 size = data.size();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (typename Common::List<T>::iterator i = data.begin(); i != data.end(); i++) {
+ sync<T>(ser, *i, minVersion, maxVersion);
+ }
+ } else {
+ uint32 size;
+ data.clear();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (uint32 i = 0; i < size; i++) {
+ T item;
+ sync<T>(ser, item, minVersion, maxVersion);
+ data.push_back(item);
+ }
+ }
+}
+template<class T>
+void ComposerEngine::syncListReverse(Common::Serializer &ser, Common::List<T> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ if (ser.isSaving()) {
+ uint32 size = data.size();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (typename Common::List<T>::iterator i = data.reverse_begin(); i != data.end(); i--) {
+ sync<T>(ser, *i, minVersion, maxVersion);
+ }
+ } else {
+ uint32 size;
+ data.clear();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (uint32 i = 0; i < size; i++) {
+ T item;
+ sync<T>(ser, item, minVersion, maxVersion);
+ data.push_front(item);
+ }
+ }
+}
+template<>
+void ComposerEngine::sync<uint16>(Common::Serializer &ser, uint16 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint16LE(data, minVersion, maxVersion);
+}
+template<>
+void ComposerEngine::sync<uint32>(Common::Serializer &ser, uint32 &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint32LE(data, minVersion, maxVersion);
+}
+template<>
+void ComposerEngine::sync<Library>(Common::Serializer &ser, Library &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ if (ser.isSaving()) {
+ ser.syncAsUint16LE(data._id, minVersion, maxVersion);
+ ser.syncString(data._group, minVersion, maxVersion);
+ } else {
+ uint16 id;
+ ser.syncAsUint16LE(id, minVersion, maxVersion);
+ ser.syncString(_bookGroup, minVersion, maxVersion);
+ loadLibrary(id);
+ }
+}
+template<>
+void ComposerEngine::syncListReverse<Library>(Common::Serializer &ser, Common::List<Library> &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ if (ser.isSaving()) {
+ uint32 size = data.size();
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (Common::List<Library>::iterator i = data.reverse_begin(); i != data.end(); i--) {
+ sync<Library>(ser, *i, minVersion, maxVersion);
+ }
+ } else {
+ uint32 size;
+ ser.syncAsUint32LE(size, minVersion, maxVersion);
+ for (uint32 i = 0; i < size; i++) {
+ Library item;
+ sync<Library>(ser, item, minVersion, maxVersion);
+ }
+ }
+}
+template<>
+void ComposerEngine::sync<PendingPageChange>(Common::Serializer &ser, PendingPageChange &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint16LE(data._pageId, minVersion, maxVersion);
+ ser.syncAsByte(data._remove, minVersion, maxVersion);
+}
+template<>
+void ComposerEngine::sync<OldScript *>(Common::Serializer &ser, OldScript *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ uint16 id;
+ uint32 pos, delay;
+ if (ser.isSaving()) {
+ pos = data->_stream->pos();
+ id = data->_id;
+ delay = data->_currDelay;
+ }
+ ser.syncAsUint32LE(pos);
+ ser.syncAsUint16LE(id);
+ ser.syncAsUint32LE(delay);
+ if (ser.isLoading()) {
+ data = new OldScript(id, getResource(ID_SCRP, id));
+ data->_currDelay = delay;
+ data->_stream->seek(pos, SEEK_SET);
+ }
+}
+template<>
+void ComposerEngine::sync<QueuedScript>(Common::Serializer &ser, QueuedScript &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint32LE(data._baseTime);
+ ser.syncAsUint32LE(data._duration);
+ ser.syncAsUint32LE(data._count);
+ ser.syncAsUint16LE(data._scriptId);
+ if (ser.isLoading()) data._baseTime += _timeDelta;
+}
+template<>
+void ComposerEngine::sync<Pipe *>(Common::Serializer &ser, Pipe *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ uint16 id;
+ uint32 offset, tmp;
+ if (ser.isSaving()) {
+ id = data->getPipeId();
+ offset = data->getOffset();
+ tmp = data->_bufferedResources.size();
+ }
+ ser.syncAsUint16LE(id);
+ ser.syncAsUint32LE(offset);
+
+ if (ser.isLoading()) {
+ // On load, get and initialize streams
+ Common::SeekableReadStream *stream;
+ if (getGameType() == GType_ComposerV1) {
+ stream = getResource(ID_PIPE, id);
+ data = new OldPipe(stream, id);
+ } else {
+ stream = getResource(ID_ANIM, id);
+ data = new Pipe(stream, id);
+ }
+ _pipeStreams.push_back(stream);
+ data->setOffset(offset);
+ ser.syncAsUint32LE(tmp);
+ for (uint32 j = tmp; j > 0; j--) {
+ uint32 tag;
+ ser.syncAsUint32LE(tag);
+ ser.syncAsUint32LE(tmp);
+ for (uint32 k = tmp; k > 0; k--) {
+ ser.syncAsUint16LE(id);
+ if (data->hasResource(tag, id))
+ data->getResource(tag, id, true);
+ }
+ }
+ } else {
+ ser.syncAsUint32LE(tmp);
+ for (Pipe::DelMap::iterator i = data->_bufferedResources.begin(); i != data->_bufferedResources.end(); i++) {
+ uint32 key = (*i)._key;
+ ser.syncAsUint32LE(key);
+ syncList<uint16>(ser, (*i)._value, minVersion, maxVersion);
+ }
+ }
+}
+template<>
+void ComposerEngine::sync<AnimationEntry>(Common::Serializer &ser, AnimationEntry &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint32LE(data.state);
+ ser.syncAsUint16LE(data.counter);
+ ser.syncAsUint16LE(data.prevValue);
+}
+template<>
+void ComposerEngine::sync<Animation *>(Common::Serializer &ser, Animation *&data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ uint16 animId, x, y;
+ uint32 offset, state, param;
+ int32 size;
+ if (ser.isSaving()) {
+ animId = data->_id;
+ offset = data->_offset;
+ x = data->_basePos.x;
+ y = data->_basePos.x;
+ state = data->_state;
+ param = data->_eventParam;
+ size = data->_size;
+ }
+ ser.syncAsUint16LE(animId);
+ ser.syncAsUint32LE(offset);
+ ser.syncAsUint16LE(x);
+ ser.syncAsUint16LE(y);
+ ser.syncAsUint32LE(state);
+ ser.syncAsUint32LE(param);
+ ser.syncAsUint32LE(size);
+ if (ser.isLoading()) {
+ // On load, get and initialize streams
+ loadAnimation(data, animId, x, y, param, size);
+ data->_offset = offset;
+ data->_state = state;
+ uint32 tmp;
+ ser.syncAsUint32LE(tmp);
+ for (uint32 i = 0; i < tmp; i++) {
+ sync<AnimationEntry>(ser, data->_entries[i], minVersion, maxVersion);
+ }
+ } else {
+ syncArray<AnimationEntry>(ser, data->_entries, minVersion, maxVersion);
+ }
+}
+template<>
+void ComposerEngine::sync<Sprite>(Common::Serializer &ser, Sprite &data, Common::Serializer::Version minVersion, Common::Serializer::Version maxVersion) {
+ ser.syncAsUint16LE(data._id);
+ ser.syncAsUint16LE(data._animId);
+ ser.syncAsSint16LE(data._pos.x);
+ ser.syncAsSint16LE(data._pos.y);
+ ser.syncAsUint16LE(data._surface.w);
+ ser.syncAsUint16LE(data._surface.h);
+ ser.syncAsUint16LE(data._surface.pitch);
+ ser.syncAsUint16LE(data._zorder);
+ if (ser.isLoading())
+ data._surface.setPixels(malloc(data._surface.h * data._surface.pitch));
+ byte *pix = static_cast<byte *>(data._surface.getPixels());
+ for (uint16 y = 0; y < data._surface.h; y++) {
+ for (uint16 x = 0; x < data._surface.w; x++) {
+ ser.syncAsByte(pix[x]);
+ }
+ pix += data._surface.pitch;
+ }
+
+}
+Common::String ComposerEngine::makeSaveGameName(int slot) {
+ return (_targetName + Common::String::format(".%02d", slot));
+}
+
+Common::Error ComposerEngine::loadGameState(int slot) {
+ Common::String filename = makeSaveGameName(slot);
+ Common::InSaveFile *in;
+ if (!(in = _saveFileMan->openForLoading(filename)))
+ return Common::kPathNotFile;
+
+ Common::Serializer ser(in, NULL);
+ byte magic[4];
+ ser.syncBytes(magic, 4);
+ if (magic[0] != 'C' || magic[1] != 'M' || magic[2] != 'P' || magic[3] != 'S')
+ return Common::kUnknownError;
+
+ ser.syncVersion(0);
+ Common::String desc;
+ ser.syncString(desc);
+ uint32 tmp;
+ ser.syncAsUint32LE(tmp);
+ _rnd->setSeed(tmp);
+ ser.syncAsUint32LE(_currentTime);
+ _timeDelta = _system->getMillis() - _currentTime;
+ _currentTime += _timeDelta;
+ ser.syncAsUint32LE(_lastTime);
+ _lastTime += _timeDelta;
+
+ // Unload all Libraries
+ Common::Array<uint16> libIds;
+ for (Common::List<Library>::iterator i = _libraries.begin(); i != _libraries.end(); i++)
+ libIds.push_back((*i)._id);
+ for (uint32 i = 0; i < libIds.size(); i++)
+ unloadLibrary(libIds[i]);
+
+ syncListReverse<Library>(ser, _libraries);
+ ser.syncString(_bookGroup);
+
+ syncArray<PendingPageChange>(ser, _pendingPageChanges);
+ syncArray<uint16>(ser, _stack);
+ syncArray<uint16>(ser, _vars);
+
+ // Free outdated pointers
+ for (Common::List<OldScript *>::iterator i = _oldScripts.begin(); i != _oldScripts.end(); i++) {
+ delete *i;
+ }
+
+ syncList<OldScript *>(ser, _oldScripts);
+ syncArray<QueuedScript>(ser, _queuedScripts);
+
+ ser.syncAsSint16LE(_lastMousePos.x);
+ ser.syncAsSint16LE(_lastMousePos.y);
+ g_system->warpMouse(_lastMousePos.x, _lastMousePos.y);
+ ser.syncAsByte(_mouseEnabled);
+ ser.syncAsByte(_mouseVisible);
+ ser.syncAsUint16LE(_mouseSpriteId);
+
+ // Free outdated pointers
+ for (Common::List<Pipe *>::iterator i = _pipes.begin(); i != _pipes.end(); i++) {
+ delete *i;
+ }
+ for (Common::Array<Common::SeekableReadStream *>::iterator i = _pipeStreams.begin(); i != _pipeStreams.end(); i++) {
+ delete *i;
+ }
+
+ _pipeStreams.clear();
+ syncListReverse<Pipe *>(ser, _pipes);
+
+ // Free outdated pointers
+ for (Common::List<Animation *>::iterator i = _anims.begin(); i != _anims.end(); i++) {
+ delete *i;
+ }
+
+ syncList<Animation *>(ser, _anims);
+ syncList<Sprite>(ser, _sprites);
+
+ _dirtyRects.clear();
+
+ // Redraw the whole screen
+ _dirtyRects.push_back(Common::Rect(0, 0, 640, 480));
+ byte palbuf[256 * 3];
+ ser.syncBytes(palbuf, 256 * 3);
+ _system->getPaletteManager()->setPalette(palbuf, 0, 256);
+ _needsUpdate = true;
+
+ _mixer->stopAll();
+ _audioStream = NULL;
+
+ // Restore the buffered audio
+ ser.syncAsSint16LE(_currSoundPriority);
+ int32 numSamples;
+ ser.syncAsSint32LE(numSamples);
+ int16 *audioBuffer = (int16 *)malloc(numSamples * 2);
+ for (int32 i = 0; i < numSamples; i++)
+ ser.syncAsSint16LE(audioBuffer[i]);
+ _audioStream = Audio::makeQueuingAudioStream(22050, false);
+ _audioStream->queueBuffer((byte *)audioBuffer, numSamples * 2, DisposeAfterUse::YES, Audio::FLAG_16BITS);
+ if (!_mixer->isSoundHandleActive(_soundHandle))
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, _audioStream);
+
+
+ // Reset autosave duration on load
+ _lastSaveTime = _system->getMillis();
+
+ return Common::kNoError;
+}
+
+Common::Error ComposerEngine::saveGameState(int slot, const Common::String &desc) {
+ Common::String filename = makeSaveGameName(slot);
+ Common::OutSaveFile *out;
+ _lastSaveTime = _system->getMillis();
+ if (!(out = _saveFileMan->openForSaving(filename)))
+ return Common::kWritingFailed;
+
+ Common::Serializer ser(NULL, out);
+ byte magic[4] = {'C', 'M', 'P', 'S'};
+ ser.syncBytes(magic, 4);
+ ser.syncVersion(0);
+ Common::String desctmp = desc;
+ ser.syncString(desctmp);
+ uint32 tmp = _rnd->getSeed();
+ ser.syncAsUint32LE(tmp);
+ ser.syncAsUint32LE(_currentTime);
+ ser.syncAsUint32LE(_lastTime);
+
+ syncListReverse<Library>(ser, _libraries);
+ ser.syncString(_bookGroup);
+
+ syncArray<PendingPageChange>(ser, _pendingPageChanges);
+ syncArray<uint16>(ser, _stack);
+ syncArray<uint16>(ser, _vars);
+ syncList<OldScript *>(ser, _oldScripts);
+ syncArray<QueuedScript>(ser, _queuedScripts);
+
+ ser.syncAsSint16LE(_lastMousePos.x);
+ ser.syncAsSint16LE(_lastMousePos.y);
+ ser.syncAsByte(_mouseEnabled);
+ ser.syncAsByte(_mouseVisible);
+ ser.syncAsUint16LE(_mouseSpriteId);
+
+ syncListReverse<Pipe *>(ser, _pipes);
+ syncList<Animation *>(ser, _anims);
+ syncList<Sprite>(ser, _sprites);
+
+ byte paletteBuffer[256 * 3];
+ _system->getPaletteManager()->grabPalette(paletteBuffer, 0, 256);
+ ser.syncBytes(paletteBuffer, 256 * 3);
+
+ ser.syncAsSint16LE(_currSoundPriority);
+ int16 audioBuffer[22050];
+ int32 numSamples = _audioStream->readBuffer(audioBuffer, 22050);
+ if (numSamples == -1) numSamples = 0;
+ ser.syncAsSint32LE(numSamples);
+ for (int32 i = 0; i < numSamples; i++)
+ ser.syncAsSint16LE(audioBuffer[i]);
+
+ out->finalize();
+ return Common::kNoError;
+}
+} // End of namespace Composer