From 7c68cf96e54935eeab08a8f61cad973740c83bd3 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sun, 23 Jan 2022 14:37:13 +0100 Subject: [PATCH 1/4] Close #16363: Allow listing .DAT files in JSON scenery groups --- src/openrct2/object/SceneryGroupObject.cpp | 30 +++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index 977a1437f0..fc3bfb27ff 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -192,7 +192,35 @@ std::vector SceneryGroupObject::ReadJsonEntries(json_t& j for (const auto& jEntry : jEntries) { - entries.emplace_back(Json::GetString(jEntry)); + auto entryName = Json::GetString(jEntry); + if (String::StartsWith(entryName, "$DAT:")) + { + if (entryName.length() != 5 + 8 + 1 + 8) + { + log_error("Malformed DAT entry in scenery group: %s", entryName.c_str()); + continue; + } + + try + { + auto originalName = entryName.substr(5 + 8 + 1, 8); + + rct_object_entry entry = {}; + entry.flags = std::stoul(entryName.substr(5, 8), nullptr, 16); + entry.checksum = 0; + auto minLength = std::min(8, originalName.length()); + std::memcpy(entry.name, originalName.c_str(), minLength); + entries.emplace_back(entry); + } + catch (std::invalid_argument&) + { + log_error("Malformed flags in DAT entry in scenery group: %s", entryName.c_str()); + } + } + else + { + entries.emplace_back(entryName); + } } return entries; } From b1637e11993c482004425a8b2464d1d2b9be711f Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sun, 23 Jan 2022 15:40:08 +0100 Subject: [PATCH 2/4] Use IReadObjectContext for error log --- src/openrct2/object/SceneryGroupObject.cpp | 10 ++++++---- src/openrct2/object/SceneryGroupObject.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index fc3bfb27ff..134dbb2a1b 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -141,7 +141,7 @@ void SceneryGroupObject::ReadJson(IReadObjectContext* context, json_t& root) _legacyType.priority = Json::GetNumber(properties["priority"]); _legacyType.entertainer_costumes = ReadJsonEntertainerCostumes(properties["entertainerCostumes"]); - _items = ReadJsonEntries(properties["entries"]); + _items = ReadJsonEntries(context, properties["entries"]); } PopulateTablesFromJson(context, root); @@ -186,7 +186,7 @@ EntertainerCostume SceneryGroupObject::ParseEntertainerCostume(const std::string return EntertainerCostume::Panda; } -std::vector SceneryGroupObject::ReadJsonEntries(json_t& jEntries) +std::vector SceneryGroupObject::ReadJsonEntries(IReadObjectContext* context, json_t& jEntries) { std::vector entries; @@ -197,7 +197,8 @@ std::vector SceneryGroupObject::ReadJsonEntries(json_t& j { if (entryName.length() != 5 + 8 + 1 + 8) { - log_error("Malformed DAT entry in scenery group: %s", entryName.c_str()); + std::string errorMessage = "Malformed DAT entry in scenery group: " + entryName; + context->LogError(ObjectError::InvalidProperty, errorMessage.c_str()); continue; } @@ -214,7 +215,8 @@ std::vector SceneryGroupObject::ReadJsonEntries(json_t& j } catch (std::invalid_argument&) { - log_error("Malformed flags in DAT entry in scenery group: %s", entryName.c_str()); + std::string errorMessage = "Malformed flags in DAT entry in scenery group: " + entryName; + context->LogError(ObjectError::InvalidProperty, errorMessage.c_str()); } } else diff --git a/src/openrct2/object/SceneryGroupObject.h b/src/openrct2/object/SceneryGroupObject.h index fed7094cb5..677f9dce19 100644 --- a/src/openrct2/object/SceneryGroupObject.h +++ b/src/openrct2/object/SceneryGroupObject.h @@ -46,5 +46,5 @@ private: static std::vector ReadItems(OpenRCT2::IStream* stream); static uint32_t ReadJsonEntertainerCostumes(json_t& jCostumes); static EntertainerCostume ParseEntertainerCostume(const std::string& s); - static std::vector ReadJsonEntries(json_t& jEntries); + static std::vector ReadJsonEntries(IReadObjectContext* context, json_t& jEntries); }; From 9228cde8cd9cc59b4ea94fdc8e126920007b9a18 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sun, 23 Jan 2022 15:40:37 +0100 Subject: [PATCH 3/4] Add comments to explain the numbers --- src/openrct2/object/SceneryGroupObject.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index 134dbb2a1b..fc9d2ec84b 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -193,8 +193,10 @@ std::vector SceneryGroupObject::ReadJsonEntries(IReadObje for (const auto& jEntry : jEntries) { auto entryName = Json::GetString(jEntry); + // Example entry: "$DAT:09F55406|00STBEN " if (String::StartsWith(entryName, "$DAT:")) { + // 5 for $DAT:, 8 for the checksum, 1 for the vertical bar, 8 for the .DAT name. if (entryName.length() != 5 + 8 + 1 + 8) { std::string errorMessage = "Malformed DAT entry in scenery group: " + entryName; From a16d548b69b94330019697525400ec82746bcde7 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Mon, 24 Jan 2022 12:30:22 +0100 Subject: [PATCH 4/4] Clean up rct_object_entry creation code --- src/openrct2/object/SceneryGroupObject.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/openrct2/object/SceneryGroupObject.cpp b/src/openrct2/object/SceneryGroupObject.cpp index fc9d2ec84b..e5bdf7d06b 100644 --- a/src/openrct2/object/SceneryGroupObject.cpp +++ b/src/openrct2/object/SceneryGroupObject.cpp @@ -20,6 +20,7 @@ #include "../drawing/Image.h" #include "../entity/Staff.h" #include "../localisation/Language.h" +#include "ObjectLimits.h" #include "ObjectManager.h" #include "ObjectRepository.h" @@ -27,6 +28,16 @@ using namespace OpenRCT2; +// Example entry: "$DAT:09F55406|00STBEN " +// 5 for $DAT:, 8 for the checksum, 1 for the vertical bar, 8 for the .DAT name. +static constexpr uint8_t DatEntryPrefixLength = 5; +static constexpr uint8_t DatEntryFlagsLength = 8; +static constexpr uint8_t DatEntrySeparatorLength = 1; +static constexpr uint8_t DatEntryLength = DatEntryPrefixLength + DatEntryFlagsLength + DatEntrySeparatorLength + + DAT_NAME_LENGTH; +static constexpr uint8_t DatEntryFlagsStart = DatEntryPrefixLength; +static constexpr uint8_t DatEntryNameStart = DatEntryPrefixLength + DatEntryFlagsLength + DatEntrySeparatorLength; + void SceneryGroupObject::ReadLegacy(IReadObjectContext* context, IStream* stream) { stream->Seek(6, STREAM_SEEK_CURRENT); @@ -193,11 +204,9 @@ std::vector SceneryGroupObject::ReadJsonEntries(IReadObje for (const auto& jEntry : jEntries) { auto entryName = Json::GetString(jEntry); - // Example entry: "$DAT:09F55406|00STBEN " if (String::StartsWith(entryName, "$DAT:")) { - // 5 for $DAT:, 8 for the checksum, 1 for the vertical bar, 8 for the .DAT name. - if (entryName.length() != 5 + 8 + 1 + 8) + if (entryName.length() != DatEntryLength) { std::string errorMessage = "Malformed DAT entry in scenery group: " + entryName; context->LogError(ObjectError::InvalidProperty, errorMessage.c_str()); @@ -206,13 +215,10 @@ std::vector SceneryGroupObject::ReadJsonEntries(IReadObje try { - auto originalName = entryName.substr(5 + 8 + 1, 8); - rct_object_entry entry = {}; - entry.flags = std::stoul(entryName.substr(5, 8), nullptr, 16); + entry.flags = std::stoul(entryName.substr(DatEntryFlagsStart, DatEntryFlagsLength), nullptr, 16); + std::memcpy(entry.name, entryName.c_str() + DatEntryNameStart, DAT_NAME_LENGTH); entry.checksum = 0; - auto minLength = std::min(8, originalName.length()); - std::memcpy(entry.name, originalName.c_str(), minLength); entries.emplace_back(entry); } catch (std::invalid_argument&)