From cce3bb1845581182c6ad22b171d83006261a62a4 Mon Sep 17 00:00:00 2001 From: Duncan Date: Wed, 29 Sep 2021 17:48:50 +0100 Subject: [PATCH] Fix #15136: Exported SV6 hangs/crashes vanilla RCT2 --- distribution/changelog.txt | 1 + src/openrct2/rct2/S6Exporter.cpp | 39 +++++++++++++++++++++----------- src/openrct2/rct2/S6Exporter.h | 1 + 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 160dcffa53..e8ac49de2f 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -16,6 +16,7 @@ - Fix: [#14649] ImageImporter incorrectly remaps colours outside the RCT2 palette. - Fix: [#14667] “Extreme Hawaiian Island” has unpurchaseable land tiles (original bug). - Fix: [#15096] Crash when placing entrances in the scenario editor near the map corner. +- Fix: [#15136] Exported SV6 files cause vanilla RCT2 to hang. - Fix: [#15142] ToonTowner's mine roofs were moved into the pirate theme scenery group instead of the mine theme scenery group. - Fix: [#15148] Track Designs Manager delete confirmation window doesn't display properly. - Fix: [#15170] Plugin: incorrect label text alignment. diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 7015d66994..f4ea1932d8 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -1090,6 +1090,23 @@ void S6Exporter::ExportMarketingCampaigns() } } +void S6Exporter::RebuildEntitySpatialLocation(const TileCoordsXY& loc) +{ + uint16_t previous = SPRITE_INDEX_NULL; + for (auto* entity : EntityTileList(loc.ToCoordsXY())) + { + if (previous != SPRITE_INDEX_NULL) + { + _s6.sprites[previous].unknown.next_in_quadrant = entity->sprite_index; + } + previous = entity->sprite_index; + } + if (previous != SPRITE_INDEX_NULL) + { + _s6.sprites[previous].unknown.next_in_quadrant = SPRITE_INDEX_NULL; + } +} + void S6Exporter::RebuildEntityLinks() { // Rebuild next/previous linked list entity indexs @@ -1098,6 +1115,9 @@ void S6Exporter::RebuildEntityLinks() RCT12EntityLinkListOffset::Peep, RCT12EntityLinkListOffset::TrainHead, RCT12EntityLinkListOffset::Vehicle }) { uint16_t previous = SPRITE_INDEX_NULL; + // Intialise Head to NULL for situations where there are no entities of this type. + _s6.sprite_lists_head[EnumValue(list) >> 1] = SPRITE_INDEX_NULL; + for (auto& entity : _s6.sprites) { if (entity.unknown.linked_list_type_offset == list) @@ -1119,25 +1139,18 @@ void S6Exporter::RebuildEntityLinks() } // Rebuild next_in_quadrant linked list entity indexs + // This in theory is not required but to be on the safe side we are rebuilding it. for (auto x = 0; x < 255; ++x) { for (auto y = 0; y < 255; ++y) { - uint16_t previous = SPRITE_INDEX_NULL; - for (auto* entity : EntityTileList(TileCoordsXY{ x, y }.ToCoordsXY())) - { - if (previous != SPRITE_INDEX_NULL) - { - _s6.sprites[previous].unknown.next_in_quadrant = entity->sprite_index; - } - previous = entity->sprite_index; - } - if (previous != SPRITE_INDEX_NULL) - { - _s6.sprites[previous].unknown.next_in_quadrant = SPRITE_INDEX_NULL; - } + RebuildEntitySpatialLocation(TileCoordsXY{ x, y }); } } + // Rebuild next_in_quadrant linked list for LOCATION_NULL + TileCoordsXY invalid; + invalid.SetNull(); + RebuildEntitySpatialLocation(invalid); } constexpr RCT12EntityLinkListOffset GetRCT2LinkListOffset(const EntityBase* src) diff --git a/src/openrct2/rct2/S6Exporter.h b/src/openrct2/rct2/S6Exporter.h index b5a69db52d..42cc586019 100644 --- a/src/openrct2/rct2/S6Exporter.h +++ b/src/openrct2/rct2/S6Exporter.h @@ -79,4 +79,5 @@ private: std::optional AllocateUserString(std::string_view value); void ExportUserStrings(); void RebuildEntityLinks(); + void RebuildEntitySpatialLocation(const TileCoordsXY& loc); };