diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 91d4018af1..bbf977651f 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -154,8 +154,14 @@ void S6Exporter::Export() { sint32 spatial_cycle = check_for_spatial_index_cycles(false); sint32 regular_cycle = check_for_sprite_list_cycles(false); + sint32 disjoint_sprites_count = fix_disjoint_sprites(); openrct2_assert(spatial_cycle == -1, "Sprite cycle exists in spatial list %d", spatial_cycle); openrct2_assert(regular_cycle == -1, "Sprite cycle exists in regular list %d", regular_cycle); + // This one is less harmful, no need to assert for it ~janisozaur + if (disjoint_sprites_count > 0) + { + log_error("Found %d disjoint null sprites", disjoint_sprites_count); + } _s6.info = gS6Info; uint32 researchedTrackPiecesA[128]; uint32 researchedTrackPiecesB[128]; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index d12ea1071e..3f71867f5c 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -417,6 +417,12 @@ public: // We try to fix the cycles on import, hence the 'true' parameter check_for_sprite_list_cycles(true); check_for_spatial_index_cycles(true); + sint32 disjoint_sprites_count = fix_disjoint_sprites(); + // This one is less harmful, no need to assert for it ~janisozaur + if (disjoint_sprites_count > 0) + { + log_error("Found %d disjoint null sprites", disjoint_sprites_count); + } } void Initialise() diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index 98f80778c9..67058f0cb0 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -951,6 +951,50 @@ sint32 check_for_sprite_list_cycles(bool fix) return -1; } +/** + * Finds and fixes null sprites that are not reachable via SPRITE_LIST_NULL list. + * + * @return count of disjoint sprites found + */ +sint32 fix_disjoint_sprites() +{ + // Find reachable sprites + bool reachable[MAX_SPRITES] = {}; + uint16 sprite_idx = gSpriteListHead[SPRITE_LIST_NULL]; + rct_sprite * null_list_tail = NULL; + while (sprite_idx != SPRITE_INDEX_NULL) + { + reachable[sprite_idx] = true; + // cache the tail, so we don't have to walk the list twice + null_list_tail = get_sprite(sprite_idx); + sprite_idx = null_list_tail->unknown.next; + } + + sint32 count = 0; + + // Find all null sprites + for (sprite_idx = 0; sprite_idx < MAX_SPRITES; sprite_idx++) + { + rct_sprite * spr = get_sprite(sprite_idx); + if (spr->unknown.sprite_identifier == SPRITE_IDENTIFIER_NULL) + { + openrct2_assert(null_list_tail != NULL, "Null list is empty, yet found null sprites"); + spr->unknown.sprite_index = sprite_idx; + if (!reachable[sprite_idx]) + { + // Add the sprite directly to the list + null_list_tail->unknown.next = sprite_idx; + spr->unknown.next = SPRITE_INDEX_NULL; + spr->unknown.previous = null_list_tail->unknown.sprite_index; + null_list_tail = spr; + count++; + reachable[sprite_idx] = true; + } + } + } + return count; +} + sint32 check_for_spatial_index_cycles(bool fix) { for (sint32 i = 0; i < SPATIAL_INDEX_LOCATION_NULL; i++) { diff --git a/src/openrct2/world/sprite.h b/src/openrct2/world/sprite.h index ba132ff8f7..44929c96ca 100644 --- a/src/openrct2/world/sprite.h +++ b/src/openrct2/world/sprite.h @@ -479,5 +479,6 @@ void sprite_set_flashing(rct_sprite *sprite, bool flashing); bool sprite_get_flashing(rct_sprite *sprite); sint32 check_for_sprite_list_cycles(bool fix); sint32 check_for_spatial_index_cycles(bool fix); +sint32 fix_disjoint_sprites(); #endif