diff options
author | Eugene Sandulenko | 2016-11-03 23:51:18 +0100 |
---|---|---|
committer | GitHub | 2016-11-03 23:51:18 +0100 |
commit | 6cad083f84ed3ab5b1cd1bc054308ce26930d6c4 (patch) | |
tree | 7223e6d939a839275e40b3cba862402b93e80efc | |
parent | 869354830fe8fe8fedd0829cdc7e99b1bd82d6b7 (diff) | |
parent | ec06c04faa7a14e3586849a670ff8db71d43a7ca (diff) | |
download | scummvm-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.cpp | 14 | ||||
-rw-r--r-- | engines/composer/composer.h | 19 | ||||
-rw-r--r-- | engines/composer/detection.cpp | 50 | ||||
-rw-r--r-- | engines/composer/graphics.cpp | 65 | ||||
-rw-r--r-- | engines/composer/graphics.h | 1 | ||||
-rw-r--r-- | engines/composer/module.mk | 1 | ||||
-rw-r--r-- | engines/composer/resource.cpp | 21 | ||||
-rw-r--r-- | engines/composer/resource.h | 12 | ||||
-rw-r--r-- | engines/composer/saveload.cpp | 428 |
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 §ion, const Common::String &key); Common::String getFilename(const Common::String §ion, 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 |