From 2a4e9a63588bc462c2a71195bb7597fe81ab0617 Mon Sep 17 00:00:00 2001 From: Marisa-Chan Date: Wed, 5 Mar 2014 16:21:03 +0700 Subject: [PATCH] ZVISION: Refactor of save functions --- engines/zvision/save_manager.cpp | 157 +++++++++++++++++------------ engines/zvision/save_manager.h | 9 +- engines/zvision/script_manager.cpp | 131 +++++++++++++++++++++--- engines/zvision/script_manager.h | 6 +- engines/zvision/timer_node.cpp | 4 +- 5 files changed, 224 insertions(+), 83 deletions(-) diff --git a/engines/zvision/save_manager.cpp b/engines/zvision/save_manager.cpp index e8947d8d168b..15b7424de7a0 100644 --- a/engines/zvision/save_manager.cpp +++ b/engines/zvision/save_manager.cpp @@ -42,44 +42,27 @@ const uint32 SaveManager::SAVEGAME_ID = MKTAG('Z', 'E', 'N', 'G'); void SaveManager::saveGame(uint slot, const Common::String &saveName) { // The games only support 20 slots - assert(slot <= 1 && slot <= 20); + //assert(slot <= 1 && slot <= 20); Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Write version - file->writeByte(SAVE_VERSION); + writeSaveGameHeader(file, saveName); - // Write savegame name - file->writeString(saveName); - file->writeByte(0); + _engine->getScriptManager()->serialize(file); - // We can't call writeGameSaveData because the save menu is actually - // a room, so writeGameSaveData would save us in the save menu. - // However, an auto save is performed before each room change, so we - // can copy the data from there. We can guarantee that an auto save file will - // exist before this is called because the save menu can only be accessed - // after the first room (the main menu) has loaded. - Common::InSaveFile *autoSaveFile = saveFileManager->openForLoading(_engine->generateAutoSaveFileName()); + file->finalize(); + delete file; +} - // Skip over the header info - autoSaveFile->readSint32BE(); // SAVEGAME_ID - autoSaveFile->readByte(); // Version - autoSaveFile->seek(5, SEEK_CUR); // The string "auto" with terminating NULL +void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { + Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); + Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); - // Read the rest to a buffer - uint32 size = autoSaveFile->size() - autoSaveFile->pos(); - byte *buffer = new byte[size]; - autoSaveFile->read(buffer, size); + writeSaveGameHeader(file, saveName); - // Then write the buffer to the new file - file->write(buffer, size); + file->write(stream->getData(), stream->size()); - // Cleanup - delete[] buffer; file->finalize(); delete file; } @@ -87,23 +70,26 @@ void SaveManager::saveGame(uint slot, const Common::String &saveName) { void SaveManager::autoSave() { Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - // Write out the savegame header - file->writeUint32BE(SAVEGAME_ID); - - // Version - file->writeByte(SAVE_VERSION); + writeSaveGameHeader(file, "auto"); - file->writeString("auto"); - file->writeByte(0); - - writeSaveGameData(file); + _engine->getScriptManager()->serialize(file); // Cleanup file->finalize(); delete file; } -void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { +void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { + + file->writeUint32BE(SAVEGAME_ID); + + // Write version + file->writeByte(SAVE_VERSION); + + // Write savegame name + file->writeString(saveName); + file->writeByte(0); + // Create a thumbnail and save it Graphics::saveThumbnail(*file); @@ -115,26 +101,13 @@ void SaveManager::writeSaveGameData(Common::OutSaveFile *file) { file->writeSint16LE(td.tm_mday); file->writeSint16LE(td.tm_hour); file->writeSint16LE(td.tm_min); - - ScriptManager *scriptManager = _engine->getScriptManager(); - // Write out the current location - Location currentLocation = scriptManager->getCurrentLocation(); - file->writeByte(currentLocation.world); - file->writeByte(currentLocation.room); - file->writeByte(currentLocation.node); - file->writeByte(currentLocation.view); - file->writeUint32LE(currentLocation.offset); - - // Write out the current state table values - scriptManager->serializeStateTable(file); - } Common::Error SaveManager::loadGame(uint slot) { // The games only support 20 slots - assert(slot <= 1 && slot <= 20); + //assert(slot <= 1 && slot <= 20); - Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + Common::SeekableReadStream *saveFile = getSlotFile(slot); if (saveFile == 0) { return Common::kPathDoesNotExist; } @@ -145,24 +118,59 @@ Common::Error SaveManager::loadGame(uint slot) { return Common::kUnknownError; } - char world = (char)saveFile->readByte(); - char room = (char)saveFile->readByte(); - char node = (char)saveFile->readByte(); - char view = (char)saveFile->readByte(); - uint32 offset = (char)saveFile->readUint32LE(); + ScriptManager *scriptManager = _engine->getScriptManager(); + // Update the state table values + scriptManager->deserialize(saveFile); + + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; + + return Common::kNoError; +} + +Common::Error SaveManager::loadGame(const Common::String &saveName) { + Common::File *saveFile = _engine->getSearchManager()->openFile(saveName); + if (saveFile == NULL) { + saveFile = new Common::File; + if (!saveFile->open(saveName)) { + delete saveFile; + return Common::kPathDoesNotExist; + } + } + + // Read the header + SaveGameHeader header; + if (!readSaveGameHeader(saveFile, header)) { + return Common::kUnknownError; + } ScriptManager *scriptManager = _engine->getScriptManager(); // Update the state table values - scriptManager->deserializeStateTable(saveFile); + scriptManager->deserialize(saveFile); - // Load the room - scriptManager->changeLocation(world, room, node, view, offset); + delete saveFile; + if (header.thumbnail) + delete header.thumbnail; return Common::kNoError; } bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header) { - if (in->readUint32BE() != SAVEGAME_ID) { + uint32 tag = in->readUint32BE(); + if (tag == MKTAG('Z', 'N', 'S', 'G')) { + header.saveYear = 0; + header.saveMonth = 0; + header.saveDay = 0; + header.saveHour = 0; + header.saveMinutes = 0; + header.saveName = "Original Save"; + header.thumbnail = NULL; + header.version = SAVE_ORIGINAL; + in->seek(-4, SEEK_CUR); + return true; + } + if (tag != SAVEGAME_ID) { warning("File is not a ZVision save file. Aborting load"); return false; } @@ -198,4 +206,29 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea return true; } +Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { + Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); + if (saveFile == NULL) { + // Try to load standart save file + Common::String filename; + if (_engine->getGameId() == GID_GRANDINQUISITOR) + filename.format("inqsav%u.sav", slot); + else if (_engine->getGameId() == GID_NEMESIS) + filename.format("nemsav%u.sav", slot); + + saveFile = _engine->getSearchManager()->openFile(filename); + if (saveFile == NULL) { + Common::File *tmpFile = new Common::File; + if (!tmpFile->open(filename)) { + delete tmpFile; + } else { + saveFile = tmpFile; + } + } + + } + + return saveFile; +} + } // End of namespace ZVision diff --git a/engines/zvision/save_manager.h b/engines/zvision/save_manager.h index ec80b37e2076..84afc8cb13e9 100644 --- a/engines/zvision/save_manager.h +++ b/engines/zvision/save_manager.h @@ -24,6 +24,7 @@ #define ZVISION_SAVE_MANAGER_H #include "common/savefile.h" +#include "common/memstream.h" namespace Common { class String; @@ -54,6 +55,7 @@ class SaveManager { static const uint32 SAVEGAME_ID; enum { + SAVE_ORIGINAL = 0, SAVE_VERSION = 1 }; @@ -73,6 +75,7 @@ class SaveManager { * @param saveName The internal name for this save. This is NOT the name of the actual save file. */ void saveGame(uint slot, const Common::String &saveName); + void saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream); /** * Loads the state data from the save file that slot references. Uses * ZVision::generateSaveFileName(slot) to get the save file name. @@ -80,10 +83,12 @@ class SaveManager { * @param slot The save slot to load. Must be [1, 20] */ Common::Error loadGame(uint slot); + Common::Error loadGame(const Common::String &saveName); + Common::SeekableReadStream *getSlotFile(uint slot); + bool readSaveGameHeader(Common::SeekableReadStream *in, SaveGameHeader &header); private: - void writeSaveGameData(Common::OutSaveFile *file); - bool readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &header); + void writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName); }; } // End of namespace ZVision diff --git a/engines/zvision/script_manager.cpp b/engines/zvision/script_manager.cpp index 54d126f8cb70..5e101cb58f77 100644 --- a/engines/zvision/script_manager.cpp +++ b/engines/zvision/script_manager.cpp @@ -30,6 +30,7 @@ #include "zvision/save_manager.h" #include "zvision/actions.h" #include "zvision/utility.h" +#include "zvision/timer_node.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -283,6 +284,13 @@ void ScriptManager::setStateValue(uint32 key, int value) { queuePuzzles(key); } +void ScriptManager::setStateValueSilent(uint32 key, int value) { + if (value == 0) + _globalState.erase(key); + else + _globalState[key] = value; +} + uint ScriptManager::getStateFlag(uint32 key) { if (_globalStateFlags.contains(key)) return _globalStateFlags[key]; @@ -296,6 +304,13 @@ void ScriptManager::setStateFlag(uint32 key, uint value) { _globalStateFlags[key] |= value; } +void ScriptManager::setStateFlagSilent(uint32 key, uint value) { + if (value == 0) + _globalStateFlags.erase(key); + else + _globalStateFlags[key] = value; +} + void ScriptManager::unsetStateFlag(uint32 key, uint value) { queuePuzzles(key); @@ -560,30 +575,114 @@ void ScriptManager::do_changeLocation() { } } -void ScriptManager::serializeStateTable(Common::WriteStream *stream) { - // Write the number of state value entries - stream->writeUint32LE(_globalState.size()); +void ScriptManager::serialize(Common::WriteStream *stream) { + stream->writeUint32BE(MKTAG('Z', 'N', 'S', 'G')); + stream->writeUint32LE(4); + stream->writeUint32LE(0); + stream->writeUint32BE(MKTAG('L', 'O', 'C', ' ')); + stream->writeUint32LE(8); + stream->writeByte(getStateValue(StateKey_World)); + stream->writeByte(getStateValue(StateKey_Room)); + stream->writeByte(getStateValue(StateKey_Node)); + stream->writeByte(getStateValue(StateKey_View)); + stream->writeUint32LE(getStateValue(StateKey_ViewPos)); - for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) { - // Write out the key/value pair - stream->writeUint32LE(iter->_key); - stream->writeUint32LE(iter->_value); - } + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) + (*iter)->serialize(stream); + + stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G')); + + int32 slots = 20000; + if (_engine->getGameId() == GID_NEMESIS) + slots = 30000; + + stream->writeUint32LE(slots * 2); + + for (int32 i = 0; i < slots; i++) + stream->writeUint16LE(getStateFlag(i)); + + stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z')); + + stream->writeUint32LE(slots * 2); + + for (int32 i = 0; i < slots; i++) + stream->writeSint16LE(getStateValue(i)); } -void ScriptManager::deserializeStateTable(Common::SeekableReadStream *stream) { +void ScriptManager::deserialize(Common::SeekableReadStream *stream) { // Clear out the current table values _globalState.clear(); + _globalStateFlags.clear(); - // Read the number of key/value pairs - uint32 numberOfPairs = stream->readUint32LE(); + cleanScriptScope(nodeview); + cleanScriptScope(room); + cleanScriptScope(world); - for (uint32 i = 0; i < numberOfPairs; ++i) { - uint32 key = stream->readUint32LE(); - uint32 value = stream->readUint32LE(); - // Directly access the state table so we don't trigger Puzzle checks - _globalState[key] = value; + _currentLocation.node = 0; + _currentLocation.world = 0; + _currentLocation.room = 0; + _currentLocation.view = 0; + + for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) + delete(*iter); + + _activeSideFx.clear(); + + _referenceTable.clear(); + + if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) { + changeLocation('g', 'a', 'r', 'y', 0); + debug("ZNSG"); + return; } + + stream->seek(4, SEEK_CUR); + + if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) { + changeLocation('g', 'a', 'r', 'y', 0); + debug("LOC"); + return; + } + + Location next_loc; + + next_loc.world = stream->readByte(); + next_loc.room = stream->readByte(); + next_loc.node = stream->readByte(); + next_loc.view = stream->readByte(); + next_loc.offset = stream->readUint32LE() & 0x0000FFFF; + + // What the fck, eos is not 'return pos >= size' + // while (!stream->eos()) {*/ + while (stream->pos() < stream->size()) { + uint32 tag = stream->readUint32BE(); + uint32 tag_size = stream->readUint32LE(); + switch (tag) { + case MKTAG('T', 'I', 'M', 'R'): { + uint32 key = stream->readUint32LE(); + uint32 time = stream->readUint32LE(); + addSideFX(new TimerNode(_engine, key, time)); + } + break; + case MKTAG('F', 'L', 'A', 'G'): + for (uint32 i = 0; i < tag_size / 2; i++) + setStateFlagSilent(i, stream->readUint16LE()); + break; + case MKTAG('P', 'U', 'Z', 'Z'): + for (uint32 i = 0; i < tag_size / 2; i++) + setStateValueSilent(i, stream->readUint16LE()); + break; + default: + stream->seek(tag_size, SEEK_CUR); + } + } + + _nextLocation = next_loc; + + do_changeLocation(); + // Place for read prefs + _engine->setRenderDelay(10); + setStateValue(StateKey_RestoreFlag, 1); } Location ScriptManager::getCurrentLocation() const { diff --git a/engines/zvision/script_manager.h b/engines/zvision/script_manager.h index 2f585b629174..6adade574577 100644 --- a/engines/zvision/script_manager.h +++ b/engines/zvision/script_manager.h @@ -230,8 +230,8 @@ class ScriptManager { /** Mark next location */ void changeLocation(char world, char room, char node, char view, uint32 offset); - void serializeStateTable(Common::WriteStream *stream); - void deserializeStateTable(Common::SeekableReadStream *stream); + void serialize(Common::WriteStream *stream); + void deserialize(Common::SeekableReadStream *stream); Location getCurrentLocation() const; @@ -253,6 +253,8 @@ class ScriptManager { int16 invertory_getItem(int8 id); void invertory_setItem(int8 id, int16 item); + void setStateFlagSilent(uint32 key, uint value); + void setStateValueSilent(uint32 key, int value); public: void invertory_add(int16 item); diff --git a/engines/zvision/timer_node.cpp b/engines/zvision/timer_node.cpp index f8da0bcf371f..0d7f9b111401 100644 --- a/engines/zvision/timer_node.cpp +++ b/engines/zvision/timer_node.cpp @@ -67,8 +67,10 @@ bool TimerNode::stop() { } void TimerNode::serialize(Common::WriteStream *stream) { + stream->writeUint32BE(MKTAG('T', 'I', 'M', 'R')); + stream->writeUint32LE(8); // size stream->writeUint32LE(_key); - stream->writeUint32LE(_timeLeft); + stream->writeUint32LE(_timeLeft / (_engine->getGameId() == GID_NEMESIS ? 1000 : 100)); } void TimerNode::deserialize(Common::SeekableReadStream *stream) {