diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index c777e77de6..8138ee616a 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3677,6 +3677,10 @@ STR_6485 :See-Through vehicles toggle STR_6486 :See-Through guests toggle STR_6487 :See-Through staff toggle STR_6488 :{RED}Guests are complaining about the length of the queues in your park.{NEWLINE}Consider shortening problematic queues, or increasing the rides’ throughput. +STR_6489 :Error: Incompatible Park Version +STR_6490 :This park was saved in a later version of OpenRCT2. Park is v{INT32} and requires at least v{INT32}. +STR_6491 :Warning: Semi-compatible Park Version +STR_6492 :This park was saved in a later version of OpenRCT2, some data may be lost. Park is v{INT32} and requires at least v{INT32}. ############# # Scenarios # diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index ea50eff39e..b5a2c34766 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -719,6 +719,14 @@ namespace OpenRCT2 start_silent_record(); } #endif + if (result.SemiCompatibleVersion) + { + auto windowManager = _uiContext->GetWindowManager(); + auto ft = Formatter(); + ft.Add(result.MinVersion); + ft.Add(result.TargetVersion); + windowManager->ShowError(STR_WARNING_PARK_VERSION_TITLE, STR_WARNING_PARK_VERSION_MESSAGE, ft); + } return true; } catch (const ObjectLoadException& e) @@ -760,6 +768,18 @@ namespace OpenRCT2 auto windowManager = _uiContext->GetWindowManager(); windowManager->ShowError(STR_FILE_CONTAINS_UNSUPPORTED_RIDE_TYPES, STR_NONE, {}); } + catch (const UnsupportedVersionException& e) + { + if (loadTitleScreenFirstOnFail) + { + title_load(); + } + auto windowManager = _uiContext->GetWindowManager(); + Formatter ft; + ft.Add(e.MinVersion); + ft.Add(e.TargetVersion); + windowManager->ShowError(STR_ERROR_PARK_VERSION_TITLE, STR_ERROR_PARK_VERSION_MESSAGE, ft); + } catch (const std::exception& e) { // If loading the SV6 or SV4 failed return to the title screen if requested. diff --git a/src/openrct2/ParkImporter.h b/src/openrct2/ParkImporter.h index af3caf9fb7..031a4cf55d 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -32,6 +32,9 @@ struct ParkLoadResult final { public: ObjectList RequiredObjects; + bool SemiCompatibleVersion{}; + uint32_t MinVersion{}; + uint32_t TargetVersion{}; explicit ParkLoadResult(ObjectList&& requiredObjects) : RequiredObjects(std::move(requiredObjects)) @@ -101,3 +104,16 @@ public: { } }; + +class UnsupportedVersionException : public std::exception +{ +public: + uint32_t const MinVersion; + uint32_t const TargetVersion; + + explicit UnsupportedVersionException(uint32_t minVersion, uint32_t targetVersion) + : MinVersion(minVersion) + , TargetVersion(targetVersion) + { + } +}; diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index 77af76400a..379ff843e9 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3948,6 +3948,11 @@ enum : uint16_t STR_PEEPS_COMPLAINING_ABOUT_QUEUE_LENGTH_WARNING = 6488, + STR_ERROR_PARK_VERSION_TITLE = 6489, + STR_ERROR_PARK_VERSION_MESSAGE = 6490, + STR_WARNING_PARK_VERSION_TITLE = 6491, + STR_WARNING_PARK_VERSION_MESSAGE = 6492, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index 89aa1526ba..539d9e49b6 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -106,7 +106,24 @@ namespace OpenRCT2 ObjectEntryIndex _pathToQueueSurfaceMap[MAX_PATH_OBJECTS]; ObjectEntryIndex _pathToRailingsMap[MAX_PATH_OBJECTS]; + void ThrowIfIncompatibleVersion() + { + const auto& header = _os->GetHeader(); + if (header.MinVersion > PARK_FILE_CURRENT_VERSION) + { + throw UnsupportedVersionException(header.MinVersion, header.TargetVersion); + } + } + public: + bool IsSemiCompatibleVersion(uint32_t& minVersion, uint32_t& targetVersion) + { + const auto& header = _os->GetHeader(); + minVersion = header.MinVersion; + targetVersion = header.TargetVersion; + return targetVersion > PARK_FILE_CURRENT_VERSION; + } + void Load(const std::string_view path) { FileStream fs(path, FILE_MODE_OPEN); @@ -116,6 +133,8 @@ namespace OpenRCT2 void Load(IStream& stream) { _os = std::make_unique(stream, OrcaStream::Mode::READING); + ThrowIfIncompatibleVersion(); + RequiredObjects = {}; ReadWriteObjectsChunk(*_os); ReadWritePackedObjectsChunk(*_os); @@ -2311,7 +2330,10 @@ public: { _parkFile = std::make_unique(); _parkFile->Load(path); - return ParkLoadResult(std::move(_parkFile->RequiredObjects)); + + auto result = ParkLoadResult(std::move(_parkFile->RequiredObjects)); + result.SemiCompatibleVersion = _parkFile->IsSemiCompatibleVersion(result.MinVersion, result.TargetVersion); + return result; } ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override @@ -2329,7 +2351,10 @@ public: { _parkFile = std::make_unique(); _parkFile->Load(*stream); - return ParkLoadResult(std::move(_parkFile->RequiredObjects)); + + auto result = ParkLoadResult(std::move(_parkFile->RequiredObjects)); + result.SemiCompatibleVersion = _parkFile->IsSemiCompatibleVersion(result.MinVersion, result.TargetVersion); + return result; } void Import() override