From 49de1602b0ddc87cbbf26181f83b3d41b253da65 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Fri, 22 Dec 2017 08:52:31 -0500 Subject: XEEN: Create a separate current state saver for each side Previously, I only had a single savefile, which maintains the state of the party and mazes. But I've realised that I'll need a separate archive for each side of Xeen. I'm still not entirely happy with the cleanliness of the new structure, but it at least is now functionally separating the sides. --- engines/xeen/detection.cpp | 4 +- engines/xeen/files.cpp | 139 ++++++++++++++++++++++++- engines/xeen/files.h | 93 +++++++++++++---- engines/xeen/items.h | 34 ------- engines/xeen/map.cpp | 12 +-- engines/xeen/party.cpp | 12 +-- engines/xeen/party.h | 1 - engines/xeen/saves.cpp | 247 ++++++++++++++++++++++++--------------------- engines/xeen/saves.h | 74 +++++++------- engines/xeen/spells.cpp | 2 +- engines/xeen/xeen.cpp | 124 +---------------------- engines/xeen/xeen.h | 22 ---- 12 files changed, 395 insertions(+), 369 deletions(-) delete mode 100644 engines/xeen/items.h diff --git a/engines/xeen/detection.cpp b/engines/xeen/detection.cpp index 0866d5f107..df3df4ffa7 100644 --- a/engines/xeen/detection.cpp +++ b/engines/xeen/detection.cpp @@ -144,7 +144,7 @@ SaveStateList XeenMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { - Xeen::XeenEngine::readSavegameHeader(in, header); + Xeen::SavesManager::readSavegameHeader(in, header); saveList.push_back(SaveStateDescriptor(slot, header._saveName)); header._thumbnail->free(); @@ -172,7 +172,7 @@ SaveStateDescriptor XeenMetaEngine::querySaveMetaInfos(const char *target, int s if (f) { Xeen::XeenSavegameHeader header; - Xeen::XeenEngine::readSavegameHeader(f, header); + Xeen::SavesManager::readSavegameHeader(f, header); delete f; // Create the return descriptor diff --git a/engines/xeen/files.cpp b/engines/xeen/files.cpp index b62a0d8cd5..d63c63d651 100644 --- a/engines/xeen/files.cpp +++ b/engines/xeen/files.cpp @@ -186,8 +186,6 @@ Common::SeekableReadStream *CCArchive::createReadStreamForMember(const Common::S FileManager::FileManager(XeenEngine *vm) { Common::File f; - int sideNum = 0; - _isDarkCc = vm->getGameID() == GType_DarkSide; File::_xeenCc = (vm->getGameID() == GType_DarkSide) ? nullptr : @@ -220,9 +218,12 @@ void FileManager::setGameCc(int ccMode) { /*------------------------------------------------------------------------*/ -CCArchive *File::_currentArchive; CCArchive *File::_xeenCc; CCArchive *File::_darkCc; +SaveArchive *File::_xeenSave; +SaveArchive *File::_darkSave; +BaseCCArchive *File::_currentArchive; +SaveArchive *File::_currentSave; File::File(const Common::String &filename) { File::open(filename); @@ -237,7 +238,7 @@ File::File(const Common::String &filename, int ccMode) { } bool File::open(const Common::String &filename) { - if (!g_vm->_saves || !Common::File::open(filename, *g_vm->_saves)) { + if (!_currentSave || !Common::File::open(filename, *_currentSave)) { if (!Common::File::open(filename, *_currentArchive)) { // Could not find in current archive, so try intro.cc or in folder if (!Common::File::open(filename)) @@ -269,10 +270,12 @@ void File::setCurrentArchive(int ccMode) { switch (ccMode) { case 0: _currentArchive = _xeenCc; + _currentSave = _xeenSave; break; case 1: _currentArchive = _darkCc; + _currentSave = _darkSave; break; default: @@ -293,7 +296,7 @@ Common::String File::readString() { } bool File::exists(const Common::String &filename) { - if (!g_vm->_saves || !g_vm->_saves->hasFile(filename)) { + if (!_currentSave || !_currentSave->hasFile(filename)) { if (!_currentArchive->hasFile(filename)) { // Could not find in current archive, so try intro.cc or in folder return Common::File::exists(filename); @@ -314,6 +317,28 @@ bool File::exists(const Common::String &filename, int ccMode) { return result; } +void File::syncBitFlags(Common::Serializer &s, bool *startP, bool *endP) { + byte data = 0; + + int bitCounter = 0; + for (bool *p = startP; p <= endP; ++p, bitCounter = (bitCounter + 1) % 8) { + if (p == endP || bitCounter == 0) { + if (p != endP || s.isSaving()) + s.syncAsByte(data); + if (p == endP) + break; + + if (s.isSaving()) + data = 0; + } + + if (s.isLoading()) + *p = (data >> bitCounter) != 0; + else if (*p) + data |= 1 << bitCounter; + } +} + /*------------------------------------------------------------------------*/ void StringArray::load(const Common::String &name) { @@ -330,4 +355,108 @@ void StringArray::load(const Common::String &name, int ccMode) { push_back(f.readString()); } +/*------------------------------------------------------------------------*/ + +SaveArchive::SaveArchive(Party *party) : BaseCCArchive(), _party(party) { + _data = nullptr; +} + +SaveArchive::~SaveArchive() { + for (Common::HashMap::iterator it = _newData.begin(); it != _newData.end(); it++) { + delete (*it)._value; + } + delete[] _data; +} + +Common::SeekableReadStream *SaveArchive::createReadStreamForMember(const Common::String &name) const { + CCEntry ccEntry; + + // If the given resource has already been perviously "written" to the + // save manager, then return that new resource + uint16 id = BaseCCArchive::convertNameToId(name); + if (_newData.contains(id)) { + Common::MemoryWriteStreamDynamic *stream = _newData[id]; + return new Common::MemoryReadStream(stream->getData(), stream->size()); + } + + // Retrieve the resource from the loaded savefile + if (getHeaderEntry(name, ccEntry)) { + // Open the correct CC entry + return new Common::MemoryReadStream(_data + ccEntry._offset, ccEntry._size); + } + + return nullptr; +} + +void SaveArchive::load(Common::SeekableReadStream *stream) { + loadIndex(stream); + + delete[] _data; + _data = new byte[stream->size()]; + stream->seek(0); + stream->read(_data, stream->size()); + + // Load in the character stats and active party + Common::SeekableReadStream *chr = createReadStreamForMember("maze.chr"); + Common::Serializer sChr(chr, nullptr); + _party->_roster.synchronize(sChr); + delete chr; + + Common::SeekableReadStream *pty = createReadStreamForMember("maze.pty"); + Common::Serializer sPty(pty, nullptr); + _party->synchronize(sPty); + delete pty; +} + +void SaveArchive::reset(CCArchive *src) { + Common::MemoryWriteStreamDynamic saveFile(DisposeAfterUse::YES); + File fIn; + + g_vm->_files->setGameCc(g_vm->getGameID() == GType_DarkSide ? 1 : 0); + const int RESOURCES[6] = { 0x2A0C, 0x2A1C, 0x2A2C, 0x2A3C, 0x284C, 0x2A5C }; + for (int i = 0; i < 6; ++i) { + Common::String filename = Common::String::format("%.4x", RESOURCES[i]); + if (src->hasFile(filename)) { + // Read in the next resource + fIn.open(filename, *src); + byte *data = new byte[fIn.size()]; + fIn.read(data, fIn.size()); + + // Copy it to the combined savefile resource + saveFile.write(data, fIn.size()); + delete[] data; + fIn.close(); + } + } + + assert(saveFile.size() > 0); + Common::MemoryReadStream f(saveFile.getData(), saveFile.size()); + load(&f); +} + +/*------------------------------------------------------------------------*/ + +OutFile::OutFile(const Common::String filename) : + _filename(filename), _backingStream(DisposeAfterUse::YES) { + _archive = File::_currentSave; +} + +uint32 OutFile::write(const void *dataPtr, uint32 dataSize) { + return _backingStream.write(dataPtr, dataSize); +} + +int32 OutFile::pos() const { + return _backingStream.pos(); +} + +void OutFile::finalize() { + uint16 id = BaseCCArchive::convertNameToId(_filename); + + if (!_archive->_newData.contains(id)) + _archive->_newData[id] = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); + + Common::MemoryWriteStreamDynamic *out = _archive->_newData[id]; + out->write(_backingStream.getData(), _backingStream.size()); +} + } // End of namespace Xeen diff --git a/engines/xeen/files.h b/engines/xeen/files.h index 1fcfa6a7b8..f18d23671a 100644 --- a/engines/xeen/files.h +++ b/engines/xeen/files.h @@ -26,6 +26,8 @@ #include "common/scummsys.h" #include "common/array.h" #include "common/file.h" +#include "common/memstream.h" +#include "common/savefile.h" #include "common/serializer.h" #include "common/str-array.h" #include "graphics/surface.h" @@ -34,7 +36,12 @@ namespace Xeen { class XeenEngine; class CCArchive; +class BaseCCArchive; class File; +class SaveArchive; +class Party; +class OutFile; +class SavesManager; #define SYNC_AS(SUFFIX,STREAM,TYPE,SIZE) \ template \ @@ -50,6 +57,20 @@ class File; _bytesSynced += SIZE; \ } +/** + * Details of a single entry in a CC file index + */ +struct CCEntry { + uint16 _id; + uint32 _offset; + uint16 _size; + + CCEntry() : _id(0), _offset(0), _size(0) {} + CCEntry(uint16 id, uint32 offset, uint32 size) + : _id(id), _offset(offset), _size(size) { + } +}; + /* * Main resource manager */ @@ -79,15 +100,23 @@ public: */ class File : public Common::File { friend class FileManager; + friend class OutFile; + friend class SavesManager; private: - static CCArchive *_currentArchive; - static CCArchive *_xeenCc; - static CCArchive *_darkCc; + static CCArchive *_xeenCc, *_darkCc; + static SaveArchive *_xeenSave, *_darkSave; + static BaseCCArchive *_currentArchive; + static SaveArchive *_currentSave; public: /** * Sets which archive is used by default */ static void setCurrentArchive(int ccMode); + + /** + * Synchronizes a boolean array as a bitfield set + */ + static void syncBitFlags(Common::Serializer &s, bool *startP, bool *endP); public: File() : Common::File() {} File(const Common::String &filename); @@ -175,22 +204,8 @@ public: }; /** -* Details of a single entry in a CC file index -*/ -struct CCEntry { - uint16 _id; - uint32 _offset; - uint16 _size; - - CCEntry() : _id(0), _offset(0), _size(0) {} - CCEntry(uint16 id, uint32 offset, uint32 size) - : _id(id), _offset(offset), _size(size) { - } -}; - -/** -* Base Xeen CC file implementation -*/ + * Base Xeen CC file implementation + */ class BaseCCArchive : public Common::Archive { protected: Common::Array _index; @@ -220,8 +235,8 @@ public: }; /** -* Xeen CC file implementation -*/ + * Xeen CC file implementation + */ class CCArchive : public BaseCCArchive { private: Common::String _filename; @@ -238,6 +253,42 @@ public: virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; }; +class SaveArchive : public BaseCCArchive { + friend class OutFile; +private: + Party *_party; + byte *_data; + Common::HashMap _newData; + + void load(Common::SeekableReadStream *stream); +public: + SaveArchive(Party *party); + ~SaveArchive(); + + /** + * Sets up the dynamic data for the game for a new game + */ + void reset(CCArchive *src); + + // Archive implementation + virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; +}; + +class OutFile : public Common::WriteStream { +private: + SaveArchive *_archive; + Common::String _filename; + Common::MemoryWriteStreamDynamic _backingStream; +public: + OutFile(const Common::String filename); + + void finalize(); + + uint32 write(const void *dataPtr, uint32 dataSize) override; + + int32 pos() const override; +}; + } // End of namespace Xeen #endif /* XEEN_FILES_H */ diff --git a/engines/xeen/items.h b/engines/xeen/items.h deleted file mode 100644 index bfbd9e4481..0000000000 --- a/engines/xeen/items.h +++ /dev/null @@ -1,34 +0,0 @@ -/* 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. - * - */ - -#ifndef XEEN_ITEMS_H -#define XEEN_ITEMS_H - -#include "xeen/character.h" - -namespace Xeen { - - - -} // End of namespace Xeen - -#endif /* XEEN_ITEMS_H */ diff --git a/engines/xeen/map.cpp b/engines/xeen/map.cpp index 6777178d31..6a442be247 100644 --- a/engines/xeen/map.cpp +++ b/engines/xeen/map.cpp @@ -620,9 +620,9 @@ void MazeData::synchronize(Common::SeekableReadStream &s) { Common::Serializer ser(&s, nullptr); for (int y = 0; y < MAP_HEIGHT; ++y) - SavesManager::syncBitFlags(ser, &_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH]); + File::syncBitFlags(ser, &_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH]); for (int y = 0; y < MAP_HEIGHT; ++y) - SavesManager::syncBitFlags(ser, &_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH]); + File::syncBitFlags(ser, &_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH]); } void MazeData::setAllTilesStepped() { @@ -1115,7 +1115,7 @@ void Map::load(int mapId) { mapId = party._mazeId; Common::String filename = Common::String::format("maze%c%03d.mob", (mapId >= 100) ? 'x' : '0', mapId); - File mobFile(filename, *_vm->_saves); + File mobFile(filename); XeenSerializer sMob(&mobFile, nullptr); _mobData.synchronize(sMob, _monsterData); mobFile.close(); @@ -1395,7 +1395,7 @@ void Map::loadEvents(int mapId) { // Load events Common::String filename = Common::String::format("maze%c%03d.evt", (mapId >= 100) ? 'x' : '0', mapId); - File fEvents(filename, *_vm->_saves); + File fEvents(filename); XeenSerializer sEvents(&fEvents, nullptr); _events.synchronize(sEvents); fEvents.close(); @@ -1418,7 +1418,7 @@ void Map::saveMaze() { // Save the event data Common::String filename = Common::String::format("maze%c%03d.evt", (mazeNum >= 100) ? 'x' : '0', mazeNum); - OutFile fEvents(_vm, filename); + OutFile fEvents(filename); XeenSerializer sEvents(nullptr, &fEvents); _events.synchronize(sEvents); fEvents.finalize(); @@ -1426,7 +1426,7 @@ void Map::saveMaze() { // Save the maze MOB file filename = Common::String::format("maze%c%03d.mob", (mazeNum >= 100) ? 'x' : '0', mazeNum); - OutFile fMob(_vm, filename); + OutFile fMob(filename); XeenSerializer sMob(nullptr, &fEvents); _mobData.synchronize(sMob, _monsterData); fEvents.finalize(); diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index abedaa46ed..0a2e5b9f82 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -206,11 +206,11 @@ void Party::synchronize(Common::Serializer &s) { s.syncAsUint32LE(_bankGems); s.syncAsUint32LE(_totalTime); s.syncAsByte(_rested); - SavesManager::syncBitFlags(s, &_gameFlags[0][0], &_gameFlags[0][256]); - SavesManager::syncBitFlags(s, &_gameFlags[1][0], &_gameFlags[1][256]); - SavesManager::syncBitFlags(s, &_worldFlags[0], &_worldFlags[128]); - SavesManager::syncBitFlags(s, &_questFlags[0][0], &_questFlags[0][30]); - SavesManager::syncBitFlags(s, &_questFlags[1][0], &_questFlags[1][30]); + File::syncBitFlags(s, &_gameFlags[0][0], &_gameFlags[0][256]); + File::syncBitFlags(s, &_gameFlags[1][0], &_gameFlags[1][256]); + File::syncBitFlags(s, &_worldFlags[0], &_worldFlags[128]); + File::syncBitFlags(s, &_questFlags[0][0], &_questFlags[0][30]); + File::syncBitFlags(s, &_questFlags[1][0], &_questFlags[1][30]); for (int i = 0; i < 85; ++i) s.syncAsByte(_questItems[i]); @@ -225,7 +225,7 @@ void Party::synchronize(Common::Serializer &s) { _blacksmithMisc[1][i].synchronize(s); for (int i = 0; i < TOTAL_CHARACTERS; ++i) - SavesManager::syncBitFlags(s, &_characterFlags[i][0], &_characterFlags[i][24]); + File::syncBitFlags(s, &_characterFlags[i][0], &_characterFlags[i][24]); s.syncBytes(&dummy[0], 30); } diff --git a/engines/xeen/party.h b/engines/xeen/party.h index f6df5ff83e..31feaec503 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -30,7 +30,6 @@ #include "xeen/character.h" #include "xeen/combat.h" #include "xeen/dialogs_error.h" -#include "xeen/items.h" namespace Xeen { diff --git a/engines/xeen/saves.cpp b/engines/xeen/saves.cpp index 5126642df1..504bb18dd7 100644 --- a/engines/xeen/saves.cpp +++ b/engines/xeen/saves.cpp @@ -23,155 +23,176 @@ #include "common/scummsys.h" #include "common/algorithm.h" #include "common/memstream.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" #include "xeen/saves.h" #include "xeen/files.h" #include "xeen/xeen.h" namespace Xeen { -OutFile::OutFile(XeenEngine *vm, const Common::String filename) : - _vm(vm), - _filename(filename), - _backingStream(DisposeAfterUse::YES) { -} +SavesManager::SavesManager(const Common::String &targetName): _targetName(targetName), + _wonWorld(false), _wonDarkSide(false) { + File::_xeenSave = nullptr; + File::_darkSave = nullptr; -uint32 OutFile::write(const void *dataPtr, uint32 dataSize) { - return _backingStream.write(dataPtr, dataSize); -} + if (g_vm->getGameID() != GType_Clouds) { + File::_darkSave = new SaveArchive(g_vm->_party); + File::_darkSave->reset(File::_darkCc); + } + if (g_vm->getGameID() != GType_DarkSide) { + File::_xeenSave = new SaveArchive(g_vm->_party); + File::_xeenSave->reset(File::_xeenCc); + } -int32 OutFile::pos() const { - return _backingStream.pos(); -} + File::_currentSave = g_vm->getGameID() == GType_DarkSide ? + File::_darkSave : File::_xeenSave; + assert(File::_currentSave); -void OutFile::finalize() { - uint16 id = BaseCCArchive::convertNameToId(_filename); + // Set any final initial values + Party &party = *g_vm->_party; + party.resetBlacksmithWares(); + party._year = g_vm->getGameID() == GType_WorldOfXeen ? 610 : 850; + party._totalTime = 0; +} - if (!_vm->_saves->_newData.contains(id)) - _vm->_saves->_newData[id] = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); +SavesManager::~SavesManager() { + delete File::_xeenSave; + delete File::_darkSave; +} - Common::MemoryWriteStreamDynamic *out = _vm->_saves->_newData[id]; - out->write(_backingStream.getData(), _backingStream.size()); +void SavesManager::readCharFile() { + warning("TODO: readCharFile"); } -/*------------------------------------------------------------------------*/ +void SavesManager::writeCharFile() { + warning("TODO: writeCharFile"); +} -SavesManager::SavesManager(XeenEngine *vm, Party &party) : - BaseCCArchive(), _vm(vm), _party(party) { - _data = nullptr; - _wonWorld = false; - _wonDarkSide = false; +void SavesManager::saveChars() { + warning("TODO: saveChars"); } -SavesManager::~SavesManager() { - for (Common::HashMap::iterator it = _newData.begin(); it != _newData.end(); it++) { - delete (*it)._value; - } - delete[] _data; +const char *const SAVEGAME_STR = "XEEN"; +#define SAVEGAME_STR_SIZE 6 + +bool SavesManager::readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header) { + char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; + header._thumbnail = nullptr; + + // Validate the header Id + in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); + if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) + return false; + + header._version = in->readByte(); + if (header._version > XEEN_SAVEGAME_VERSION) + return false; + + // Read in the string + header._saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header._saveName += ch; + + // Get the thumbnail + header._thumbnail = Graphics::loadThumbnail(*in); + if (!header._thumbnail) + return false; + + // Read in save date/time + header._year = in->readSint16LE(); + header._month = in->readSint16LE(); + header._day = in->readSint16LE(); + header._hour = in->readSint16LE(); + header._minute = in->readSint16LE(); + header._totalFrames = in->readUint32LE(); + + return true; } -void SavesManager::syncBitFlags(Common::Serializer &s, bool *startP, bool *endP) { - byte data = 0; - - int bitCounter = 0; - for (bool *p = startP; p <= endP; ++p, bitCounter = (bitCounter + 1) % 8) { - if (p == endP || bitCounter == 0) { - if (p != endP || s.isSaving()) - s.syncAsByte(data); - if (p == endP) - break; - - if (s.isSaving()) - data = 0; - } - - if (s.isLoading()) - *p = (data >> bitCounter) != 0; - else if (*p) - data |= 1 << bitCounter; - } +void SavesManager::writeSavegameHeader(Common::OutSaveFile *out, XeenSavegameHeader &header) { + // Write out a savegame header + out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); + + out->writeByte(XEEN_SAVEGAME_VERSION); + + // Write savegame name + out->writeString(header._saveName); + out->writeByte('\0'); + + // Write a thumbnail of the screen + /* + uint8 thumbPalette[768]; + _screen->getPalette(thumbPalette); + Graphics::Surface saveThumb; + ::createThumbnail(&saveThumb, (const byte *)_screen->getPixels(), + _screen->w, _screen->h, thumbPalette); + Graphics::saveThumbnail(*out, saveThumb); + saveThumb.free(); + */ + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); + // out->writeUint32LE(_events->getFrameCounter()); } -Common::SeekableReadStream *SavesManager::createReadStreamForMember(const Common::String &name) const { - CCEntry ccEntry; +Common::Error SavesManager::saveGameState(int slot, const Common::String &desc) { + Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving( + generateSaveName(slot)); + if (!out) + return Common::kCreatingFileFailed; - // If the given resource has already been perviously "written" to the - // save manager, then return that new resource - uint16 id = BaseCCArchive::convertNameToId(name); - if (_newData.contains(id)) { - Common::MemoryWriteStreamDynamic *stream = _newData[id]; - return new Common::MemoryReadStream(stream->getData(), stream->size()); - } + XeenSavegameHeader header; + header._saveName = desc; + writeSavegameHeader(out, header); - // Retrieve the resource from the loaded savefile - if (getHeaderEntry(name, ccEntry)) { - // Open the correct CC entry - return new Common::MemoryReadStream(_data + ccEntry._offset, ccEntry._size); - } + Common::Serializer s(nullptr, out); + synchronize(s); - return nullptr; -} + out->finalize(); + delete out; -void SavesManager::load(Common::SeekableReadStream *stream) { - loadIndex(stream); + return Common::kNoError; +} - delete[] _data; - _data = new byte[stream->size()]; - stream->seek(0); - stream->read(_data, stream->size()); +Common::Error SavesManager::loadGameState(int slot) { + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading( + generateSaveName(slot)); + if (!saveFile) + return Common::kReadingFailed; - // Load in the character stats and active party - Common::SeekableReadStream *chr = createReadStreamForMember("maze.chr"); - Common::Serializer sChr(chr, nullptr); - _party._roster.synchronize(sChr); - delete chr; + Common::Serializer s(saveFile, nullptr); - Common::SeekableReadStream *pty = createReadStreamForMember("maze.pty"); - Common::Serializer sPty(pty, nullptr); - _party.synchronize(sPty); - delete pty; -} + // Load the savaegame header + XeenSavegameHeader header; + if (!readSavegameHeader(saveFile, header)) + error("Invalid savegame"); -void SavesManager::reset() { - Common::MemoryWriteStreamDynamic saveFile(DisposeAfterUse::YES); - File fIn; - - g_vm->_files->setGameCc(g_vm->getGameID() == GType_DarkSide ? 1 : 0); - const int RESOURCES[6] = { 0x2A0C, 0x2A1C, 0x2A2C, 0x2A3C, 0x284C, 0x2A5C }; - for (int i = 0; i < 6; ++i) { - Common::String filename = Common::String::format("%.4x", RESOURCES[i]); - if (fIn.exists(filename)) { - // Read in the next resource - fIn.open(filename); - byte *data = new byte[fIn.size()]; - fIn.read(data, fIn.size()); - - // Copy it to the combined savefile resource - saveFile.write(data, fIn.size()); - delete[] data; - fIn.close(); - } + if (header._thumbnail) { + header._thumbnail->free(); + delete header._thumbnail; } - assert(saveFile.size() > 0); - Common::MemoryReadStream f(saveFile.getData(), saveFile.size()); - load(&f); + // Load most of the savegame data + synchronize(s); + delete saveFile; - // Set any final initial values - _party.resetBlacksmithWares(); - _party._year = _vm->getGameID() == GType_WorldOfXeen ? 610 : 850; - _party._totalTime = 0; + return Common::kNoError; } -void SavesManager::readCharFile() { - warning("TODO: readCharFile"); +Common::String SavesManager::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); } -void SavesManager::writeCharFile() { - warning("TODO: writeCharFile"); +void SavesManager::synchronize(Common::Serializer &s) { + // TODO } -void SavesManager::saveChars() { - warning("TODO: saveChars"); -} } // End of namespace Xeen diff --git a/engines/xeen/saves.h b/engines/xeen/saves.h index 9c161c838c..2711cc7f2c 100644 --- a/engines/xeen/saves.h +++ b/engines/xeen/saves.h @@ -24,11 +24,11 @@ #define XEEN_SAVES_H #include "common/scummsys.h" -#include "common/memstream.h" #include "common/savefile.h" +#include "common/serializer.h" +#include "common/str.h" #include "graphics/surface.h" #include "xeen/party.h" -#include "xeen/files.h" namespace Xeen { @@ -41,59 +41,57 @@ struct XeenSavegameHeader { int _totalFrames; }; -class XeenEngine; -class SavesManager; - -class OutFile : public Common::WriteStream { +class SavesManager { private: - XeenEngine *_vm; - Common::String _filename; - Common::MemoryWriteStreamDynamic _backingStream; -public: - OutFile(XeenEngine *vm, const Common::String filename); - - void finalize(); - - uint32 write(const void *dataPtr, uint32 dataSize) override; - - int32 pos() const override; -}; - -class SavesManager: public BaseCCArchive { - friend class OutFile; + Common::String _targetName; private: - XeenEngine *_vm; - Party &_party; - byte *_data; - Common::HashMap _newData; + /** + * Synchronize savegame data + */ + void synchronize(Common::Serializer &s); - void load(Common::SeekableReadStream *stream); -public: /** - * Synchronizes a boolean array as a bitfield set + * Support method that generates a savegame name + * @param slot Slot number */ - static void syncBitFlags(Common::Serializer &s, bool *startP, bool *endP); + Common::String generateSaveName(int slot); + + /** + * Initializes a new savegame + */ + void reset(); public: bool _wonWorld; bool _wonDarkSide; public: - SavesManager(XeenEngine *vm, Party &party); - + SavesManager(const Common::String &targetName); ~SavesManager(); - /** - * Sets up the dynamic data for the game for a new game - */ - void reset(); - void readCharFile(); void writeCharFile(); void saveChars(); - // Archive implementation - virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + /** + * Read in a savegame header + */ + static bool readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header); + + /** + * Write out a savegame header + */ + void writeSavegameHeader(Common::OutSaveFile *out, XeenSavegameHeader &header); + + /** + * Load a savegame + */ + Common::Error loadGameState(int slot); + + /** + * Save the game + */ + Common::Error saveGameState(int slot, const Common::String &desc); }; } // End of namespace Xeen diff --git a/engines/xeen/spells.cpp b/engines/xeen/spells.cpp index 255e5ceb55..53303c384b 100644 --- a/engines/xeen/spells.cpp +++ b/engines/xeen/spells.cpp @@ -36,7 +36,7 @@ Spells::Spells(XeenEngine *vm) : _vm(vm) { } void Spells::load() { - File f1("spells.xen"); + File f1("spells.xen", 1); while (f1.pos() < f1.size()) _spellNames.push_back(f1.readString()); f1.close(); diff --git a/engines/xeen/xeen.cpp b/engines/xeen/xeen.cpp index 39e5f4b7b5..9a0e42e44c 100644 --- a/engines/xeen/xeen.cpp +++ b/engines/xeen/xeen.cpp @@ -25,8 +25,6 @@ #include "common/debug-channels.h" #include "common/events.h" #include "engines/util.h" -#include "graphics/scaler.h" -#include "graphics/thumbnail.h" #include "xeen/xeen.h" #include "xeen/files.h" #include "xeen/resources.h" @@ -99,14 +97,14 @@ void XeenEngine::initialize() { _locations = new LocationManager(); _map = new Map(this); _party = new Party(this); - _saves = new SavesManager(this, *_party); + _saves = new SavesManager(_targetName); _screen = new Screen(this); _scripts = new Scripts(this); _sound = new Sound(this, _mixer); _spells = new Spells(this); _windows = new Windows(); - File f("029.obj"); + File f("029.obj", 1); _eventData = f.readStream(f.size()); // Set graphics mode @@ -137,51 +135,11 @@ int XeenEngine::getRandomNumber(int minNumber, int maxNumber) { } Common::Error XeenEngine::saveGameState(int slot, const Common::String &desc) { - Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving( - generateSaveName(slot)); - if (!out) - return Common::kCreatingFileFailed; - - XeenSavegameHeader header; - header._saveName = desc; - writeSavegameHeader(out, header); - - Common::Serializer s(nullptr, out); - synchronize(s); - - out->finalize(); - delete out; - - return Common::kNoError; + return _saves->saveGameState(slot, desc); } Common::Error XeenEngine::loadGameState(int slot) { - Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading( - generateSaveName(slot)); - if (!saveFile) - return Common::kReadingFailed; - - Common::Serializer s(saveFile, nullptr); - - // Load the savaegame header - XeenSavegameHeader header; - if (!readSavegameHeader(saveFile, header)) - error("Invalid savegame"); - - if (header._thumbnail) { - header._thumbnail->free(); - delete header._thumbnail; - } - - // Load most of the savegame data - synchronize(s); - delete saveFile; - - return Common::kNoError; -} - -Common::String XeenEngine::generateSaveName(int slot) { - return Common::String::format("%s.%03d", _targetName.c_str(), slot); + return _saves->loadGameState(slot); } bool XeenEngine::canLoadGameStateCurrently() { @@ -192,81 +150,7 @@ bool XeenEngine::canSaveGameStateCurrently() { return true; } -void XeenEngine::synchronize(Common::Serializer &s) { - // TODO -} - -const char *const SAVEGAME_STR = "XEEN"; -#define SAVEGAME_STR_SIZE 6 - -bool XeenEngine::readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header) { - char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; - header._thumbnail = nullptr; - - // Validate the header Id - in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); - if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) - return false; - - header._version = in->readByte(); - if (header._version > XEEN_SAVEGAME_VERSION) - return false; - - // Read in the string - header._saveName.clear(); - char ch; - while ((ch = (char)in->readByte()) != '\0') - header._saveName += ch; - - // Get the thumbnail - header._thumbnail = Graphics::loadThumbnail(*in); - if (!header._thumbnail) - return false; - - // Read in save date/time - header._year = in->readSint16LE(); - header._month = in->readSint16LE(); - header._day = in->readSint16LE(); - header._hour = in->readSint16LE(); - header._minute = in->readSint16LE(); - header._totalFrames = in->readUint32LE(); - - return true; -} - -void XeenEngine::writeSavegameHeader(Common::OutSaveFile *out, XeenSavegameHeader &header) { - // Write out a savegame header - out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); - - out->writeByte(XEEN_SAVEGAME_VERSION); - - // Write savegame name - out->writeString(header._saveName); - out->writeByte('\0'); - - // Write a thumbnail of the screen -/* - uint8 thumbPalette[768]; - _screen->getPalette(thumbPalette); - Graphics::Surface saveThumb; - ::createThumbnail(&saveThumb, (const byte *)_screen->getPixels(), - _screen->w, _screen->h, thumbPalette); - Graphics::saveThumbnail(*out, saveThumb); - saveThumb.free(); -*/ - // Write out the save date/time - TimeDate td; - g_system->getTimeAndDate(td); - out->writeSint16LE(td.tm_year + 1900); - out->writeSint16LE(td.tm_mon + 1); - out->writeSint16LE(td.tm_mday); - out->writeSint16LE(td.tm_hour); - out->writeSint16LE(td.tm_min); -// out->writeUint32LE(_events->getFrameCounter()); -} - void XeenEngine::playGame() { - _saves->reset(); _files->setGameCc(0); _sound->stopAllAudio(); diff --git a/engines/xeen/xeen.h b/engines/xeen/xeen.h index 022ad6673a..afbec4a7f9 100644 --- a/engines/xeen/xeen.h +++ b/engines/xeen/xeen.h @@ -27,7 +27,6 @@ #include "common/system.h" #include "common/error.h" #include "common/random.h" -#include "common/savefile.h" #include "common/serializer.h" #include "common/util.h" #include "engines/engine.h" @@ -124,17 +123,6 @@ protected: private: void initialize(); - /** - * Synchronize savegame data - */ - void synchronize(Common::Serializer &s); - - /** - * Support method that generates a savegame name - * @param slot Slot number - */ - Common::String generateSaveName(int slot); - // Engine APIs virtual Common::Error run(); virtual bool hasFeature(EngineFeature f) const; @@ -196,16 +184,6 @@ public: */ bool canSaveGameStateCurrently(); - /** - * Read in a savegame header - */ - static bool readSavegameHeader(Common::InSaveFile *in, XeenSavegameHeader &header); - - /** - * Write out a savegame header - */ - void writeSavegameHeader(Common::OutSaveFile *out, XeenSavegameHeader &header); - static Common::String printMil(uint value); static Common::String printK(uint value); -- cgit v1.2.3