scummvm/engines/composer/saveload.cpp
Paul Gilbert 9f175c4053 ENGINES: Cleanup of savegame filenames generation
This removes filename methods when it matched the Engine method.
Secondly, ensuring there was an overriden getSaveStateName method
for engines that didn't do the standard target.00x save filenames
2020-02-16 15:44:28 -08:00

425 lines
14 KiB
C++

/* 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::getSaveStateName(int slot) const {
return Common::String::format("%s.%02d", _targetName.c_str(), slot);
}
Common::Error ComposerEngine::loadGameState(int slot) {
Common::String filename = getSaveStateName(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);
return Common::kNoError;
}
Common::Error ComposerEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
Common::String filename = getSaveStateName(slot);
Common::OutSaveFile *out;
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