aboutsummaryrefslogtreecommitdiff
path: root/engines/composer/saveload.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/composer/saveload.cpp')
-rw-r--r--engines/composer/saveload.cpp428
1 files changed, 428 insertions, 0 deletions
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