From 22f88f80b015af2173981116dd9e867f7582f822 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 19:36:35 +0100 Subject: [PATCH 1/7] 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 From 89bfe1d1bde222244c3b4c67971c6c37a8c47227 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 22:33:59 +0100 Subject: [PATCH 2/7] Code quality improvements --- src/openrct2/world/sprite.c | 54 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index 7f4cf210a4..086d8b8306 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -259,15 +259,13 @@ void sprite_clear_all_unused() spriteIndex = gSpriteListHead[SPRITE_LIST_NULL]; while (spriteIndex != SPRITE_INDEX_NULL) { sprite = &get_sprite(spriteIndex)->unknown; - nextSpriteIndex = sprite->next; - previousSpriteIndex = sprite->previous; - memset(sprite, 0, sizeof(rct_sprite)); - sprite->sprite_identifier = SPRITE_IDENTIFIER_NULL; - sprite->next = nextSpriteIndex; - sprite->previous = previousSpriteIndex; + sprite_reset(sprite); 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; + + // sprite->next_in_quadrant will only end up as zero owing to corruption + // most likely due to previous builds not preserving it when resetting sprites + // We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists + if (sprite->next_in_quadrant == 0) { sprite->next_in_quadrant = SPRITE_INDEX_NULL; } _spriteFlashingList[spriteIndex] = false; spriteIndex = nextSpriteIndex; } @@ -290,6 +288,7 @@ static void sprite_reset(rct_unk_sprite *sprite) sprite->next_in_quadrant = next_in_quadrant; sprite->previous = prev; sprite->sprite_index = sprite_index; + sprite->sprite_identifier = SPRITE_IDENTIFIER_NULL; } // Resets all sprites in SPRITE_LIST_NULL list @@ -827,7 +826,7 @@ bool sprite_get_flashing(rct_sprite *sprite) return _spriteFlashingList[sprite->unknown.sprite_index]; } -static bool sprite_is_in_cycle(uint16 sprite_idx, bool fix) +static bool sprite_is_in_cycle(uint16 sprite_idx) { if (sprite_idx == SPRITE_INDEX_NULL) { @@ -856,19 +855,13 @@ static bool sprite_is_in_cycle(uint16 sprite_idx, bool fix) 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) +static bool sprite_is_in_quadrant_cycle(uint16 sprite_idx) { if (sprite_idx == SPRITE_INDEX_NULL) { @@ -897,13 +890,6 @@ static bool sprite_is_in_cycle_quadrant(uint16 sprite_idx, bool fix) 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; } } @@ -912,8 +898,15 @@ static bool sprite_is_in_cycle_quadrant(uint16 sprite_idx, bool fix) bool check_for_sprite_list_cycles(bool fix) { - for (sint32 i = 0; i < 6; i++) { + for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++) { if (sprite_is_in_cycle(gSpriteListHead[i], fix)) { + if (fix) + { + rct_sprite * spr = get_sprite(gSpriteListHead[i]); + + // There may be a better solution than simply setting this to 0xFFFF + spr->unknown.next = SPRITE_INDEX_NULL; + } return true; } } @@ -922,10 +915,17 @@ bool check_for_sprite_list_cycles(bool fix) 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)) { + for (sint32 i = 0; i < SPATIAL_INDEX_LOCATION_NULL; i++) { + if (sprite_is_in_quadrant_cycle(gSpriteSpatialIndex[i])) { + if (fix) + { + rct_sprite * spr = get_sprite(gSpriteSpatialIndex[i]); + + // There may be a better solution than simply setting this to 0xFFFF + spr->unknown.next_in_quadrant = SPRITE_INDEX_NULL; + } return true; } } return false; -} \ No newline at end of file +} From 5cc188076c5fddb850f284079eb69510b1ba97f5 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 22:42:45 +0100 Subject: [PATCH 3/7] Restore resetting of sprite index --- src/openrct2/world/sprite.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index 086d8b8306..2da130a022 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -262,6 +262,10 @@ void sprite_clear_all_unused() sprite_reset(sprite); sprite->linked_list_type_offset = SPRITE_LIST_NULL * 2; + // This shouldn't be necessary, as sprite_reset() preserves the index + // but it has been left in as a safety net in case the index isn't set correctly + sprite->sprite_index = spriteIndex; + // sprite->next_in_quadrant will only end up as zero owing to corruption // most likely due to previous builds not preserving it when resetting sprites // We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists From 30648df16afaa6d98e069a2b131fca393dc262a0 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 22:44:05 +0100 Subject: [PATCH 4/7] Remove superfluous parameter --- src/openrct2/world/sprite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index 2da130a022..fb81396841 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -903,7 +903,7 @@ static bool sprite_is_in_quadrant_cycle(uint16 sprite_idx) bool check_for_sprite_list_cycles(bool fix) { for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++) { - if (sprite_is_in_cycle(gSpriteListHead[i], fix)) { + if (sprite_is_in_cycle(gSpriteListHead[i])) { if (fix) { rct_sprite * spr = get_sprite(gSpriteListHead[i]); From 9d338101b901013f45d5cef4cd644a8007e9e164 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 22:55:46 +0100 Subject: [PATCH 5/7] Fix compilation issues and restore line that went astray --- src/openrct2/world/sprite.c | 57 +++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index fb81396841..0efa585d52 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -247,34 +247,6 @@ const char * sprite_checksum() #endif // DISABLE_NETWORK -/** - * Clears all the unused sprite memory to zero. Probably so that it can be compressed better when saving. - * rct2: 0x0069EBA4 - */ -void sprite_clear_all_unused() -{ - rct_unk_sprite *sprite; - uint16 spriteIndex, nextSpriteIndex, previousSpriteIndex; - - spriteIndex = gSpriteListHead[SPRITE_LIST_NULL]; - while (spriteIndex != SPRITE_INDEX_NULL) { - sprite = &get_sprite(spriteIndex)->unknown; - sprite_reset(sprite); - sprite->linked_list_type_offset = SPRITE_LIST_NULL * 2; - - // This shouldn't be necessary, as sprite_reset() preserves the index - // but it has been left in as a safety net in case the index isn't set correctly - sprite->sprite_index = spriteIndex; - - // sprite->next_in_quadrant will only end up as zero owing to corruption - // most likely due to previous builds not preserving it when resetting sprites - // We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists - if (sprite->next_in_quadrant == 0) { sprite->next_in_quadrant = SPRITE_INDEX_NULL; } - _spriteFlashingList[spriteIndex] = false; - spriteIndex = nextSpriteIndex; - } -} - static void sprite_reset(rct_unk_sprite *sprite) { // Need to retain how the sprite is linked in lists @@ -295,6 +267,35 @@ static void sprite_reset(rct_unk_sprite *sprite) sprite->sprite_identifier = SPRITE_IDENTIFIER_NULL; } +/** +* Clears all the unused sprite memory to zero. Probably so that it can be compressed better when saving. +* rct2: 0x0069EBA4 +*/ +void sprite_clear_all_unused() +{ + rct_unk_sprite *sprite; + uint16 spriteIndex, nextSpriteIndex; + + spriteIndex = gSpriteListHead[SPRITE_LIST_NULL]; + while (spriteIndex != SPRITE_INDEX_NULL) { + sprite = &get_sprite(spriteIndex)->unknown; + nextSpriteIndex = sprite->next; + sprite_reset(sprite); + sprite->linked_list_type_offset = SPRITE_LIST_NULL * 2; + + // This shouldn't be necessary, as sprite_reset() preserves the index + // but it has been left in as a safety net in case the index isn't set correctly + sprite->sprite_index = spriteIndex; + + // sprite->next_in_quadrant will only end up as zero owing to corruption + // most likely due to previous builds not preserving it when resetting sprites + // We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists + if (sprite->next_in_quadrant == 0) { sprite->next_in_quadrant = SPRITE_INDEX_NULL; } + _spriteFlashingList[spriteIndex] = false; + spriteIndex = nextSpriteIndex; + } +} + // Resets all sprites in SPRITE_LIST_NULL list void reset_empty_sprites() { From a586c961fbdb29373f8fd2981d627ec203b3a4c9 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 23:28:14 +0100 Subject: [PATCH 6/7] Correct sprite cycle fixing algorithm --- src/openrct2/world/sprite.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/openrct2/world/sprite.c b/src/openrct2/world/sprite.c index 0efa585d52..8f3b835a95 100644 --- a/src/openrct2/world/sprite.c +++ b/src/openrct2/world/sprite.c @@ -831,7 +831,7 @@ bool sprite_get_flashing(rct_sprite *sprite) return _spriteFlashingList[sprite->unknown.sprite_index]; } -static bool sprite_is_in_cycle(uint16 sprite_idx) +static rct_sprite * find_sprite_list_cycle(uint16 sprite_idx) { if (sprite_idx == SPRITE_INDEX_NULL) { @@ -840,7 +840,7 @@ static bool sprite_is_in_cycle(uint16 sprite_idx) const rct_sprite * fast = get_sprite(sprite_idx); const rct_sprite * slow = fast; bool increment_slow = false; - bool cycled = false; + rct_sprite * cycle_start = NULL; while (fast->unknown.sprite_index != SPRITE_INDEX_NULL) { // increment fast every time, unless reached the end @@ -859,14 +859,14 @@ static bool sprite_is_in_cycle(uint16 sprite_idx) increment_slow = !increment_slow; if (fast == slow) { - cycled = true; + cycle_start = get_sprite(slow->unknown.sprite_index); break; } } - return cycled; + return cycle_start; } -static bool sprite_is_in_quadrant_cycle(uint16 sprite_idx) +static rct_sprite * find_sprite_quadrant_cycle(uint16 sprite_idx) { if (sprite_idx == SPRITE_INDEX_NULL) { @@ -875,7 +875,7 @@ static bool sprite_is_in_quadrant_cycle(uint16 sprite_idx) const rct_sprite * fast = get_sprite(sprite_idx); const rct_sprite * slow = fast; bool increment_slow = false; - bool cycled = false; + rct_sprite * cycle_start = NULL; while (fast->unknown.sprite_index != SPRITE_INDEX_NULL) { // increment fast every time, unless reached the end @@ -894,23 +894,23 @@ static bool sprite_is_in_quadrant_cycle(uint16 sprite_idx) increment_slow = !increment_slow; if (fast == slow) { - cycled = true; + cycle_start = get_sprite(slow->unknown.sprite_index); break; } } - return cycled; + return cycle_start; } bool check_for_sprite_list_cycles(bool fix) { for (sint32 i = 0; i < NUM_SPRITE_LISTS; i++) { - if (sprite_is_in_cycle(gSpriteListHead[i])) { + rct_sprite * cycle_start = find_sprite_list_cycle(gSpriteListHead[i]); + if (cycle_start != NULL) + { if (fix) { - rct_sprite * spr = get_sprite(gSpriteListHead[i]); - // There may be a better solution than simply setting this to 0xFFFF - spr->unknown.next = SPRITE_INDEX_NULL; + cycle_start->unknown.next = SPRITE_INDEX_NULL; } return true; } @@ -921,13 +921,13 @@ bool check_for_sprite_list_cycles(bool fix) bool check_for_spatial_index_cycles(bool fix) { for (sint32 i = 0; i < SPATIAL_INDEX_LOCATION_NULL; i++) { - if (sprite_is_in_quadrant_cycle(gSpriteSpatialIndex[i])) { + rct_sprite * cycle_start = find_sprite_quadrant_cycle(gSpriteSpatialIndex[i]); + if (cycle_start != NULL) + { if (fix) { - rct_sprite * spr = get_sprite(gSpriteSpatialIndex[i]); - // There may be a better solution than simply setting this to 0xFFFF - spr->unknown.next_in_quadrant = SPRITE_INDEX_NULL; + cycle_start->unknown.next_in_quadrant = SPRITE_INDEX_NULL; } return true; } From 8224e715d8c60b09bb40b524e7c657b7812773e9 Mon Sep 17 00:00:00 2001 From: rwjuk Date: Wed, 5 Jul 2017 23:31:51 +0100 Subject: [PATCH 7/7] Network version bump --- src/openrct2/network/network.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 3a3038ecf9..0eafe2503d 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -55,7 +55,7 @@ extern "C" { // This define specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "26" +#define NETWORK_STREAM_VERSION "27" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION #ifdef __cplusplus