From cd54d61de7ebf26ae78aa4adce7d8975ff6a5c5d Mon Sep 17 00:00:00 2001 From: elasota Date: Sat, 9 Jul 2022 01:55:56 -0400 Subject: [PATCH] MTROPOLIS: Add auto-save option and add versioning to save files. --- engines/mtropolis/detection.cpp | 11 +++++++ engines/mtropolis/detection_tables.h | 5 +-- engines/mtropolis/modifiers.cpp | 27 ++++++++-------- engines/mtropolis/modifiers.h | 16 +++++----- engines/mtropolis/mtropolis.cpp | 4 ++- engines/mtropolis/mtropolis.h | 3 ++ engines/mtropolis/plugin/standard.cpp | 4 +-- engines/mtropolis/plugin/standard.h | 4 +-- engines/mtropolis/runtime.cpp | 4 +-- engines/mtropolis/runtime.h | 4 +-- engines/mtropolis/saveload.cpp | 44 ++++++++++++++++++++++++++- engines/mtropolis/saveload.h | 2 +- 12 files changed, 92 insertions(+), 36 deletions(-) diff --git a/engines/mtropolis/detection.cpp b/engines/mtropolis/detection.cpp index e9ea41b8fa7..8280f2867fa 100644 --- a/engines/mtropolis/detection.cpp +++ b/engines/mtropolis/detection.cpp @@ -59,6 +59,17 @@ static const ADExtraGuiOptionsMap optionsList[] = { 0 } }, + { + GAMEOPTION_AUTO_SAVE, + { + _s("Save Progress Automatically"), + _s("Automatically saves the game at certain progress points."), + "mtropolis_mod_auto_save", + true, + 0, + 0 + } + }, { GAMEOPTION_LAUNCH_DEBUG, { diff --git a/engines/mtropolis/detection_tables.h b/engines/mtropolis/detection_tables.h index c84b5d8a308..40a63564187 100644 --- a/engines/mtropolis/detection_tables.h +++ b/engines/mtropolis/detection_tables.h @@ -30,6 +30,7 @@ #define GAMEOPTION_DYNAMIC_MIDI GUIO_GAMEOPTIONS2 #define GAMEOPTION_LAUNCH_DEBUG GUIO_GAMEOPTIONS3 #define GAMEOPTION_LAUNCH_BREAK GUIO_GAMEOPTIONS4 +#define GAMEOPTION_AUTO_SAVE GUIO_GAMEOPTIONS5 namespace MTropolis { @@ -51,7 +52,7 @@ static const MTropolisGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE, - GUIO1(GAMEOPTION_WIDESCREEN_MOD) + GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE) }, GID_OBSIDIAN, 0, @@ -78,7 +79,7 @@ static const MTropolisGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GAMEOPTION_WIDESCREEN_MOD) + GUIO2(GAMEOPTION_WIDESCREEN_MOD, GAMEOPTION_AUTO_SAVE) }, GID_OBSIDIAN, 0, diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp index af7551546ee..cf7451fdd4d 100644 --- a/engines/mtropolis/modifiers.cpp +++ b/engines/mtropolis/modifiers.cpp @@ -34,7 +34,7 @@ class CompoundVarLoader : public ISaveReader { public: explicit CompoundVarLoader(RuntimeObject *object); - bool readSave(Common::ReadStream *stream) override; + bool readSave(Common::ReadStream *stream, uint32 saveFileVersion) override; private: RuntimeObject *_object; @@ -43,7 +43,7 @@ private: CompoundVarLoader::CompoundVarLoader(RuntimeObject *object) : _object(object) { } -bool CompoundVarLoader::readSave(Common::ReadStream *stream) { +bool CompoundVarLoader::readSave(Common::ReadStream *stream, uint32 saveFileVersion) { if (_object == nullptr || !_object->isModifier()) return false; @@ -52,7 +52,7 @@ bool CompoundVarLoader::readSave(Common::ReadStream *stream) { if (!saveLoad) return false; - if (!saveLoad->load(modifier, stream)) + if (!saveLoad->load(modifier, stream, saveFileVersion)) return false; if (stream->err()) @@ -63,9 +63,6 @@ bool CompoundVarLoader::readSave(Common::ReadStream *stream) { return true; } - - - bool BehaviorModifier::load(ModifierLoaderContext &context, const Data::BehaviorModifier &data) { if (data.numChildren > 0) { ChildLoaderContext loaderContext; @@ -1733,7 +1730,7 @@ void CompoundVariableModifier::SaveLoad::saveInternal(Common::WriteStream *strea childSL.saveLoad->save(childSL.modifier, stream); } -bool CompoundVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool CompoundVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { const uint32 numChildren = stream->readUint32BE(); if (stream->err()) return false; @@ -1742,7 +1739,7 @@ bool CompoundVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream return false; for (const ChildSaveLoad &childSL : _childrenSaveLoad) { - if (!childSL.saveLoad->load(childSL.modifier, stream)) + if (!childSL.saveLoad->load(childSL.modifier, stream, saveFileVersion)) return false; } @@ -1825,7 +1822,7 @@ void BooleanVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream stream->writeByte(_value ? 1 : 0); } -bool BooleanVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool BooleanVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { byte b = stream->readByte(); if (stream->err()) return false; @@ -1896,7 +1893,7 @@ void IntegerVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream stream->writeSint32BE(_value); } -bool IntegerVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool IntegerVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { _value = stream->readSint32BE(); if (stream->err()) @@ -1985,7 +1982,7 @@ void IntegerRangeVariableModifier::SaveLoad::saveInternal(Common::WriteStream *s stream->writeSint32BE(_range.max); } -bool IntegerRangeVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool IntegerRangeVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { _range.min = stream->readSint32BE(); _range.max = stream->readSint32BE(); @@ -2075,7 +2072,7 @@ void VectorVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) stream->writeDoubleBE(_vector.magnitude); } -bool VectorVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool VectorVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { _vector.angleDegrees = stream->readDoubleBE(); _vector.magnitude = stream->readDoubleBE(); @@ -2167,7 +2164,7 @@ void PointVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) stream->writeSint16BE(_value.y); } -bool PointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool PointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { _value.x = stream->readSint16BE(); _value.y = stream->readSint16BE(); @@ -2233,7 +2230,7 @@ void FloatingPointVariableModifier::SaveLoad::saveInternal(Common::WriteStream * stream->writeDoubleBE(_value); } -bool FloatingPointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool FloatingPointVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { _value = stream->readDoubleBE(); if (stream->err()) @@ -2297,7 +2294,7 @@ void StringVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) stream->writeString(_value); } -bool StringVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool StringVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { uint32 size = stream->readUint32BE(); if (stream->err()) diff --git a/engines/mtropolis/modifiers.h b/engines/mtropolis/modifiers.h index e7c59224ae8..600f51e57da 100644 --- a/engines/mtropolis/modifiers.h +++ b/engines/mtropolis/modifiers.h @@ -690,7 +690,7 @@ private: explicit SaveLoad(CompoundVariableModifier *modifier); void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; void commitLoad() const override; private: @@ -744,7 +744,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; BooleanVariableModifier *_modifier; bool _value; @@ -779,7 +779,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; IntegerVariableModifier *_modifier; int32 _value; @@ -817,7 +817,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; IntegerRangeVariableModifier *_modifier; IntRange _range; @@ -855,7 +855,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; VectorVariableModifier *_modifier; AngleMagVector _vector; @@ -893,7 +893,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; PointVariableModifier *_modifier; Common::Point _value; @@ -928,7 +928,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; FloatingPointVariableModifier *_modifier; double _value; @@ -963,7 +963,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; StringVariableModifier *_modifier; Common::String _value; diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp index a99247a3fd7..aca9a3b09e8 100644 --- a/engines/mtropolis/mtropolis.cpp +++ b/engines/mtropolis/mtropolis.cpp @@ -120,7 +120,9 @@ Common::Error MTropolisEngine::run() { enhancedColorDepthMode = kColorDepthMode32Bit; HackSuites::addObsidianBugFixes(*_gameDescription, _runtime->getHacks()); - HackSuites::addObsidianAutoSaves(*_gameDescription, _runtime->getHacks(), this); + + if (ConfMan.getBool("mtropolis_mod_auto_save")) + HackSuites::addObsidianAutoSaves(*_gameDescription, _runtime->getHacks(), this); if (ConfMan.getBool("mtropolis_mod_obsidian_widescreen")) { _runtime->getHacks().reportDisplaySize = Common::Point(640, 480); diff --git a/engines/mtropolis/mtropolis.h b/engines/mtropolis/mtropolis.h index af743426c69..73dfac59ba3 100644 --- a/engines/mtropolis/mtropolis.h +++ b/engines/mtropolis/mtropolis.h @@ -70,6 +70,9 @@ protected: void pauseEngineIntern(bool pause) override; private: + static const uint kCurrentSaveFileVersion = 1; + static const uint kSavegameSignature = 0x6d545356; // mTSV + Common::ScopedPtr _runtime; }; diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp index 767f1d8205a..3f82ec85235 100644 --- a/engines/mtropolis/plugin/standard.cpp +++ b/engines/mtropolis/plugin/standard.cpp @@ -2201,7 +2201,7 @@ void ObjectReferenceVariableModifier::SaveLoad::saveInternal(Common::WriteStream stream->writeString(_objectPath); } -bool ObjectReferenceVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool ObjectReferenceVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { uint32 stringLen = stream->readUint32BE(); if (stream->err()) return false; @@ -2805,7 +2805,7 @@ void ListVariableModifier::SaveLoad::saveInternal(Common::WriteStream *stream) c recursiveWriteList(_list.get(), stream); } -bool ListVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream) { +bool ListVariableModifier::SaveLoad::loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) { Common::SharedPtr list = recursiveReadList(stream); if (list) { _list = list; diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h index ebb3242c652..f5033c6bc12 100644 --- a/engines/mtropolis/plugin/standard.h +++ b/engines/mtropolis/plugin/standard.h @@ -178,7 +178,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; ObjectReferenceVariableModifier *_modifier; Common::String _objectPath; @@ -326,7 +326,7 @@ private: private: void commitLoad() const override; void saveInternal(Common::WriteStream *stream) const override; - bool loadInternal(Common::ReadStream *stream) override; + bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) override; static void recursiveWriteList(DynamicList *list, Common::WriteStream *stream); static Common::SharedPtr recursiveReadList(Common::ReadStream *stream); diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp index 58f0282930f..edbe6fe1645 100644 --- a/engines/mtropolis/runtime.cpp +++ b/engines/mtropolis/runtime.cpp @@ -7520,7 +7520,7 @@ void ModifierSaveLoad::save(Modifier *modifier, Common::WriteStream *stream) { saveInternal(stream); } -bool ModifierSaveLoad::load(Modifier *modifier, Common::ReadStream *stream) { +bool ModifierSaveLoad::load(Modifier *modifier, Common::ReadStream *stream, uint32 saveFileVersion) { uint32 checkGUID = stream->readUint32BE(); uint16 nameLen = stream->readUint16BE(); @@ -7544,7 +7544,7 @@ bool ModifierSaveLoad::load(Modifier *modifier, Common::ReadStream *stream) { if (modifier->getStaticGUID() != checkGUID) return false; - return loadInternal(stream); + return loadInternal(stream, saveFileVersion); } ModifierHooks::~ModifierHooks() { diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h index 69ebbca804b..09a5afd473e 100644 --- a/engines/mtropolis/runtime.h +++ b/engines/mtropolis/runtime.h @@ -2547,7 +2547,7 @@ public: virtual ~ModifierSaveLoad(); void save(Modifier *modifier, Common::WriteStream *stream); - bool load(Modifier *modifier, Common::ReadStream *stream); + bool load(Modifier *modifier, Common::ReadStream *stream, uint32 saveFileVersion); virtual void commitLoad() const = 0; protected: @@ -2556,7 +2556,7 @@ protected: // Loads the modifier state from a stream into the save/load state and returns true // if successful. This will not trigger any actual changes until "commit" is called. - virtual bool loadInternal(Common::ReadStream *stream) = 0; + virtual bool loadInternal(Common::ReadStream *stream, uint32 saveFileVersion) = 0; }; class ModifierHooks { diff --git a/engines/mtropolis/saveload.cpp b/engines/mtropolis/saveload.cpp index b4d3b28ee5c..0ae46092663 100644 --- a/engines/mtropolis/saveload.cpp +++ b/engines/mtropolis/saveload.cpp @@ -20,8 +20,10 @@ */ #include "common/savefile.h" +#include "common/system.h" #include "common/translation.h" +#include "gui/message.h" #include "gui/saveload.h" #include "mtropolis/mtropolis.h" @@ -74,6 +76,10 @@ bool MTropolisEngine::promptSave(ISaveWriter *writer) { Common::String saveFileName = getSaveStateName(slot); Common::SharedPtr out(_saveFileMan->openForSaving(saveFileName, false)); + + out->writeUint32BE(kSavegameSignature); + out->writeUint32BE(kCurrentSaveFileVersion); + if (!writer->writeSave(out.get()) || out->err()) warning("An error occurred while writing file '%s'", saveFileName.c_str()); @@ -96,7 +102,37 @@ bool MTropolisEngine::promptLoad(ISaveReader *reader) { Common::String saveFileName = getSaveStateName(slot); Common::SharedPtr in(_saveFileMan->openForLoading(saveFileName)); - if (!reader->readSave(in.get())) { + + uint32 signature = in->readUint32BE(); + uint32 saveFileVersion = in->readUint32BE(); + if (in->err()) { + GUI::MessageDialog dialog(_("Failed to read version information from save file")); + dialog.runModal(); + + warning("An error occurred while reading the save file version from '%s'", saveFileName.c_str()); + return false; + } + + if (signature != kSavegameSignature) { + GUI::MessageDialog dialog(_("Failed to load save, the save file doesn't contain valid version information.")); + dialog.runModal(); + + warning("Save file '%s' version is above the current save file version", saveFileName.c_str()); + return false; + } + + if (saveFileVersion > kCurrentSaveFileVersion) { + GUI::MessageDialog dialog(_("Failed to load save, the save file was created by a newer version of ScummVM.")); + dialog.runModal(); + + warning("Save file '%s' version is above the current save file version", saveFileName.c_str()); + return false; + } + + if (!reader->readSave(in.get(), saveFileVersion)) { + GUI::MessageDialog dialog(_("Failed to load save, an error occurred when reading the save game data.")); + dialog.runModal(); + warning("An error occurred while reading file '%s'", saveFileName.c_str()); return false; } @@ -109,11 +145,17 @@ bool MTropolisEngine::autoSave(ISaveWriter *writer) { Common::String saveFileName = getSaveStateName(slot); Common::SharedPtr out(_saveFileMan->openForSaving(saveFileName, false)); + + out->writeUint32BE(kSavegameSignature); + out->writeUint32BE(kCurrentSaveFileVersion); + if (!writer->writeSave(out.get()) || out->err()) warning("An error occurred while writing file '%s'", saveFileName.c_str()); getMetaEngine()->appendExtendedSave(out.get(), getTotalPlayTime(), "Auto Save", true); + g_system->displayMessageOnOSD(_("Progress Saved")); + return true; } diff --git a/engines/mtropolis/saveload.h b/engines/mtropolis/saveload.h index 3342f3d9f41..6ef2e173799 100644 --- a/engines/mtropolis/saveload.h +++ b/engines/mtropolis/saveload.h @@ -42,7 +42,7 @@ struct ISaveWriter : public IInterfaceBase { }; struct ISaveReader : public IInterfaceBase { - virtual bool readSave(Common::ReadStream *stream) = 0; + virtual bool readSave(Common::ReadStream *stream, uint32 saveFileVersion) = 0; }; struct ISaveUIProvider : public IInterfaceBase {