From 22f88f80b015af2173981116dd9e867f7582f822 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 19:36:35 +0100 Subject: [PATCH] Implement sprite cycle checking --- src/openrct2/rct2/S6Exporter.cpp | 2 +- src/openrct2/rct2/S6Importer.cpp | 4 ++ src/openrct2/world/sprite.c | 106 +++++++++++++++++++++++++++++++ src/openrct2/world/sprite.h | 3 +- 4 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 7a62e5626b..086456d20f 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -152,6 +152,7 @@ void S6Exporter::Save(IStream * stream, bool isScenario) void S6Exporter::Export() { + openrct2_assert(!(check_for_spatial_index_cycles(false) || check_for_sprite_list_cycles(false)), "A sprite cycle exists."); _s6.info = gS6Info; uint32 researchedTrackPiecesA[128]; uint32 researchedTrackPiecesB[128]; @@ -449,7 +450,6 @@ extern "C" map_reorganise_elements(); sprite_clear_all_unused(); - viewport_set_saved_view(); bool result = false; diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 53ac9a2971..d12ea1071e 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -413,6 +413,10 @@ public: map_update_tile_pointers(); game_convert_strings_to_utf8(); map_count_remaining_land_rights(); + + // We try to fix the cycles on import, hence the 'true' parameter + check_for_sprite_list_cycles(true); + check_for_spatial_index_cycles(true); } void Initialise() diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index e313a71106..7f4cf210a4 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -267,6 +267,7 @@ void sprite_clear_all_unused() sprite->previous = previousSpriteIndex; sprite->linked_list_type_offset = SPRITE_LIST_NULL * 2; sprite->sprite_index = spriteIndex; + sprite->next_in_quadrant = (sprite->next_in_quadrant == 0) ? SPRITE_INDEX_NULL : sprite->next_in_quadrant; _spriteFlashingList[spriteIndex] = false; spriteIndex = nextSpriteIndex; } @@ -277,6 +278,7 @@ static void sprite_reset(rct_unk_sprite *sprite) // Need to retain how the sprite is linked in lists uint8 llto = sprite->linked_list_type_offset; uint16 next = sprite->next; + uint16 next_in_quadrant = sprite->next_in_quadrant; uint16 prev = sprite->previous; uint16 sprite_index = sprite->sprite_index; _spriteFlashingList[sprite_index] = false; @@ -285,6 +287,7 @@ static void sprite_reset(rct_unk_sprite *sprite) sprite->linked_list_type_offset = llto; sprite->next = next; + sprite->next_in_quadrant = next_in_quadrant; sprite->previous = prev; sprite->sprite_index = sprite_index; } @@ -823,3 +826,106 @@ bool sprite_get_flashing(rct_sprite *sprite) assert(sprite->unknown.sprite_index < MAX_SPRITES); return _spriteFlashingList[sprite->unknown.sprite_index]; } + +static bool sprite_is_in_cycle(uint16 sprite_idx, bool fix) +{ + if (sprite_idx == SPRITE_INDEX_NULL) + { + return false; + } + const rct_sprite * fast = get_sprite(sprite_idx); + const rct_sprite * slow = fast; + bool increment_slow = false; + bool cycled = false; + while (fast->unknown.sprite_index != SPRITE_INDEX_NULL) + { + // increment fast every time, unless reached the end + if (fast->unknown.next == SPRITE_INDEX_NULL) + { + break; + } + else { + fast = get_sprite(fast->unknown.next); + } + // increment slow only every second iteration + if (increment_slow) + { + slow = get_sprite(slow->unknown.next); + } + increment_slow = !increment_slow; + if (fast == slow) + { + cycled = true; + if (fix) + { + rct_sprite * spr = get_sprite(sprite_idx); + // There may be a better solution than simply setting this to 0xFFFF + spr->unknown.next = SPRITE_INDEX_NULL; + } + break; + } + } + return cycled; +} + +static bool sprite_is_in_cycle_quadrant(uint16 sprite_idx, bool fix) +{ + if (sprite_idx == SPRITE_INDEX_NULL) + { + return false; + } + const rct_sprite * fast = get_sprite(sprite_idx); + const rct_sprite * slow = fast; + bool increment_slow = false; + bool cycled = false; + while (fast->unknown.sprite_index != SPRITE_INDEX_NULL) + { + // increment fast every time, unless reached the end + if (fast->unknown.next_in_quadrant == SPRITE_INDEX_NULL) + { + break; + } + else { + fast = get_sprite(fast->unknown.next_in_quadrant); + } + // increment slow only every second iteration + if (increment_slow) + { + slow = get_sprite(slow->unknown.next_in_quadrant); + } + increment_slow = !increment_slow; + if (fast == slow) + { + cycled = true; + if (fix) + { + rct_sprite * spr = get_sprite(sprite_idx); + + // There may be a better solution than simply setting this to 0xFFFF + spr->unknown.next_in_quadrant = SPRITE_INDEX_NULL; + } + break; + } + } + return cycled; +} + +bool check_for_sprite_list_cycles(bool fix) +{ + for (sint32 i = 0; i < 6; i++) { + if (sprite_is_in_cycle(gSpriteListHead[i], fix)) { + return true; + } + } + return false; +} + +bool check_for_spatial_index_cycles(bool fix) +{ + for (size_t i = 0; i < 0x10000; i++) { + if (sprite_is_in_cycle_quadrant(gSpriteSpatialIndex[i], fix)) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/src/openrct2/world/sprite.h b/src/openrct2/world/sprite.h index 55dde661bb..1cbe3415d0 100644 --- a/src/openrct2/world/sprite.h +++ b/src/openrct2/world/sprite.h @@ -478,6 +478,7 @@ const char *sprite_checksum(); void sprite_set_flashing(rct_sprite *sprite, bool flashing); bool sprite_get_flashing(rct_sprite *sprite); - +bool check_for_sprite_list_cycles(bool fix); +bool check_for_spatial_index_cycles(bool fix); #endif