From 79d4de341bd22cd54ec8c5dc9ae4c53cc7989446 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Tue, 18 Mar 2025 23:24:50 +0100 Subject: [PATCH] Do not import garbage data in RCT1/2 news queues --- src/openrct2/rct1/RCT1.h | 3 +- src/openrct2/rct1/S4Importer.cpp | 69 ++++++++++++++++++++------------ src/openrct2/rct12/Limits.h | 3 +- src/openrct2/rct2/RCT2.h | 3 +- src/openrct2/rct2/S6Importer.cpp | 59 ++++++++++++++++----------- 5 files changed, 84 insertions(+), 53 deletions(-) diff --git a/src/openrct2/rct1/RCT1.h b/src/openrct2/rct1/RCT1.h index e8ad484125..d346c9b394 100644 --- a/src/openrct2/rct1/RCT1.h +++ b/src/openrct2/rct1/RCT1.h @@ -889,7 +889,8 @@ namespace OpenRCT2::RCT1 uint8_t TargetWeatherGloom; uint8_t Rain; uint8_t TargetRain; - RCT12NewsItem Messages[Limits::MaxNewsItems]; + RCT12NewsItem recentMessages[Limits::kMaxRecentNewsItems]; + RCT12NewsItem archivedMessages[Limits::kMaxArchivedNewsItems]; char ScenarioName[62]; uint16_t ScenarioSlotIndex; uint32_t ScenarioFlags; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 9bafe64659..23f4d75ce4 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -2192,6 +2192,46 @@ namespace OpenRCT2::RCT1 park.Name = std::move(parkName); } + std::vector convertNewsQueue(const RCT12NewsItem* queue, uint8_t size) + { + std::vector output{}; + const RCT12NewsItem* src = queue; + + for (uint8_t i = 0; i < size; i++) + { + News::Item dst{}; + + if (src->Type == 0) + break; + + dst.Type = static_cast(src->Type); + dst.Flags = src->Flags; + dst.Ticks = src->Ticks; + dst.MonthYear = src->MonthYear; + dst.Day = src->Day; + dst.Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text))); + + if (dst.Type == News::ItemType::Research) + { + uint8_t researchItem = src->Assoc & 0x000000FF; + uint8_t researchType = (src->Assoc & 0x00FF0000) >> 16; + + ::ResearchItem tmpResearchItem = {}; + ConvertResearchEntry(&tmpResearchItem, researchItem, researchType); + dst.Assoc = tmpResearchItem.rawValue; + } + else + { + dst.Assoc = src->Assoc; + } + + output.emplace_back(dst); + src++; + } + + return output; + } + void ImportParkFlags(GameState_t& gameState) { // Date and srand @@ -2241,32 +2281,9 @@ namespace OpenRCT2::RCT1 } // News items - for (size_t i = 0; i < Limits::MaxNewsItems; i++) - { - const RCT12NewsItem* src = &_s4.Messages[i]; - News::Item* dst = &gameState.NewsItems[i]; - - dst->Type = static_cast(src->Type); - dst->Flags = src->Flags; - dst->Ticks = src->Ticks; - dst->MonthYear = src->MonthYear; - dst->Day = src->Day; - dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text))); - - if (dst->Type == News::ItemType::Research) - { - uint8_t researchItem = src->Assoc & 0x000000FF; - uint8_t researchType = (src->Assoc & 0x00FF0000) >> 16; - - ::ResearchItem tmpResearchItem = {}; - ConvertResearchEntry(&tmpResearchItem, researchItem, researchType); - dst->Assoc = tmpResearchItem.rawValue; - } - else - { - dst->Assoc = src->Assoc; - } - } + auto recentMessages = convertNewsQueue(_s4.recentMessages, std::size(_s4.recentMessages)); + auto archivedMessages = convertNewsQueue(_s4.archivedMessages, std::size(_s4.archivedMessages)); + News::importNewsItems(gameState, recentMessages, archivedMessages); // Initial guest status gameState.GuestInitialCash = ToMoney64(_s4.GuestInitialCash); diff --git a/src/openrct2/rct12/Limits.h b/src/openrct2/rct12/Limits.h index ee21b87462..2d5e8d6c00 100644 --- a/src/openrct2/rct12/Limits.h +++ b/src/openrct2/rct12/Limits.h @@ -16,7 +16,8 @@ namespace OpenRCT2::RCT12::Limits constexpr uint8_t kMaxRidesInPark = 255; constexpr uint8_t kMaxAwards = 4; - constexpr uint8_t MaxNewsItems = 61; + constexpr uint8_t kMaxRecentNewsItems = 11; + constexpr uint8_t kMaxArchivedNewsItems = 50; constexpr uint8_t kMaxStationsPerRide = 4; constexpr uint8_t kMaxPeepSpawns = 2; constexpr uint8_t kMaxParkEntrances = 4; diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index 53c2492eea..f4d543fb4b 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -990,7 +990,8 @@ namespace OpenRCT2::RCT2 uint8_t NextWeatherGloom; uint8_t CurrentWeatherLevel; uint8_t NextWeatherLevel; - RCT12NewsItem NewsItems[Limits::MaxNewsItems]; + RCT12NewsItem recentMessages[Limits::kMaxRecentNewsItems]; + RCT12NewsItem archivedMessages[Limits::kMaxArchivedNewsItems]; char RCT1ScenarioName[62]; // Unused in RCT2 uint16_t RCT1ScenarioSlotIndex; // Unused in RCT2 uint32_t RCT1ScenarioFlags; // Unused in RCT2 diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 792684a855..e5ca347c5f 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -320,6 +320,38 @@ namespace OpenRCT2::RCT2 return {}; } + std::vector convertNewsQueue(const RCT12NewsItem* queue, uint8_t size) + { + std::vector output{}; + const RCT12NewsItem* src = queue; + + for (uint8_t i = 0; i < size; i++) + { + if (src->Type == 0) + break; + + if (src->Type >= News::ItemTypeCount) + { + LOG_ERROR("Invalid news type 0x%x for news item %d, ignoring remaining news items", src->Type, i); + break; + } + + News::Item dst{}; + dst.Type = static_cast(src->Type); + dst.Flags = src->Flags; + dst.Assoc = src->Assoc; + dst.Ticks = src->Ticks; + dst.MonthYear = src->MonthYear; + dst.Day = src->Day; + dst.Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text))); + + output.emplace_back(dst); + src++; + } + + return output; + } + void Import(GameState_t& gameState) override { Initialise(gameState); @@ -573,30 +605,9 @@ namespace OpenRCT2::RCT2 }; // News items - News::InitQueue(); - for (size_t i = 0; i < Limits::MaxNewsItems; i++) - { - const RCT12NewsItem* src = &_s6.NewsItems[i]; - News::Item* dst = &gameState.NewsItems[i]; - if (src->Type < News::ItemTypeCount) - { - dst->Type = static_cast(src->Type); - dst->Flags = src->Flags; - dst->Assoc = src->Assoc; - dst->Ticks = src->Ticks; - dst->MonthYear = src->MonthYear; - dst->Day = src->Day; - dst->Text = ConvertFormattedStringToOpenRCT2(std::string_view(src->Text, sizeof(src->Text))); - } - else - { - // In case where news item type is broken, consider all remaining news items invalid. - LOG_ERROR("Invalid news type 0x%x for news item %d, ignoring remaining news items", src->Type, i); - // Still need to set the correct type to properly terminate the queue - dst->Type = News::ItemType::Null; - break; - } - } + auto recentMessages = convertNewsQueue(_s6.recentMessages, std::size(_s6.recentMessages)); + auto archivedMessages = convertNewsQueue(_s6.archivedMessages, std::size(_s6.archivedMessages)); + News::importNewsItems(gameState, recentMessages, archivedMessages); // Pad13CE730 // rct1_scenario_flags