From 6329e8fdfef7e5c821456ec71475f0ef2fda5434 Mon Sep 17 00:00:00 2001 From: Ted John Date: Wed, 14 Apr 2021 10:36:21 +0100 Subject: [PATCH] Reduce use of rct_object_entry --- .../windows/EditorObjectSelection.cpp | 4 +- src/openrct2/EditorObjectSelectionSession.cpp | 23 +- src/openrct2/EditorObjectSelectionSession.h | 2 - src/openrct2/core/DataSerialiserTraits.h | 95 +++--- src/openrct2/core/IStream.cpp | 14 +- src/openrct2/core/IStream.hpp | 2 + src/openrct2/core/String.hpp | 1 + src/openrct2/interface/Window_internal.h | 1 - src/openrct2/object/Object.cpp | 84 ++++- src/openrct2/object/Object.h | 42 ++- src/openrct2/object/ObjectList.cpp | 29 +- src/openrct2/object/ObjectList.h | 1 - src/openrct2/object/ObjectManager.cpp | 14 +- src/openrct2/object/ObjectRepository.cpp | 42 +-- src/openrct2/object/ObjectRepository.h | 2 + src/openrct2/rct2/T6Exporter.cpp | 2 +- src/openrct2/rct2/T6Importer.cpp | 2 +- src/openrct2/ride/TrackDesign.cpp | 67 ++-- src/openrct2/ride/TrackDesign.h | 15 +- src/openrct2/ride/TrackDesignSave.cpp | 290 ++++++++++-------- 20 files changed, 397 insertions(+), 335 deletions(-) diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index 2d02a5fc61..d9a9eaa02b 100644 --- a/src/openrct2-ui/windows/EditorObjectSelection.cpp +++ b/src/openrct2-ui/windows/EditorObjectSelection.cpp @@ -249,7 +249,6 @@ enum struct list_item { const ObjectRepositoryItem* repositoryItem; - rct_object_entry* entry; std::unique_ptr filter; uint8_t* flags; }; @@ -307,7 +306,6 @@ static void visible_list_refresh(rct_window* w) list_item currentListItem; currentListItem.repositoryItem = item; - currentListItem.entry = const_cast(&item->ObjectEntry); currentListItem.filter = std::move(filter); currentListItem.flags = &_objectSelectionFlags[i]; _listItems.push_back(std::move(currentListItem)); @@ -681,7 +679,7 @@ static void window_editor_object_selection_scroll_mousedown( if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) { - if (!window_editor_object_selection_select_object(0, INPUT_FLAG_EDITOR_OBJECT_SELECT, listItem->entry)) + if (!window_editor_object_selection_select_object(0, INPUT_FLAG_EDITOR_OBJECT_SELECT, listItem->repositoryItem)) return; // Close any other open windows such as options/colour schemes to prevent a crash. diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp index 9bf836f4b8..15c043bad7 100644 --- a/src/openrct2/EditorObjectSelectionSession.cpp +++ b/src/openrct2/EditorObjectSelectionSession.cpp @@ -380,7 +380,7 @@ static void window_editor_object_selection_select_default_objects() 0, INPUT_FLAG_EDITOR_OBJECT_SELECT | INPUT_FLAG_EDITOR_OBJECT_1 | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP, - defaultSelectedObject); + ObjectEntryDescriptor(defaultSelectedObject)); } } } @@ -395,7 +395,7 @@ static void SelectDesignerObjects() 0, INPUT_FLAG_EDITOR_OBJECT_SELECT | INPUT_FLAG_EDITOR_OBJECT_1 | INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP, - designerSelectedObject); + ObjectEntryDescriptor(designerSelectedObject)); } } } @@ -596,27 +596,14 @@ bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_ } } -bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, std::string_view identifier) +bool window_editor_object_selection_select_object( + uint8_t isMasterObject, int32_t flags, const ObjectEntryDescriptor& descriptor) { auto& objectRepository = OpenRCT2::GetContext()->GetObjectRepository(); - const auto* item = objectRepository.FindObject(identifier); + const auto* item = objectRepository.FindObject(descriptor); return window_editor_object_selection_select_object(isMasterObject, flags, item); } -bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const rct_object_entry* entry) -{ - const ObjectRepositoryItem* item = object_repository_find_object_by_entry(entry); - return window_editor_object_selection_select_object(isMasterObject, flags, item); -} - -bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const ObjectEntryDescriptor& entry) -{ - if (entry.Generation == ObjectGeneration::DAT) - return window_editor_object_selection_select_object(isMasterObject, flags, &entry.Entry); - - return window_editor_object_selection_select_object(isMasterObject, flags, entry.Identifier); -} - bool editor_check_object_group_at_least_one_selected(ObjectType checkObjectType) { auto numObjects = std::min(object_repository_get_items_count(), _objectSelectionFlags.size()); diff --git a/src/openrct2/EditorObjectSelectionSession.h b/src/openrct2/EditorObjectSelectionSession.h index 45f4f8b482..e03be7004e 100644 --- a/src/openrct2/EditorObjectSelectionSession.h +++ b/src/openrct2/EditorObjectSelectionSession.h @@ -34,8 +34,6 @@ void sub_6AB211(); void reset_selected_object_count_and_size(); void finish_object_selection(); bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const ObjectRepositoryItem* item); -bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, std::string_view identifier); -bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const rct_object_entry* entry); bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_t flags, const ObjectEntryDescriptor& entry); /** diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index 6128404106..4eab0df0ab 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -627,6 +627,50 @@ template<> struct DataSerializerTraits_t } }; +template<> struct DataSerializerTraits_t +{ + static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) + { + stream->WriteValue(static_cast(val.Generation)); + if (val.Generation == ObjectGeneration::DAT) + { + DataSerializerTraits s; + s.encode(stream, val.Entry); + } + else + { + stream->WriteValue(static_cast(val.GetType())); + stream->WriteString(val.Identifier); + } + } + + static void decode(OpenRCT2::IStream* stream, ObjectEntryDescriptor& val) + { + auto generation = static_cast(stream->ReadValue()); + if (generation == ObjectGeneration::DAT) + { + DataSerializerTraits s; + rct_object_entry entry; + s.decode(stream, entry); + val = ObjectEntryDescriptor(entry); + } + else + { + auto type = static_cast(stream->ReadValue()); + auto identifier = stream->ReadStdString(); + val = ObjectEntryDescriptor(type, identifier); + } + } + + static void log(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) + { + auto identifier = std::string(val.GetName()); + char msg[128] = {}; + snprintf(msg, sizeof(msg), "ObjectEntryDescriptor[%s]", identifier.c_str()); + stream->Write(msg, strlen(msg)); + } +}; + template<> struct DataSerializerTraits_t { static void encode(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) @@ -706,7 +750,7 @@ template<> struct DataSerializerTraits_t stream->Write(&val.flags); stream->Write(&val.primary_colour); stream->Write(&val.secondary_colour); - DataSerializerTraits s; + DataSerializerTraits s; s.encode(stream, val.scenery_object); } static void decode(OpenRCT2::IStream* stream, TrackDesignSceneryElement& val) @@ -717,7 +761,7 @@ template<> struct DataSerializerTraits_t stream->Read(&val.flags); stream->Read(&val.primary_colour); stream->Read(&val.secondary_colour); - DataSerializerTraits s; + DataSerializerTraits s; s.decode(stream, val.scenery_object); } static void log(OpenRCT2::IStream* stream, const TrackDesignSceneryElement& val) @@ -727,7 +771,9 @@ template<> struct DataSerializerTraits_t msg, sizeof(msg), "TrackDesignSceneryElement(x = %d, y = %d, z = %d, flags = %d, colour1 = %d, colour2 = %d)", val.x, val.y, val.z, val.flags, val.primary_colour, val.secondary_colour); stream->Write(msg, strlen(msg)); - stream->WriteArray(val.scenery_object.name, 8); + + auto identifier = val.scenery_object.GetName(); + stream->WriteArray(identifier.data(), identifier.size()); } }; @@ -750,46 +796,3 @@ template<> struct DataSerializerTraits_t stream->Write(msg, strlen(msg)); } }; - -template<> struct DataSerializerTraits_t -{ - static void encode(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) - { - stream->Write(&val.Generation); - if (val.Generation == ObjectGeneration::DAT) - { - DataSerializerTraits s; - s.encode(stream, val.Entry); - } - else - { - DataSerializerTraits s; - s.encode(stream, val.Identifier); - } - } - static void decode(OpenRCT2::IStream* stream, ObjectEntryDescriptor& val) - { - ObjectGeneration generation; - stream->Read(&generation); - if (generation == ObjectGeneration::DAT) - { - rct_object_entry obj; - DataSerializerTraits s; - s.decode(stream, obj); - val = ObjectEntryDescriptor(obj); - } - else - { - std::string id; - DataSerializerTraits s; - s.decode(stream, id); - val = ObjectEntryDescriptor(id); - } - } - static void log(OpenRCT2::IStream* stream, const ObjectEntryDescriptor& val) - { - char msg[128] = {}; - snprintf(msg, sizeof(msg), "ObjectEntryDescriptor (Generation = %d)", static_cast(val.Generation)); - stream->Write(msg, strlen(msg)); - } -}; diff --git a/src/openrct2/core/IStream.cpp b/src/openrct2/core/IStream.cpp index 88bf74d02e..4af586b1d5 100644 --- a/src/openrct2/core/IStream.cpp +++ b/src/openrct2/core/IStream.cpp @@ -12,9 +12,6 @@ #include "../object/Object.h" #include "Memory.hpp" #include "String.hpp" - -#include - namespace OpenRCT2 { utf8* IStream::ReadString() @@ -57,6 +54,17 @@ namespace OpenRCT2 } } + void IStream::WriteString(std::string_view str) + { + for (auto c : str) + { + if (c == '\0') + break; + WriteValue(c); + } + WriteValue(0); + } + void IStream::WriteString(const std::string& str) { WriteString(str.c_str()); diff --git a/src/openrct2/core/IStream.hpp b/src/openrct2/core/IStream.hpp index 6f751dc77c..a30aff6faf 100644 --- a/src/openrct2/core/IStream.hpp +++ b/src/openrct2/core/IStream.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #ifdef __WARN_SUGGEST_FINAL_METHODS__ @@ -207,6 +208,7 @@ namespace OpenRCT2 utf8* ReadString(); std::string ReadStdString(); void WriteString(const utf8* str); + void WriteString(std::string_view string); void WriteString(const std::string& string); }; diff --git a/src/openrct2/core/String.hpp b/src/openrct2/core/String.hpp index d4ce69210e..7525ca5c65 100644 --- a/src/openrct2/core/String.hpp +++ b/src/openrct2/core/String.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace CODE_PAGE diff --git a/src/openrct2/interface/Window_internal.h b/src/openrct2/interface/Window_internal.h index 0733c4290e..2864c94bb7 100644 --- a/src/openrct2/interface/Window_internal.h +++ b/src/openrct2/interface/Window_internal.h @@ -90,7 +90,6 @@ struct rct_window uint32_t highlighted_item; uint16_t ride_colour; ResearchItem* research_item; - rct_object_entry* object_entry; const scenario_index_entry* highlighted_scenario; uint16_t var_496; }; diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index 7a4c4f156b..dd016b7aed 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -35,7 +35,7 @@ ObjectType& operator++(ObjectType& d, int) ObjectEntryDescriptor::ObjectEntryDescriptor(const rct_object_entry& newEntry) { - if (!object_entry_is_empty(&newEntry)) + if (!newEntry.IsEmpty()) { Generation = ObjectGeneration::DAT; Entry = newEntry; @@ -48,6 +48,13 @@ ObjectEntryDescriptor::ObjectEntryDescriptor(std::string_view newIdentifier) Identifier = std::string(newIdentifier); } +ObjectEntryDescriptor::ObjectEntryDescriptor(ObjectType type, std::string_view newIdentifier) +{ + Generation = ObjectGeneration::JSON; + Identifier = std::string(newIdentifier); + Type = type; +} + ObjectEntryDescriptor::ObjectEntryDescriptor(const ObjectRepositoryItem& ori) { if (!ori.Identifier.empty()) @@ -77,8 +84,28 @@ std::string_view ObjectEntryDescriptor::GetName() const return Generation == ObjectGeneration::JSON ? Identifier : Entry.GetName(); } +bool ObjectEntryDescriptor::operator==(const ObjectEntryDescriptor& rhs) const +{ + if (Generation != rhs.Generation) + return false; + if (Generation == ObjectGeneration::DAT) + { + return Entry == rhs.Entry; + } + else + { + return Type == rhs.Type && Identifier == rhs.Identifier; + } +} + +bool ObjectEntryDescriptor::operator!=(const ObjectEntryDescriptor& rhs) const +{ + return !(*this == rhs); +} + Object::Object(const rct_object_entry& entry) { + _type = entry.GetType(); _objectEntry = entry; } @@ -215,6 +242,61 @@ std::optional rct_object_entry::GetSceneryType() const } } +bool rct_object_entry::IsEmpty() const +{ + uint64_t a, b; + std::memcpy(&a, reinterpret_cast(this), 8); + std::memcpy(&b, reinterpret_cast(this) + 8, 8); + + if (a == 0xFFFFFFFFFFFFFFFF && b == 0xFFFFFFFFFFFFFFFF) + return true; + if (a == 0 && b == 0) + return true; + return false; +} + +bool rct_object_entry::operator==(const rct_object_entry& rhs) const +{ + const auto a = this; + const auto b = &rhs; + + // If an official object don't bother checking checksum + if ((a->flags & 0xF0) || (b->flags & 0xF0)) + { + if (a->GetType() != b->GetType()) + { + return false; + } + int32_t match = memcmp(a->name, b->name, 8); + if (match) + { + return false; + } + } + else + { + if (a->flags != b->flags) + { + return false; + } + int32_t match = memcmp(a->name, b->name, 8); + if (match) + { + return false; + } + if (a->checksum != b->checksum) + { + return false; + } + } + return true; +} + +bool rct_object_entry::operator!=(const rct_object_entry& rhs) const +{ + return !(*this == rhs); +} + /** * Couples a zip archive and a zip item stream to ensure the lifetime of the zip archive is maintained * for the lifetime of the stream. diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index c10db62eb2..6eb0142414 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -123,6 +123,10 @@ struct rct_object_entry { return static_cast((flags & 0xF0) >> 4); } + + bool IsEmpty() const; + bool operator==(const rct_object_entry& rhs) const; + bool operator!=(const rct_object_entry& rhs) const; }; assert_struct_size(rct_object_entry, 0x10); @@ -173,10 +177,14 @@ struct ObjectEntryDescriptor ObjectEntryDescriptor() = default; explicit ObjectEntryDescriptor(const rct_object_entry& newEntry); explicit ObjectEntryDescriptor(std::string_view newIdentifier); + explicit ObjectEntryDescriptor(ObjectType type, std::string_view newIdentifier); explicit ObjectEntryDescriptor(const ObjectRepositoryItem& ori); bool HasValue() const; ObjectType GetType() const; std::string_view GetName() const; + + bool operator==(const ObjectEntryDescriptor& rhs) const; + bool operator!=(const ObjectEntryDescriptor& rhs) const; }; struct IObjectRepository; @@ -243,13 +251,14 @@ struct IReadObjectContext class Object { private: + ObjectType _type = ObjectType::None; std::string _identifier; rct_object_entry _objectEntry{}; StringTable _stringTable; ImageTable _imageTable; std::vector _sourceGames; std::vector _authors; - bool _isJsonObject{}; + ObjectGeneration _generation{}; protected: StringTable& GetStringTable() @@ -294,20 +303,29 @@ public: void MarkAsJsonObject() { - _isJsonObject = true; + _generation = ObjectGeneration::JSON; } - bool IsJsonObject() const + ObjectGeneration GetGeneration() const { - return _isJsonObject; + return _generation; }; + ObjectType GetObjectType() const + { + return _type; + } + ObjectEntryDescriptor GetDescriptor() const { - if (_isJsonObject) - return ObjectEntryDescriptor(_identifier); - else + if (_generation == ObjectGeneration::DAT) + { return ObjectEntryDescriptor(_objectEntry); + } + else + { + return ObjectEntryDescriptor(_type, _identifier); + } } // Legacy data structures @@ -315,9 +333,9 @@ public: { return _objectEntry.GetName(); } - const rct_object_entry* GetObjectEntry() const + const rct_object_entry& GetObjectEntry() const { - return &_objectEntry; + return _objectEntry; } virtual void* GetLegacyData(); @@ -335,10 +353,6 @@ public: { } - virtual ObjectType GetObjectType() const final - { - return _objectEntry.GetType(); - } virtual std::string GetName() const; virtual std::string GetName(int32_t language) const; @@ -372,8 +386,6 @@ public: extern int32_t object_entry_group_counts[]; extern int32_t object_entry_group_encoding[]; -bool object_entry_is_empty(const rct_object_entry* entry); -bool object_entry_compare(const rct_object_entry* a, const rct_object_entry* b); int32_t object_calculate_checksum(const rct_object_entry* entry, const void* data, size_t dataLength); bool find_object_in_entry_group(const rct_object_entry* entry, ObjectType* entry_type, ObjectEntryIndex* entryIndex); void object_create_identifier_name(char* string_buffer, size_t size, const rct_object_entry* object); diff --git a/src/openrct2/object/ObjectList.cpp b/src/openrct2/object/ObjectList.cpp index 15fd10efdf..b216b986a3 100644 --- a/src/openrct2/object/ObjectList.cpp +++ b/src/openrct2/object/ObjectList.cpp @@ -174,19 +174,6 @@ void ObjectList::SetObject(ObjectType type, ObjectEntryIndex index, std::string_ SetObject(index, entry); } -bool object_entry_is_empty(const rct_object_entry* entry) -{ - uint64_t a, b; - std::memcpy(&a, reinterpret_cast(entry), 8); - std::memcpy(&b, reinterpret_cast(entry) + 8, 8); - - if (a == 0xFFFFFFFFFFFFFFFF && b == 0xFFFFFFFFFFFFFFFF) - return true; - if (a == 0 && b == 0) - return true; - return false; -} - /** * * rct2: 0x006AB344 @@ -218,7 +205,7 @@ bool find_object_in_entry_group(const rct_object_entry* entry, ObjectType* entry if (loadedObj != nullptr) { auto thisEntry = object_entry_get_object(objectType, i)->GetObjectEntry(); - if (object_entry_compare(thisEntry, entry)) + if (thisEntry == *entry) { *entry_type = objectType; *entryIndex = i; @@ -251,20 +238,6 @@ void get_type_entry_index(size_t index, ObjectType* outObjectType, ObjectEntryIn *outEntryIndex = static_cast(index); } -const rct_object_entry* get_loaded_object_entry(size_t index) -{ - ObjectType objectType; - ObjectEntryIndex entryIndex; - get_type_entry_index(index, &objectType, &entryIndex); - auto obj = object_entry_get_object(objectType, entryIndex); - if (obj == nullptr) - { - return nullptr; - } - - return obj->GetObjectEntry(); -} - void* get_loaded_object_chunk(size_t index) { ObjectType objectType; diff --git a/src/openrct2/object/ObjectList.h b/src/openrct2/object/ObjectList.h index 532b1656a6..ba9a560aa5 100644 --- a/src/openrct2/object/ObjectList.h +++ b/src/openrct2/object/ObjectList.h @@ -56,5 +56,4 @@ public: }; void get_type_entry_index(size_t index, ObjectType* outObjectType, ObjectEntryIndex* outEntryIndex); -const rct_object_entry* get_loaded_object_entry(size_t index); void* get_loaded_object_chunk(size_t index); diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index ea17132c07..22f7db8239 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -123,17 +123,7 @@ public: auto obj = GetLoadedObject(objectType, i); if (obj != nullptr) { - if (obj->IsJsonObject()) - { - auto entry = ObjectEntryDescriptor(obj->GetIdentifier()); - entry.Type = obj->GetObjectType(); - objectList.SetObject(i, entry); - } - else - { - auto entry = ObjectEntryDescriptor(*obj->GetObjectEntry()); - objectList.SetObject(i, entry); - } + objectList.SetObject(i, obj->GetDescriptor()); } } } @@ -374,7 +364,7 @@ private: object->Unload(); // TODO try to prevent doing a repository search - const ObjectRepositoryItem* ori = _objectRepository.FindObject(object->GetObjectEntry()); + const auto* ori = _objectRepository.FindObject(object->GetDescriptor()); if (ori != nullptr) { _objectRepository.UnregisterLoadedObject(ori, object); diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 1ebff6acbb..d6b1024074 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -76,7 +76,7 @@ class ObjectFileIndex final : public FileIndex { private: static constexpr uint32_t MAGIC_NUMBER = 0x5844494F; // OIDX - static constexpr uint16_t VERSION = 27; + static constexpr uint16_t VERSION = 28; static constexpr auto PATTERN = "*.dat;*.pob;*.json;*.parkobj"; IObjectRepository& _objectRepository; @@ -113,8 +113,10 @@ public: if (object != nullptr) { ObjectRepositoryItem item = {}; + item.Type = object->GetObjectType(); + item.Generation = object->GetGeneration(); item.Identifier = object->GetIdentifier(); - item.ObjectEntry = *object->GetObjectEntry(); + item.ObjectEntry = object->GetObjectEntry(); item.Path = path; item.Name = object->GetName(); item.Authors = object->GetAuthors(); @@ -622,7 +624,7 @@ private: // Read object data from file auto fs = OpenRCT2::FileStream(item->Path, OpenRCT2::FILE_MODE_OPEN); auto fileEntry = fs.ReadValue(); - if (!object_entry_compare(entry, &fileEntry)) + if (*entry != fileEntry) { throw std::runtime_error("Header found in object file does not match object to pack."); } @@ -733,40 +735,6 @@ const ObjectRepositoryItem* object_repository_find_object_by_name(const char* na return objectRepository.FindObjectLegacy(name); } -bool object_entry_compare(const rct_object_entry* a, const rct_object_entry* b) -{ - // If an official object don't bother checking checksum - if ((a->flags & 0xF0) || (b->flags & 0xF0)) - { - if (a->GetType() != b->GetType()) - { - return false; - } - int32_t match = memcmp(a->name, b->name, 8); - if (match) - { - return false; - } - } - else - { - if (a->flags != b->flags) - { - return false; - } - int32_t match = memcmp(a->name, b->name, 8); - if (match) - { - return false; - } - if (a->checksum != b->checksum) - { - return false; - } - } - return true; -} - int32_t object_calculate_checksum(const rct_object_entry* entry, const void* data, size_t dataLength) { const uint8_t* entryBytePtr = reinterpret_cast(entry); diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index ed01aab083..c8bb70b978 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -37,6 +37,8 @@ struct rct_drawpixelinfo; struct ObjectRepositoryItem { size_t Id; + ObjectType Type; + ObjectGeneration Generation; std::string Identifier; // e.g. rct2.c3d rct_object_entry ObjectEntry; std::string Path; diff --git a/src/openrct2/rct2/T6Exporter.cpp b/src/openrct2/rct2/T6Exporter.cpp index be88bc5ce1..33554c5a83 100644 --- a/src/openrct2/rct2/T6Exporter.cpp +++ b/src/openrct2/rct2/T6Exporter.cpp @@ -123,7 +123,7 @@ bool T6Exporter::SaveTrack(OpenRCT2::IStream* stream) for (const auto& sceneryElement : _trackDesign->scenery_elements) { - tempStream.Write(&sceneryElement.scenery_object, sizeof(rct_object_entry)); + tempStream.Write(&sceneryElement.scenery_object.Entry, sizeof(rct_object_entry)); tempStream.WriteValue(sceneryElement.x); tempStream.WriteValue(sceneryElement.y); tempStream.WriteValue(sceneryElement.z); diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp index 5893b357f4..e9e22909dc 100644 --- a/src/openrct2/rct2/T6Importer.cpp +++ b/src/openrct2/rct2/T6Importer.cpp @@ -202,7 +202,7 @@ public: rct_td6_scenery_element t6SceneryElement{}; _stream.Read(&t6SceneryElement, sizeof(rct_td6_scenery_element)); TrackDesignSceneryElement sceneryElement{}; - sceneryElement.scenery_object = t6SceneryElement.scenery_object; + sceneryElement.scenery_object = ObjectEntryDescriptor(t6SceneryElement.scenery_object); sceneryElement.x = t6SceneryElement.x; sceneryElement.y = t6SceneryElement.y; sceneryElement.z = t6SceneryElement.z; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index a2724d774b..3aef20101a 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -90,11 +90,18 @@ static void track_design_preview_clear_map(); rct_string_id TrackDesign::CreateTrackDesign(const Ride& ride) { type = ride.type; - auto object = object_entry_get_object(ObjectType::Ride, ride.subtype); - // Note we are only copying rct_object_entry in size and - // not the extended as we don't need the chunk size. - vehicle_object = ObjectEntryDescriptor(*object->GetObjectEntry()); + auto object = object_entry_get_object(ObjectType::Ride, ride.subtype); + if (object != nullptr) + { + auto entry = object->GetObjectEntry(); + if (entry.IsEmpty()) + { + // TODO create a new error message for `JSON objects are unsupported` + return STR_UNKNOWN_OBJECT_TYPE; + } + vehicle_object = ObjectEntryDescriptor(entry); + } ride_mode = ride.mode; colour_scheme = ride.colour_scheme_type & 3; @@ -627,17 +634,22 @@ std::unique_ptr track_design_open(const utf8* path) */ static void track_design_load_scenery_objects(TrackDesign* td6) { - object_manager_unload_all_objects(); + auto& objectManager = OpenRCT2::GetContext()->GetObjectManager(); + objectManager.UnloadAll(); // Load ride object - rct_object_entry* rideEntry = &td6->vehicle_object.Entry; - object_manager_load_object(rideEntry); + if (td6->vehicle_object.HasValue()) + { + objectManager.LoadObject(td6->vehicle_object); + } // Load scenery objects for (const auto& scenery : td6->scenery_elements) { - const rct_object_entry* sceneryEntry = &scenery.scenery_object; - object_manager_load_object(sceneryEntry); + if (scenery.scenery_object.HasValue()) + { + objectManager.LoadObject(td6->vehicle_object); + } } } @@ -647,28 +659,20 @@ static void track_design_load_scenery_objects(TrackDesign* td6) */ static void track_design_mirror_scenery(TrackDesign* td6) { + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); for (auto& scenery : td6->scenery_elements) { - ObjectType entry_type{ 0 }; - ObjectEntryIndex entryIndex{ 0 }; - if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entryIndex)) - { - entry_type = scenery.scenery_object.GetType(); - if (entry_type != ObjectType::Paths) - { - continue; - } + auto obj = objectMgr.GetLoadedObject(scenery.scenery_object); + if (obj == nullptr) + continue; - entryIndex = 0; - } - - rct_scenery_entry* scenery_entry = static_cast(object_entry_get_chunk(entry_type, entryIndex)); - switch (entry_type) + switch (obj->GetObjectType()) { case ObjectType::LargeScenery: { + auto sceneryEntry = reinterpret_cast(obj->GetLegacyData()); int16_t x1 = 0, x2 = 0, y1 = 0, y2 = 0; - for (rct_large_scenery_tile* tile = scenery_entry->large_scenery.tiles; tile->x_offset != -1; tile++) + for (rct_large_scenery_tile* tile = sceneryEntry->large_scenery.tiles; tile->x_offset != -1; tile++) { if (x1 > tile->x_offset) { @@ -711,12 +715,13 @@ static void track_design_mirror_scenery(TrackDesign* td6) } case ObjectType::SmallScenery: { + auto sceneryEntry = reinterpret_cast(obj->GetLegacyData()); scenery.y = -scenery.y; - if (scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_DIAGONAL)) + if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_DIAGONAL)) { scenery.flags ^= (1 << 0); - if (!scenery_small_entry_has_flag(scenery_entry, SMALL_SCENERY_FLAG_FULL_TILE)) + if (!scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_FULL_TILE)) { scenery.flags ^= (1 << 2); } @@ -755,7 +760,6 @@ static void track_design_mirror_scenery(TrackDesign* td6) break; } default: - // This switch processes only ObjectType for Scenery items. break; } } @@ -854,7 +858,14 @@ static void track_design_update_max_min_coordinates(const CoordsXYZ& coords) static bool TrackDesignPlaceSceneryElementGetEntry( ObjectType& entry_type, ObjectEntryIndex& entry_index, const TrackDesignSceneryElement& scenery) { - if (!find_object_in_entry_group(&scenery.scenery_object, &entry_type, &entry_index)) + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objectMgr.GetLoadedObject(scenery.scenery_object); + if (obj != nullptr) + { + entry_type = obj->GetObjectType(); + entry_index = objectMgr.GetLoadedObjectEntryIndex(obj); + } + else { entry_type = scenery.scenery_object.GetType(); if (entry_type != ObjectType::Paths) diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index da23940173..6fb3432d99 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -30,16 +30,15 @@ struct TrackDesignEntranceElement bool isExit; }; -/* Track Scenery entry size: 0x16 */ struct TrackDesignSceneryElement { - rct_object_entry scenery_object; // 0x00 - int8_t x; // 0x10 - int8_t y; // 0x11 - int8_t z; // 0x12 - uint8_t flags; // 0x13 direction quadrant tertiary colour - uint8_t primary_colour; // 0x14 - uint8_t secondary_colour; // 0x15 + ObjectEntryDescriptor scenery_object; + int8_t x; + int8_t y; + int8_t z; + uint8_t flags; + uint8_t primary_colour; + uint8_t secondary_colour; }; /** diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index 3a8a7b2ef3..7d38310c0d 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -14,7 +14,9 @@ #include "../interface/Viewport.h" #include "../localisation/Localisation.h" #include "../localisation/StringIds.h" +#include "../object/LargeSceneryObject.h" #include "../object/ObjectList.h" +#include "../object/ObjectManager.h" #include "../util/SawyerCoding.h" #include "../util/Util.h" #include "../windows/Intent.h" @@ -188,16 +190,29 @@ static void track_design_save_push_tile_element(const CoordsXY& loc, TileElement } } +static bool track_design_is_supported_object(const Object* obj) +{ + const auto& entry = obj->GetObjectEntry(); + return !entry.IsEmpty(); +} + /** * * rct2: 0x006D2FA7 */ static void track_design_save_push_tile_element_desc( - const rct_object_entry* entry, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) + const Object* obj, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { + const auto& entry = obj->GetObjectEntry(); + if (entry.IsEmpty()) + { + // Unsupported, should have been blocked earlier + assert(false); + } + auto tileLoc = TileCoordsXYZ(loc); TrackDesignSceneryElement item{}; - item.scenery_object = *entry; + item.scenery_object = ObjectEntryDescriptor(entry); item.x = tileLoc.x; item.y = tileLoc.y; item.z = tileLoc.z; @@ -210,105 +225,112 @@ static void track_design_save_push_tile_element_desc( static void track_design_save_add_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) { - int32_t entryType = sceneryElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::SmallScenery, entryType); + auto entryIndex = sceneryElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::SmallScenery, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) + { + uint8_t flags = 0; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; - uint8_t flags = 0; - flags |= sceneryElement->GetDirection(); - flags |= sceneryElement->GetSceneryQuadrant() << 2; + uint8_t primaryColour = sceneryElement->GetPrimaryColour(); + uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); - uint8_t primaryColour = sceneryElement->GetPrimaryColour(); - uint8_t secondaryColour = sceneryElement->GetSecondaryColour(); - - track_design_save_push_tile_element(loc, reinterpret_cast(sceneryElement)); - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_push_tile_element_desc( + obj, { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + } } static void track_design_save_add_large_scenery(const CoordsXY& loc, LargeSceneryElement* tileElement) { - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t direction, sequence; - if (tileElement == nullptr) { log_warning("Null tile element"); return; } - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::LargeScenery, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; - - int32_t z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - auto sceneryOrigin = map_large_scenery_get_origin( - { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); - if (!sceneryOrigin) + auto entryIndex = tileElement->GetEntryIndex(); + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objectMgr.GetLoadedObject(ObjectType::LargeScenery, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) { - return; - } + auto legacyData = reinterpret_cast(obj->GetLegacyData()); + auto sceneryTiles = legacyData->large_scenery.tiles; - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; - auto rotatedOffsetPos = offsetPos.Rotate(direction); + int32_t z = tileElement->base_height; + auto direction = tileElement->GetDirection(); + auto sequence = tileElement->GetSequenceIndex(); - CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, - sceneryOrigin->z + tile->z_offset }; - auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); - if (largeElement != nullptr) + auto sceneryOrigin = map_large_scenery_get_origin( + { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); + if (!sceneryOrigin) { - if (sequence == 0) - { - uint8_t flags = largeElement->GetDirection(); - uint8_t primaryColour = largeElement->GetPrimaryColour(); - uint8_t secondaryColour = largeElement->GetSecondaryColour(); + return; + } - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), tileLoc, flags, primaryColour, secondaryColour); + // Iterate through each tile of the large scenery element + sequence = 0; + for (auto tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) + { + CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; + auto rotatedOffsetPos = offsetPos.Rotate(direction); + + CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, + sceneryOrigin->z + tile->z_offset }; + auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); + if (largeElement != nullptr) + { + if (sequence == 0) + { + uint8_t flags = largeElement->GetDirection(); + uint8_t primaryColour = largeElement->GetPrimaryColour(); + uint8_t secondaryColour = largeElement->GetSecondaryColour(); + + track_design_save_push_tile_element_desc(obj, tileLoc, flags, primaryColour, secondaryColour); + } + track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } - track_design_save_push_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } } } static void track_design_save_add_wall(const CoordsXY& loc, WallElement* wallElement) { - int32_t entryType = wallElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Walls, entryType); + auto entryIndex = wallElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Walls, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) + { + uint8_t flags = 0; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; - uint8_t flags = 0; - flags |= wallElement->GetDirection(); - flags |= wallElement->GetTertiaryColour() << 2; + uint8_t secondaryColour = wallElement->GetSecondaryColour(); + uint8_t primaryColour = wallElement->GetPrimaryColour(); - uint8_t secondaryColour = wallElement->GetSecondaryColour(); - uint8_t primaryColour = wallElement->GetPrimaryColour(); - - track_design_save_push_tile_element(loc, reinterpret_cast(wallElement)); - track_design_save_push_tile_element_desc( - entry->GetObjectEntry(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + track_design_save_push_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_push_tile_element_desc( + obj, { loc.x, loc.y, wallElement->GetBaseZ() }, flags, primaryColour, secondaryColour); + } } static void track_design_save_add_footpath(const CoordsXY& loc, PathElement* pathElement) { - int32_t entryType = pathElement->GetSurfaceEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Paths, entryType); + auto entryIndex = pathElement->GetSurfaceEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Paths, entryIndex); + if (obj != nullptr && track_design_is_supported_object(obj)) + { + uint8_t flags = 0; + flags |= pathElement->GetEdges(); + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsSloped()) + flags |= 0b00010000; + if (pathElement->IsQueue()) + flags |= 1 << 7; - uint8_t flags = 0; - flags |= pathElement->GetEdges(); - flags |= (pathElement->GetSlopeDirection()) << 5; - if (pathElement->IsSloped()) - flags |= 0b00010000; - if (pathElement->IsQueue()) - flags |= 1 << 7; - - track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); - track_design_save_push_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); + track_design_save_push_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_push_tile_element_desc(obj, { loc.x, loc.y, pathElement->GetBaseZ() }, flags, 0, 0); + } } /** @@ -370,7 +392,7 @@ static void track_design_save_pop_tile_element(const CoordsXY& loc, TileElement* * * rct2: 0x006D2FDD */ -static void track_design_save_pop_tile_element_desc(const rct_object_entry* entry, const CoordsXYZ& loc, uint8_t flags) +static void track_design_save_pop_tile_element_desc(const ObjectEntryDescriptor& entry, const CoordsXYZ& loc, uint8_t flags) { size_t removeIndex = SIZE_MAX; auto tileLoc = TileCoordsXYZ(loc); @@ -385,7 +407,7 @@ static void track_design_save_pop_tile_element_desc(const rct_object_entry* entr continue; if (item->flags != flags) continue; - if (!object_entry_compare(&item->scenery_object, entry)) + if (item->scenery_object != entry) continue; removeIndex = i; @@ -399,93 +421,101 @@ static void track_design_save_pop_tile_element_desc(const rct_object_entry* entr static void track_design_save_remove_scenery(const CoordsXY& loc, SmallSceneryElement* sceneryElement) { - int32_t entryType = sceneryElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::SmallScenery, entryType); + auto entryIndex = sceneryElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::SmallScenery, entryIndex); + if (obj != nullptr) + { + uint8_t flags = 0; + flags |= sceneryElement->GetDirection(); + flags |= sceneryElement->GetSceneryQuadrant() << 2; - uint8_t flags = 0; - flags |= sceneryElement->GetDirection(); - flags |= sceneryElement->GetSceneryQuadrant() << 2; - - track_design_save_pop_tile_element(loc, reinterpret_cast(sceneryElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(sceneryElement)); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), { loc.x, loc.y, sceneryElement->GetBaseZ() }, flags); + } } static void track_design_save_remove_large_scenery(const CoordsXY& loc, LargeSceneryElement* tileElement) { - rct_large_scenery_tile *sceneryTiles, *tile; - int32_t direction, sequence; - if (tileElement == nullptr) { log_warning("Null tile element"); return; } - int32_t entryType = tileElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::LargeScenery, entryType); - sceneryTiles = get_large_scenery_entry(entryType)->large_scenery.tiles; - - int32_t z = tileElement->base_height; - direction = tileElement->GetDirection(); - sequence = tileElement->GetSequenceIndex(); - - auto sceneryOrigin = map_large_scenery_get_origin( - { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); - if (!sceneryOrigin) + auto entryIndex = tileElement->GetEntryIndex(); + auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objectMgr.GetLoadedObject(ObjectType::LargeScenery, entryIndex); + if (obj != nullptr) { - return; - } + auto legacyData = reinterpret_cast(obj->GetLegacyData()); + auto sceneryTiles = legacyData->large_scenery.tiles; - // Iterate through each tile of the large scenery element - sequence = 0; - for (tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) - { - CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; - auto rotatedOffsetPos = offsetPos.Rotate(direction); + int32_t z = tileElement->base_height; + auto direction = tileElement->GetDirection(); + auto sequence = tileElement->GetSequenceIndex(); - CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, - sceneryOrigin->z + tile->z_offset }; - auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); - if (largeElement != nullptr) + auto sceneryOrigin = map_large_scenery_get_origin( + { loc.x, loc.y, z << 3, static_cast(direction) }, sequence, nullptr); + if (!sceneryOrigin) { - if (sequence == 0) + return; + } + + // Iterate through each tile of the large scenery element + sequence = 0; + for (auto tile = sceneryTiles; tile->x_offset != -1; tile++, sequence++) + { + CoordsXY offsetPos{ tile->x_offset, tile->y_offset }; + auto rotatedOffsetPos = offsetPos.Rotate(direction); + + CoordsXYZ tileLoc = { sceneryOrigin->x + rotatedOffsetPos.x, sceneryOrigin->y + rotatedOffsetPos.y, + sceneryOrigin->z + tile->z_offset }; + auto largeElement = map_get_large_scenery_segment({ tileLoc, static_cast(direction) }, sequence); + if (largeElement != nullptr) { - uint8_t flags = largeElement->GetDirection(); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), tileLoc, flags); + if (sequence == 0) + { + uint8_t flags = largeElement->GetDirection(); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), tileLoc, flags); + } + track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } - track_design_save_pop_tile_element({ tileLoc.x, tileLoc.y }, reinterpret_cast(largeElement)); } } } static void track_design_save_remove_wall(const CoordsXY& loc, WallElement* wallElement) { - int32_t entryType = wallElement->GetEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Walls, entryType); + auto entryIndex = wallElement->GetEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Walls, entryIndex); + if (obj != nullptr) + { + uint8_t flags = 0; + flags |= wallElement->GetDirection(); + flags |= wallElement->GetTertiaryColour() << 2; - uint8_t flags = 0; - flags |= wallElement->GetDirection(); - flags |= wallElement->GetTertiaryColour() << 2; - - track_design_save_pop_tile_element(loc, reinterpret_cast(wallElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(wallElement)); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), { loc.x, loc.y, wallElement->GetBaseZ() }, flags); + } } static void track_design_save_remove_footpath(const CoordsXY& loc, PathElement* pathElement) { - int32_t entryType = pathElement->GetSurfaceEntryIndex(); - auto entry = object_entry_get_object(ObjectType::Paths, entryType); + auto entryIndex = pathElement->GetSurfaceEntryIndex(); + auto obj = object_entry_get_object(ObjectType::Paths, entryIndex); + if (obj != nullptr) + { + uint8_t flags = 0; + flags |= pathElement->GetEdges(); + if (pathElement->IsSloped()) + flags |= (1 << 4); + flags |= (pathElement->GetSlopeDirection()) << 5; + if (pathElement->IsQueue()) + flags |= (1 << 7); - uint8_t flags = 0; - flags |= pathElement->GetEdges(); - if (pathElement->IsSloped()) - flags |= (1 << 4); - flags |= (pathElement->GetSlopeDirection()) << 5; - if (pathElement->IsQueue()) - flags |= (1 << 7); - - track_design_save_pop_tile_element(loc, reinterpret_cast(pathElement)); - track_design_save_pop_tile_element_desc(entry->GetObjectEntry(), { loc.x, loc.y, pathElement->GetBaseZ() }, flags); + track_design_save_pop_tile_element(loc, reinterpret_cast(pathElement)); + track_design_save_pop_tile_element_desc(obj->GetDescriptor(), { loc.x, loc.y, pathElement->GetBaseZ() }, flags); + } } /**