From fdf6e1fca7333d7b4552458be037da34dc77c909 Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sun, 21 Aug 2022 23:19:14 +0200 Subject: [PATCH] Close #6326: Add support for RCTC SV6 files with 15000 entities --- distribution/changelog.txt | 1 + src/openrct2/rct2/Limits.h | 1 + src/openrct2/rct2/RCT2.h | 4 ++-- src/openrct2/rct2/S6Importer.cpp | 36 ++++++++++++++++++++++++-------- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index fe356643b0..c17af856ca 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.4.2 (in development) ------------------------------------------------------------------------ +- Feature: [#6326] Ability to load .SV6 files from RCT Classic that have more than 9601 guests. - Feature: [#13634] Add ability to sell merchandise in random colours. - Feature: [#16164] Add new track elements for Flying Coaster and Lay-Down Coaster and add paint code for more elements. - Feature: [#16283] Added parkinfo command line tool to list objects in a save file. diff --git a/src/openrct2/rct2/Limits.h b/src/openrct2/rct2/Limits.h index f7e9e33c09..36b94e6244 100644 --- a/src/openrct2/rct2/Limits.h +++ b/src/openrct2/rct2/Limits.h @@ -18,6 +18,7 @@ namespace RCT2::Limits constexpr const uint8_t MaxTrainsPerRide = 32; constexpr const uint8_t DowntimeHistorySize = 8; constexpr const uint16_t MaxEntities = 10000; + constexpr const uint16_t MaxEntitiesFlag15 = 15000; constexpr const uint32_t MaxTileElements = 0x30000; constexpr const uint16_t MaxAnimatedObjects = 2000; constexpr const uint8_t MaxResearchedRideTypeQuads = 8; // With 32 bits per uint32_t, this means there is room for diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h index 0e20a502cf..c72db15186 100644 --- a/src/openrct2/rct2/RCT2.h +++ b/src/openrct2/rct2/RCT2.h @@ -834,7 +834,7 @@ namespace RCT2 // SC6[6] uint32_t next_free_tile_element_pointer_index; - Entity sprites[Limits::MaxEntities]; + Entity sprites[Limits::MaxEntitiesFlag15]; uint16_t sprite_lists_head[static_cast(EntityListId::Count)]; uint16_t sprite_lists_count[static_cast(EntityListId::Count)]; StringId park_name; @@ -1019,7 +1019,7 @@ namespace RCT2 uint16_t wide_path_tile_loop_y; uint8_t pad_13CE778[434]; }; - assert_struct_size(S6Data, 0x46b44a); + assert_struct_size(S6Data, 0x5a3c4a); struct StexEntry { diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 9e38afef81..cddbd2196d 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -18,6 +18,7 @@ #include "../core/Console.hpp" #include "../core/FileStream.h" #include "../core/IStream.hpp" +#include "../core/MemoryStream.h" #include "../core/Numerics.hpp" #include "../core/Path.hpp" #include "../core/Random.hpp" @@ -166,11 +167,6 @@ namespace RCT2 } } - if (_s6.header.classic_flag == 0xf) - { - throw UnsupportedRCTCFlagException(_s6.header.classic_flag); - } - // Read packed objects // TODO try to contain this more and not store objects until later for (uint16_t i = 0; i < _s6.header.num_packed_objects; i++) @@ -190,7 +186,7 @@ namespace RCT2 { chunkReader.ReadChunk(&_s6.elapsed_months, 16); chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements)); - chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 2560076); + ReadChunk6(chunkReader, 76); chunkReader.ReadChunk(&_s6.guests_in_park, 4); chunkReader.ReadChunk(&_s6.last_guests_in_park, 8); chunkReader.ReadChunk(&_s6.park_rating, 2); @@ -203,7 +199,7 @@ namespace RCT2 { chunkReader.ReadChunk(&_s6.elapsed_months, 16); chunkReader.ReadChunk(&_s6.tile_elements, sizeof(_s6.tile_elements)); - chunkReader.ReadChunk(&_s6.next_free_tile_element_pointer_index, 3048816); + ReadChunk6(chunkReader, 488816); } _isScenario = isScenario; @@ -212,6 +208,22 @@ namespace RCT2 return ParkLoadResult(GetRequiredObjects()); } + void ReadChunk6(SawyerChunkReader& chunkReader, uint32_t sizeWithoutEntities) + { + uint32_t entitiesSize = GetMaxEntities() * sizeof(Entity); + auto bufferSize = sizeWithoutEntities + entitiesSize; + uint8_t buffer[bufferSize]; + chunkReader.ReadChunk(&buffer, bufferSize); + auto stream = OpenRCT2::MemoryStream(&buffer, bufferSize); + + uint32_t preEntitiesSize = sizeof(_s6.next_free_tile_element_pointer_index); + uint32_t postEntitiesSize = sizeWithoutEntities - preEntitiesSize; + + stream.Read(&_s6.next_free_tile_element_pointer_index, preEntitiesSize); + stream.Read(&_s6.sprites, entitiesSize); + stream.Read(&_s6.sprite_lists_head, postEntitiesSize); + } + bool GetDetails(scenario_index_entry* dst) override { *dst = {}; @@ -1168,8 +1180,9 @@ namespace RCT2 { // The number of riders might have overflown or underflown. Re-calculate the value. uint16_t numRiders = 0; - for (const auto& sprite : _s6.sprites) + for (int32_t i = 0; i < GetMaxEntities(); i++) { + const auto& sprite = _s6.sprites[i]; if (sprite.unknown.sprite_identifier == RCT12SpriteIdentifier::Peep) { if (sprite.peep.current_ride == static_cast(rideIndex.ToUnderlying()) @@ -1592,12 +1605,17 @@ namespace RCT2 void ImportEntities() { - for (int32_t i = 0; i < Limits::MaxEntities; i++) + for (int32_t i = 0; i < GetMaxEntities(); i++) { ImportEntity(_s6.sprites[i].unknown); } } + uint16_t GetMaxEntities() + { + return (_s6.header.classic_flag == 0xf) ? Limits::MaxEntitiesFlag15 : Limits::MaxEntities; + } + template void ImportEntity(const RCT12SpriteBase& src); void ImportEntityPeep(::Peep* dst, const Peep* src)