From bdab3219cb5110a11c4abc64bd11b6074ab8261c Mon Sep 17 00:00:00 2001 From: Gymnasiast Date: Sun, 12 Sep 2021 21:25:22 +0200 Subject: [PATCH] Modify object loading for JSON-only objects --- src/openrct2-ui/WindowManager.cpp | 2 +- src/openrct2-ui/scripting/ScTitleSequence.hpp | 2 +- src/openrct2-ui/title/TitleSequencePlayer.cpp | 4 +- .../windows/EditorObjectSelection.cpp | 4 +- src/openrct2-ui/windows/InstallTrack.cpp | 4 +- src/openrct2-ui/windows/ObjectLoadError.cpp | 90 +++++------- src/openrct2-ui/windows/TitleEditor.cpp | 2 +- src/openrct2-ui/windows/Window.h | 2 +- src/openrct2/Context.cpp | 9 +- src/openrct2/EditorObjectSelectionSession.cpp | 134 +++++++++-------- src/openrct2/EditorObjectSelectionSession.h | 3 +- src/openrct2/ParkImporter.h | 10 +- src/openrct2/ReplayManager.cpp | 2 +- src/openrct2/actions/TrackDesignAction.cpp | 40 +++-- src/openrct2/network/NetworkBase.cpp | 2 +- src/openrct2/object/BannerObject.h | 5 - src/openrct2/object/EntranceObject.h | 5 - src/openrct2/object/FootpathItemObject.h | 5 - src/openrct2/object/FootpathObject.h | 5 - src/openrct2/object/FootpathRailingsObject.h | 5 - src/openrct2/object/FootpathSurfaceObject.h | 5 - src/openrct2/object/LargeSceneryObject.h | 5 - src/openrct2/object/MusicObject.h | 5 - src/openrct2/object/Object.cpp | 138 ++++++++++++++++-- src/openrct2/object/Object.h | 80 +++++----- src/openrct2/object/ObjectFactory.cpp | 71 +++++---- src/openrct2/object/ObjectFactory.h | 3 +- src/openrct2/object/ObjectList.cpp | 137 +++++++++++++++-- src/openrct2/object/ObjectList.h | 39 +++++ src/openrct2/object/ObjectManager.cpp | 121 ++++++++------- src/openrct2/object/ObjectManager.h | 8 +- src/openrct2/object/ObjectRepository.cpp | 122 ++++++++-------- src/openrct2/object/ObjectRepository.h | 5 +- src/openrct2/object/RideObject.h | 5 - src/openrct2/object/SceneryGroupObject.h | 5 - src/openrct2/object/SceneryObject.h | 4 - src/openrct2/object/SmallSceneryObject.h | 5 - src/openrct2/object/StationObject.h | 5 - src/openrct2/object/TerrainEdgeObject.h | 5 - src/openrct2/object/TerrainSurfaceObject.h | 5 - src/openrct2/object/WallObject.h | 5 - src/openrct2/object/WaterObject.h | 5 - src/openrct2/platform/platform.h | 1 - src/openrct2/rct1/S4Importer.cpp | 15 +- src/openrct2/rct1/T4Importer.cpp | 2 +- src/openrct2/rct2/S6Exporter.cpp | 4 +- src/openrct2/rct2/S6Importer.cpp | 15 +- src/openrct2/rct2/T6Importer.cpp | 4 +- src/openrct2/ride/TrackDesign.cpp | 36 +++-- src/openrct2/ride/TrackDesign.h | 2 +- src/openrct2/ride/TrackDesignRepository.cpp | 2 +- src/openrct2/ride/TrackDesignSave.cpp | 8 +- 52 files changed, 707 insertions(+), 500 deletions(-) diff --git a/src/openrct2-ui/WindowManager.cpp b/src/openrct2-ui/WindowManager.cpp index 4b9366ecb1..be2ace022c 100644 --- a/src/openrct2-ui/WindowManager.cpp +++ b/src/openrct2-ui/WindowManager.cpp @@ -256,7 +256,7 @@ public: case WC_OBJECT_LOAD_ERROR: { std::string path = intent->GetStringExtra(INTENT_EXTRA_PATH); - const rct_object_entry* objects = static_cast(intent->GetPointerExtra(INTENT_EXTRA_LIST)); + auto objects = static_cast(intent->GetPointerExtra(INTENT_EXTRA_LIST)); size_t count = intent->GetUIntExtra(INTENT_EXTRA_LIST_COUNT); window_object_load_error_open(const_cast(path.c_str()), count, objects); diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 5c53bd8c67..0d9e80db33 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -210,7 +210,7 @@ namespace OpenRCT2::Scripting auto& objectMgr = GetContext()->GetObjectManager(); auto parkImporter = ParkImporter::Create(handle->HintPath); auto result = parkImporter->LoadFromStream(handle->Stream.get(), isScenario); - objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectMgr.LoadObjects(result.RequiredObjects); parkImporter->Import(); auto old = gLoadKeepWindowsOpen; diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 8a624e6bd1..e9271b8212 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -374,7 +374,7 @@ private: auto result = parkImporter->Load(path); auto& objectManager = GetContext()->GetObjectManager(); - objectManager.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectManager.LoadObjects(result.RequiredObjects); parkImporter->Import(); } @@ -413,7 +413,7 @@ private: auto result = parkImporter->LoadFromStream(stream, isScenario); auto& objectManager = GetContext()->GetObjectManager(); - objectManager.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectManager.LoadObjects(result.RequiredObjects); parkImporter->Import(); } diff --git a/src/openrct2-ui/windows/EditorObjectSelection.cpp b/src/openrct2-ui/windows/EditorObjectSelection.cpp index 489f8e85ef..15aeeb1dd1 100644 --- a/src/openrct2-ui/windows/EditorObjectSelection.cpp +++ b/src/openrct2-ui/windows/EditorObjectSelection.cpp @@ -694,7 +694,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. @@ -712,7 +712,7 @@ static void window_editor_object_selection_scroll_mousedown( flags |= INPUT_FLAG_EDITOR_OBJECT_SELECT; _maxObjectsWasHit = false; - if (!window_editor_object_selection_select_object(0, flags, listItem->entry)) + if (!window_editor_object_selection_select_object(0, flags, listItem->repositoryItem)) { rct_string_id error_title = (flags & INPUT_FLAG_EDITOR_OBJECT_SELECT) ? STR_UNABLE_TO_SELECT_THIS_OBJECT : STR_UNABLE_TO_DE_SELECT_THIS_OBJECT; diff --git a/src/openrct2-ui/windows/InstallTrack.cpp b/src/openrct2-ui/windows/InstallTrack.cpp index 739e722b5f..bdf2613455 100644 --- a/src/openrct2-ui/windows/InstallTrack.cpp +++ b/src/openrct2-ui/windows/InstallTrack.cpp @@ -94,7 +94,7 @@ rct_window* window_install_track_open(const utf8* path) log_error("Failed to load track (ride type null): %s", path); return nullptr; } - if (object_manager_load_object(&_trackDesign->vehicle_object) == nullptr) + if (object_manager_load_object(&_trackDesign->vehicle_object.Entry) == nullptr) { log_error("Failed to load track (vehicle load fail): %s", path); return nullptr; @@ -242,7 +242,7 @@ static void window_install_track_paint(rct_window* w, rct_drawpixelinfo* dpi) { auto ft = Formatter(); - const auto* objectEntry = object_manager_load_object(&td6->vehicle_object); + const auto* objectEntry = object_manager_load_object(&td6->vehicle_object.Entry); if (objectEntry != nullptr) { auto groupIndex = object_manager_get_loaded_object_entry_index(objectEntry); diff --git a/src/openrct2-ui/windows/ObjectLoadError.cpp b/src/openrct2-ui/windows/ObjectLoadError.cpp index 7c7ea87945..cf7f56037a 100644 --- a/src/openrct2-ui/windows/ObjectLoadError.cpp +++ b/src/openrct2-ui/windows/ObjectLoadError.cpp @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include @@ -48,8 +50,8 @@ private: } }; - std::vector _entries; - std::vector _downloadedEntries; + std::vector _entries; + std::vector _downloadedEntries; size_t _currentDownloadIndex{}; std::mutex _downloadedEntriesMutex; std::mutex _queueMutex; @@ -64,7 +66,7 @@ private: inline static bool _downloadingObjects; public: - void Begin(const std::vector& entries) + void Begin(const std::vector& entries) { _lastDownloadStatusInfo = {}; _downloadStatusInfo = {}; @@ -80,7 +82,7 @@ public: return _downloadingObjects; } - std::vector GetDownloadedEntries() + std::vector GetDownloadedEntries() { std::lock_guard guard(_downloadedEntriesMutex); return _downloadedEntries; @@ -150,7 +152,7 @@ private: _nextDownloadQueued = true; } - void DownloadObject(const rct_object_entry& entry, const std::string& name, const std::string& url) + void DownloadObject(const ObjectEntryDescriptor& entry, const std::string& name, const std::string& url) { try { @@ -168,7 +170,7 @@ private: auto dataLen = response.body.size(); auto& objRepo = OpenRCT2::GetContext()->GetObjectRepository(); - objRepo.AddObjectFromFile(name, data, dataLen); + objRepo.AddObjectFromFile(ObjectGeneration::DAT, name, data, dataLen); std::lock_guard guard(_downloadedEntriesMutex); _downloadedEntries.push_back(entry); @@ -199,7 +201,7 @@ private: } auto& entry = _entries[_currentDownloadIndex]; - auto name = String::Trim(std::string(entry.name, sizeof(entry.name))); + auto name = String::Trim(std::string(entry.GetName())); log_verbose("Downloading object: [%s]:", name.c_str()); _currentDownloadIndex++; UpdateProgress({ name, _lastDownloadSource, _currentDownloadIndex, _entries.size() }); @@ -283,7 +285,7 @@ static rct_widget window_object_load_error_widgets[] = { { WIDGETS_END }, }; -static rct_string_id get_object_type_string(const rct_object_entry *entry); +static rct_string_id get_object_type_string(ObjectType type); static void window_object_load_error_close(rct_window *w); static void window_object_load_error_update(rct_window *w); static void window_object_load_error_mouseup(rct_window *w, rct_widgetindex widgetIndex); @@ -310,7 +312,7 @@ static rct_window_event_list window_object_load_error_events([](auto& events) }); // clang-format on -static std::vector _invalid_entries; +static std::vector _invalid_entries; static int32_t highlighted_index = -1; static std::string file_path; #ifndef DISABLE_HTTP @@ -324,10 +326,10 @@ static bool _updatedListAfterDownload; * Could possibly be moved out of the window file if other * uses exist and a suitable location is found. */ -static rct_string_id get_object_type_string(const rct_object_entry* entry) +static rct_string_id get_object_type_string(ObjectType type) { rct_string_id result; - switch (entry->GetType()) + switch (type) { case ObjectType::Ride: result = STR_OBJECT_SELECTION_RIDE_VEHICLES_ATTRACTIONS; @@ -371,39 +373,21 @@ static rct_string_id get_object_type_string(const rct_object_entry* entry) */ static void copy_object_names_to_clipboard(rct_window* w) { - // Something has gone wrong, this shouldn't happen. - // We don't want to allocate stupidly large amounts of memory for no reason - assert(w->no_list_items > 0 && w->no_list_items <= OBJECT_ENTRY_COUNT); - - // No system has a newline over 2 characters - size_t line_sep_len = strnlen(PLATFORM_NEWLINE, 2); - size_t buffer_len = (w->no_list_items * (8 + line_sep_len)) + 1; - utf8* buffer = new utf8[buffer_len]{}; - - size_t cur_len = 0; + std::stringstream stream; for (uint16_t i = 0; i < w->no_list_items; i++) { - cur_len += (8 + line_sep_len); - assert(cur_len < buffer_len); - - uint16_t nameLength = 8; - for (; nameLength > 0; nameLength--) - { - if (_invalid_entries[i].name[nameLength - 1] != ' ') - break; - } - - strncat(buffer, _invalid_entries[i].name, nameLength); - strncat(buffer, PLATFORM_NEWLINE, buffer_len - strlen(buffer) - 1); + const auto& entry = _invalid_entries[i]; + stream << entry.GetName(); + stream << PLATFORM_NEWLINE; } - platform_place_string_on_clipboard(buffer); - delete[] buffer; + auto clip = stream.str(); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(clip.c_str()); } -rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const rct_object_entry* missingObjects) +rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects) { - _invalid_entries = std::vector(missingObjects, missingObjects + numMissingObjects); + _invalid_entries = std::vector(missingObjects, missingObjects + numMissingObjects); // Check if window is already open rct_window* window = window_bring_to_front_by_class(WC_OBJECT_LOAD_ERROR); @@ -476,14 +460,8 @@ static void window_object_load_error_mouseup(rct_window* w, rct_widgetindex widg case WIDX_COPY_CURRENT: if (w->selected_list_item > -1 && w->selected_list_item < w->no_list_items) { - utf8 selectedName[32]{}; - strncpy(selectedName, _invalid_entries[w->selected_list_item].name, 8); - - utf8* strp = strchr(selectedName, ' '); - if (strp != nullptr) - *strp = '\0'; - - platform_place_string_on_clipboard(selectedName); + auto name = std::string(_invalid_entries[w->selected_list_item].GetName()); + OpenRCT2::GetContext()->GetUiContext()->SetClipboardText(name.c_str()); } break; case WIDX_COPY_ALL: @@ -580,16 +558,22 @@ static void window_object_load_error_scrollpaint(rct_window* w, rct_drawpixelinf // Draw the actual object entry's name... screenCoords.x = NAME_COL_LEFT - 3; - utf8 nameBuf[32]{}; - strncpy(nameBuf, _invalid_entries[i].name, 8); - gfx_draw_string(dpi, screenCoords, nameBuf, { COLOUR_DARK_GREEN }); + const auto& entry = _invalid_entries[i]; - // ... source game ... - rct_string_id sourceStringId = object_manager_get_source_game_string(_invalid_entries[i].GetSourceGame()); - DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); + auto name = entry.GetName(); + char buffer[256]; + String::Set(buffer, sizeof(buffer), name.data(), name.size()); + gfx_draw_string(dpi, screenCoords, buffer, { COLOUR_DARK_GREEN }); + + if (entry.Generation == ObjectGeneration::DAT) + { + // ... source game ... + rct_string_id sourceStringId = object_manager_get_source_game_string(entry.Entry.GetSourceGame()); + DrawTextBasic(dpi, { SOURCE_COL_LEFT - 3, screenCoords.y }, sourceStringId, {}, { COLOUR_DARK_GREEN }); + } // ... and type - rct_string_id type = get_object_type_string(&_invalid_entries[i]); + rct_string_id type = get_object_type_string(entry.GetType()); DrawTextBasic(dpi, { TYPE_COL_LEFT - 3, screenCoords.y }, type, {}, { COLOUR_DARK_GREEN }); } } @@ -613,7 +597,7 @@ static void window_object_load_error_update_list(rct_window* w) _invalid_entries.erase( std::remove_if( _invalid_entries.begin(), _invalid_entries.end(), - [de](const rct_object_entry& e) { return std::memcmp(de.name, e.name, sizeof(e.name)) == 0; }), + [de](const ObjectEntryDescriptor& e) { return de.GetName() == e.GetName(); }), _invalid_entries.end()); w->no_list_items = static_cast(_invalid_entries.size()); } diff --git a/src/openrct2-ui/windows/TitleEditor.cpp b/src/openrct2-ui/windows/TitleEditor.cpp index fb29c329e9..0c5c782975 100644 --- a/src/openrct2-ui/windows/TitleEditor.cpp +++ b/src/openrct2-ui/windows/TitleEditor.cpp @@ -354,7 +354,7 @@ static void window_title_editor_mouseup(rct_window* w, rct_widgetindex widgetInd auto& objectMgr = OpenRCT2::GetContext()->GetObjectManager(); auto parkImporter = ParkImporter::Create(handle->HintPath); auto result = parkImporter->LoadFromStream(handle->Stream.get(), isScenario); - objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectMgr.LoadObjects(result.RequiredObjects); parkImporter->Import(); if (isScenario) diff --git a/src/openrct2-ui/windows/Window.h b/src/openrct2-ui/windows/Window.h index 0fb34b6ef9..408e5bda6c 100644 --- a/src/openrct2-ui/windows/Window.h +++ b/src/openrct2-ui/windows/Window.h @@ -163,7 +163,7 @@ void window_text_input_open( std::string_view title, std::string_view description, std::string_view initialValue, size_t maxLength, std::function okCallback, std::function cancelCallback); -rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const rct_object_entry* missingObjects); +rct_window* window_object_load_error_open(utf8* path, size_t numMissingObjects, const ObjectEntryDescriptor* missingObjects); rct_window* window_ride_construction_open(); void window_ride_construction_update_active_elements_impl(); diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 805e1a5657..d7bdaf38e5 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -644,7 +644,7 @@ namespace OpenRCT2 // so reload the title screen if that happens. loadTitleScreenFirstOnFail = true; - _objectManager->LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + _objectManager->LoadObjects(result.RequiredObjects); parkImporter->Import(); gScenarioSavePath = path; gCurrentLoadedPath = path; @@ -717,7 +717,7 @@ namespace OpenRCT2 // which the window function doesn't like auto intent = Intent(WC_OBJECT_LOAD_ERROR); intent.putExtra(INTENT_EXTRA_PATH, path); - intent.putExtra(INTENT_EXTRA_LIST, const_cast(e.MissingObjects.data())); + intent.putExtra(INTENT_EXTRA_LIST, const_cast(e.MissingObjects.data())); intent.putExtra(INTENT_EXTRA_LIST_COUNT, static_cast(e.MissingObjects.size())); auto windowManager = _uiContext->GetWindowManager(); @@ -1534,11 +1534,6 @@ utf8* platform_open_directory_browser(const utf8* title) } } -bool platform_place_string_on_clipboard(utf8* target) -{ - return GetContext()->GetUiContext()->SetClipboardText(target); -} - /** * This function is deprecated. * Use IPlatformEnvironment instead. diff --git a/src/openrct2/EditorObjectSelectionSession.cpp b/src/openrct2/EditorObjectSelectionSession.cpp index bafce4fc47..c74a861a48 100644 --- a/src/openrct2/EditorObjectSelectionSession.cpp +++ b/src/openrct2/EditorObjectSelectionSession.cpp @@ -55,8 +55,7 @@ static void setup_track_manager_objects() { uint8_t* selectionFlags = &_objectSelectionFlags[i]; const ObjectRepositoryItem* item = &items[i]; - ObjectType object_type = item->ObjectEntry.GetType(); - if (object_type == ObjectType::Ride) + if (item->Type == ObjectType::Ride) { *selectionFlags |= OBJECT_SELECTION_FLAG_6; @@ -85,8 +84,7 @@ static void setup_track_designer_objects() { uint8_t* selectionFlags = &_objectSelectionFlags[i]; const ObjectRepositoryItem* item = &items[i]; - ObjectType objectType = item->ObjectEntry.GetType(); - if (objectType == ObjectType::Ride) + if (item->Type == ObjectType::Ride) { *selectionFlags |= OBJECT_SELECTION_FLAG_6; @@ -259,7 +257,7 @@ void sub_6AB211() const ObjectRepositoryItem* items = object_repository_get_items(); for (int32_t i = 0; i < numObjects; i++) { - ObjectType objectType = items[i].ObjectEntry.GetType(); + ObjectType objectType = items[i].Type; _numAvailableObjectsForType[EnumValue(objectType)]++; } @@ -302,33 +300,40 @@ void editor_object_flags_free() * * rct2: 0x00685791 */ -static void remove_selected_objects_from_research(const rct_object_entry* installedObject) +static void remove_selected_objects_from_research(ObjectEntryDescriptor& descriptor) { - ObjectType entry_type; - ObjectEntryIndex entry_index; - if (!find_object_in_entry_group(installedObject, &entry_type, &entry_index)) - return; - - if (entry_type == ObjectType::Ride) + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto obj = objManager.GetLoadedObject(descriptor); + if (obj != nullptr) { - auto rideEntry = get_ride_entry(entry_index); - - for (auto rideType : rideEntry->ride_type) + auto entryIndex = objManager.GetLoadedObjectEntryIndex(obj); + switch (obj->GetObjectType()) { - ResearchItem tmp = {}; - tmp.type = Research::EntryType::Ride; - tmp.entryIndex = entry_index; - tmp.baseRideType = rideType; - research_remove(&tmp); + case ObjectType::Ride: + { + auto rideEntry = get_ride_entry(entryIndex); + for (auto rideType : rideEntry->ride_type) + { + ResearchItem tmp = {}; + tmp.type = Research::EntryType::Ride; + tmp.entryIndex = entryIndex; + tmp.baseRideType = rideType; + research_remove(&tmp); + } + break; + } + case ObjectType::SceneryGroup: + { + ResearchItem tmp = {}; + tmp.type = Research::EntryType::Scenery; + tmp.entryIndex = entryIndex; + research_remove(&tmp); + break; + } + default: + break; } } - else if (entry_type == ObjectType::SceneryGroup) - { - ResearchItem tmp = {}; - tmp.type = Research::EntryType::Scenery; - tmp.entryIndex = entry_index; - research_remove(&tmp); - } } /** @@ -337,18 +342,17 @@ static void remove_selected_objects_from_research(const rct_object_entry* instal */ void unload_unselected_objects() { - int32_t numItems = static_cast(object_repository_get_items_count()); - const ObjectRepositoryItem* items = object_repository_get_items(); - std::vector objectsToUnload; + auto numItems = static_cast(object_repository_get_items_count()); + const auto* items = object_repository_get_items(); + std::vector objectsToUnload; for (int32_t i = 0; i < numItems; i++) { if (!(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { - const rct_object_entry* entry = &items[i].ObjectEntry; - - remove_selected_objects_from_research(entry); - objectsToUnload.push_back(*entry); + auto descriptor = ObjectEntryDescriptor(items[i]); + remove_selected_objects_from_research(descriptor); + objectsToUnload.push_back(descriptor); } } object_manager_unload_objects(objectsToUnload); @@ -368,7 +372,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)); } } } @@ -383,7 +387,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)); } } } @@ -394,24 +398,22 @@ static void SelectDesignerObjects() static void ReplaceSelectedWaterPalette(const ObjectRepositoryItem* item) { auto& objectManager = OpenRCT2::GetContext()->GetObjectManager(); - Object* oldPalette = objectManager.GetLoadedObject(ObjectType::Water, 0); + auto* oldPalette = objectManager.GetLoadedObject(ObjectType::Water, 0); if (oldPalette != nullptr) { - const std::vector oldEntries = { *(oldPalette->GetObjectEntry()) }; + const std::vector oldEntries = { oldPalette->GetDescriptor() }; objectManager.UnloadObjects(oldEntries); } - const rct_object_entry& newPaletteEntry = item->ObjectEntry; - - if (objectManager.GetLoadedObject(ObjectEntryDescriptor(newPaletteEntry)) != nullptr - || objectManager.LoadObject(&newPaletteEntry) != nullptr) + auto newPaletteEntry = ObjectEntryDescriptor(*item); + if (objectManager.GetLoadedObject(newPaletteEntry) != nullptr || objectManager.LoadObject(newPaletteEntry) != nullptr) { load_palette(); } else { - log_error("Failed to load selected palette %.8s", newPaletteEntry.name); + log_error("Failed to load selected palette %s", std::string(newPaletteEntry.GetName()).c_str()); } } @@ -430,7 +432,7 @@ void reset_selected_object_count_and_size() const ObjectRepositoryItem* items = object_repository_get_items(); for (int32_t i = 0; i < numObjects; i++) { - ObjectType objectType = items[i].ObjectEntry.GetType(); + ObjectType objectType = items[i].Type; if (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED) { _numSelectedObjectsForType[EnumValue(objectType)]++; @@ -512,7 +514,7 @@ bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_ return false; } - ObjectType objectType = item->ObjectEntry.GetType(); + ObjectType objectType = item->Type; if (objectType == ObjectType::SceneryGroup && (flags & INPUT_FLAG_EDITOR_OBJECT_SELECT_OBJECTS_IN_SCENERY_GROUP)) { for (const auto& sgEntry : item->SceneryGroupInfo.Entries) @@ -539,7 +541,7 @@ bool window_editor_object_selection_select_object(uint8_t isMasterObject, int32_ return true; } - ObjectType objectType = item->ObjectEntry.GetType(); + ObjectType objectType = item->Type; uint16_t maxObjects = object_entry_group_counts[EnumValue(objectType)]; if (maxObjects <= _numSelectedObjectsForType[EnumValue(objectType)]) @@ -587,27 +589,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()); @@ -615,7 +604,7 @@ bool editor_check_object_group_at_least_one_selected(ObjectType checkObjectType) for (size_t i = 0; i < numObjects; i++) { - ObjectType objectType = items[i].ObjectEntry.GetType(); + auto objectType = items[i].Type; if (checkObjectType == objectType && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED)) { return true; @@ -624,6 +613,23 @@ bool editor_check_object_group_at_least_one_selected(ObjectType checkObjectType) return false; } +bool editor_check_object_group_at_least_one_surface_selected(bool queue) +{ + auto numObjects = std::min(object_repository_get_items_count(), _objectSelectionFlags.size()); + const auto* items = object_repository_get_items(); + for (size_t i = 0; i < numObjects; i++) + { + const auto& ori = items[i]; + auto isQueue = (ori.FootpathSurfaceInfo.Flags & FOOTPATH_ENTRY_FLAG_IS_QUEUE) != 0; + if (ori.Type == ObjectType::FootpathSurface && (_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_SELECTED) + && queue == isQueue) + { + return true; + } + } + return false; +} + int32_t editor_remove_unused_objects() { sub_6AB211(); @@ -641,7 +647,7 @@ int32_t editor_remove_unused_objects() && !(_objectSelectionFlags[i] & OBJECT_SELECTION_FLAG_ALWAYS_REQUIRED)) { const ObjectRepositoryItem* item = &items[i]; - ObjectType objectType = item->ObjectEntry.GetType(); + ObjectType objectType = item->Type; if (objectType >= ObjectType::SceneryGroup) { diff --git a/src/openrct2/EditorObjectSelectionSession.h b/src/openrct2/EditorObjectSelectionSession.h index 45f4f8b482..9df783e8b7 100644 --- a/src/openrct2/EditorObjectSelectionSession.h +++ b/src/openrct2/EditorObjectSelectionSession.h @@ -28,14 +28,13 @@ extern std::vector _objectSelectionFlags; extern int32_t _numSelectedObjectsForType[EnumValue(ObjectType::Count)]; bool editor_check_object_group_at_least_one_selected(ObjectType checkObjectType); +bool editor_check_object_group_at_least_one_surface_selected(bool queue); void editor_object_flags_free(); void unload_unselected_objects(); 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/ParkImporter.h b/src/openrct2/ParkImporter.h index ff2272dd8e..eded50014c 100644 --- a/src/openrct2/ParkImporter.h +++ b/src/openrct2/ParkImporter.h @@ -12,6 +12,7 @@ #include "common.h" #include "core/String.hpp" #include "object/Object.h" +#include "object/ObjectList.h" #include #include @@ -19,6 +20,7 @@ struct IObjectManager; struct IObjectRepository; + namespace OpenRCT2 { struct IStream; @@ -29,9 +31,9 @@ struct scenario_index_entry; struct ParkLoadResult final { public: - std::vector RequiredObjects; + ObjectList RequiredObjects; - explicit ParkLoadResult(std::vector&& requiredObjects) + explicit ParkLoadResult(ObjectList&& requiredObjects) : RequiredObjects(std::move(requiredObjects)) { } @@ -68,9 +70,9 @@ namespace ParkImporter class ObjectLoadException : public std::exception { public: - std::vector const MissingObjects; + std::vector const MissingObjects; - explicit ObjectLoadException(std::vector&& missingObjects) + explicit ObjectLoadException(std::vector&& missingObjects) : MissingObjects(std::move(missingObjects)) { } diff --git a/src/openrct2/ReplayManager.cpp b/src/openrct2/ReplayManager.cpp index 94fae3d0ba..ad17d560f7 100644 --- a/src/openrct2/ReplayManager.cpp +++ b/src/openrct2/ReplayManager.cpp @@ -524,7 +524,7 @@ namespace OpenRCT2 auto importer = ParkImporter::CreateS6(context->GetObjectRepository()); auto loadResult = importer->LoadFromStream(&data.parkData, false); - objManager.LoadObjects(loadResult.RequiredObjects.data(), loadResult.RequiredObjects.size()); + objManager.LoadObjects(loadResult.RequiredObjects); importer->Import(); diff --git a/src/openrct2/actions/TrackDesignAction.cpp b/src/openrct2/actions/TrackDesignAction.cpp index e72f07d692..8b34c92053 100644 --- a/src/openrct2/actions/TrackDesignAction.cpp +++ b/src/openrct2/actions/TrackDesignAction.cpp @@ -86,18 +86,16 @@ GameActions::Result::Ptr TrackDesignAction::Query() const return MakeResult(GameActions::Status::InvalidParameters); } - const rct_object_entry* rideEntryObject = &_td.vehicle_object; - - ObjectType entryType; - ObjectEntryIndex entryIndex; - if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex)) + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto entryIndex = objManager.GetLoadedObjectEntryIndex(_td.vehicle_object); + if (entryIndex == OBJECT_ENTRY_INDEX_NULL) { - entryIndex = OBJECT_ENTRY_INDEX_NULL; - } - // Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode. - else if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus) - { - entryIndex = OBJECT_ENTRY_INDEX_NULL; + // Force a fallback if the entry is not invented yet a td6 of it is selected, + // which can happen in select-by-track-type mode + if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus) + { + entryIndex = OBJECT_ENTRY_INDEX_NULL; + } } // Colours do not matter as will be overwritten @@ -149,18 +147,16 @@ GameActions::Result::Ptr TrackDesignAction::Execute() const res->Position.z = _loc.z; res->Expenditure = ExpenditureType::RideConstruction; - const rct_object_entry* rideEntryObject = &_td.vehicle_object; - - ObjectType entryType; - ObjectEntryIndex entryIndex; - if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex)) + auto& objManager = OpenRCT2::GetContext()->GetObjectManager(); + auto entryIndex = objManager.GetLoadedObjectEntryIndex(_td.vehicle_object); + if (entryIndex == OBJECT_ENTRY_INDEX_NULL) { - entryIndex = OBJECT_ENTRY_INDEX_NULL; - } - // Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode. - else if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus) - { - entryIndex = OBJECT_ENTRY_INDEX_NULL; + // Force a fallback if the entry is not invented yet a td6 of it is selected, + // which can happen in select-by-track-type mode + if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus) + { + entryIndex = OBJECT_ENTRY_INDEX_NULL; + } } // Colours do not matter as will be overwritten diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index d2a01bb1ff..819391f03a 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -2735,7 +2735,7 @@ bool NetworkBase::LoadMap(IStream* stream) auto& objManager = context.GetObjectManager(); auto importer = ParkImporter::CreateS6(context.GetObjectRepository()); auto loadResult = importer->LoadFromStream(stream, false); - objManager.LoadObjects(loadResult.RequiredObjects.data(), loadResult.RequiredObjects.size()); + objManager.LoadObjects(loadResult.RequiredObjects); importer->Import(); EntityTweener::Get().Reset(); diff --git a/src/openrct2/object/BannerObject.h b/src/openrct2/object/BannerObject.h index 4ee48e5bb5..7793e35568 100644 --- a/src/openrct2/object/BannerObject.h +++ b/src/openrct2/object/BannerObject.h @@ -19,11 +19,6 @@ private: BannerSceneryEntry _legacyType = {}; public: - explicit BannerObject(const rct_object_entry& entry) - : SceneryObject(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/EntranceObject.h b/src/openrct2/object/EntranceObject.h index 45e4d8dc72..beefbce10e 100644 --- a/src/openrct2/object/EntranceObject.h +++ b/src/openrct2/object/EntranceObject.h @@ -18,11 +18,6 @@ private: rct_entrance_type _legacyType = {}; public: - explicit EntranceObject(const rct_object_entry& entry) - : Object(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/FootpathItemObject.h b/src/openrct2/object/FootpathItemObject.h index 53cdf51ad5..56ed5b91b2 100644 --- a/src/openrct2/object/FootpathItemObject.h +++ b/src/openrct2/object/FootpathItemObject.h @@ -18,11 +18,6 @@ private: PathBitEntry _legacyType = {}; public: - explicit FootpathItemObject(const rct_object_entry& entry) - : SceneryObject(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/FootpathObject.h b/src/openrct2/object/FootpathObject.h index 5d481cec80..ebe409ae58 100644 --- a/src/openrct2/object/FootpathObject.h +++ b/src/openrct2/object/FootpathObject.h @@ -21,11 +21,6 @@ private: PathRailingsDescriptor _pathRailingsDescriptor = {}; public: - explicit FootpathObject(const rct_object_entry& entry) - : Object(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/FootpathRailingsObject.h b/src/openrct2/object/FootpathRailingsObject.h index 205ebebd8f..15bc15edd0 100644 --- a/src/openrct2/object/FootpathRailingsObject.h +++ b/src/openrct2/object/FootpathRailingsObject.h @@ -26,11 +26,6 @@ public: PathRailingsDescriptor _descriptor = {}; public: - explicit FootpathRailingsObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/FootpathSurfaceObject.h b/src/openrct2/object/FootpathSurfaceObject.h index fd23e06a81..b6c3e7cfa2 100644 --- a/src/openrct2/object/FootpathSurfaceObject.h +++ b/src/openrct2/object/FootpathSurfaceObject.h @@ -22,11 +22,6 @@ public: PathSurfaceDescriptor _descriptor = {}; public: - explicit FootpathSurfaceObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/LargeSceneryObject.h b/src/openrct2/object/LargeSceneryObject.h index d1711336db..84c587100d 100644 --- a/src/openrct2/object/LargeSceneryObject.h +++ b/src/openrct2/object/LargeSceneryObject.h @@ -24,11 +24,6 @@ private: std::unique_ptr _3dFont; public: - explicit LargeSceneryObject(const rct_object_entry& entry) - : SceneryObject(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/MusicObject.h b/src/openrct2/object/MusicObject.h index 43b519fadd..850aa27f14 100644 --- a/src/openrct2/object/MusicObject.h +++ b/src/openrct2/object/MusicObject.h @@ -42,11 +42,6 @@ private: public: rct_string_id NameStringId{}; - explicit MusicObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/Object.cpp b/src/openrct2/object/Object.cpp index 80710834e2..e4c8d05f43 100644 --- a/src/openrct2/object/Object.cpp +++ b/src/openrct2/object/Object.cpp @@ -33,9 +33,74 @@ ObjectType& operator++(ObjectType& d, int) return d = (d == ObjectType::Count) ? ObjectType::Ride : static_cast(static_cast(d) + 1); } -Object::Object(const rct_object_entry& entry) +ObjectEntryDescriptor::ObjectEntryDescriptor(const rct_object_entry& newEntry) { - _objectEntry = entry; + if (!newEntry.IsEmpty()) + { + Generation = ObjectGeneration::DAT; + Entry = newEntry; + } +} + +ObjectEntryDescriptor::ObjectEntryDescriptor(std::string_view newIdentifier) +{ + Generation = ObjectGeneration::JSON; + 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()) + { + Generation = ObjectGeneration::JSON; + Identifier = std::string(ori.Identifier); + } + else + { + Generation = ObjectGeneration::DAT; + Entry = ori.ObjectEntry; + } +} + +bool ObjectEntryDescriptor::HasValue() const +{ + return Generation != ObjectGeneration::JSON || !Identifier.empty(); +}; + +ObjectType ObjectEntryDescriptor::GetType() const +{ + return Generation == ObjectGeneration::JSON ? Type : Entry.GetType(); +} + +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); } void* Object::GetLegacyData() @@ -171,6 +236,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); +} + bool ObjectAsset::IsAvailable() const { if (_zipPath.empty()) @@ -227,20 +347,6 @@ std::unique_ptr ObjectAsset::GetStream() const return {}; } -ObjectEntryDescriptor::ObjectEntryDescriptor(const ObjectRepositoryItem& ori) -{ - if (!ori.Identifier.empty()) - { - Generation = ObjectGeneration::JSON; - Identifier = std::string(ori.Identifier); - } - else - { - Generation = ObjectGeneration::DAT; - Entry = ori.ObjectEntry; - } -} - #ifdef __WARN_SUGGEST_FINAL_METHODS__ # pragma GCC diagnostic pop #endif diff --git a/src/openrct2/object/Object.h b/src/openrct2/object/Object.h index a9e9ddf7cf..db6dbb858d 100644 --- a/src/openrct2/object/Object.h +++ b/src/openrct2/object/Object.h @@ -125,6 +125,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); @@ -162,30 +166,27 @@ enum class ObjectGeneration : uint8_t struct ObjectEntryDescriptor { - ObjectGeneration Generation; - std::string Identifier; // For JSON objects - rct_object_entry Entry; // For DAT objects + ObjectGeneration Generation = ObjectGeneration::JSON; - ObjectEntryDescriptor() - : Generation(ObjectGeneration::JSON) - , Identifier() - , Entry() - { - } + // DAT + rct_object_entry Entry{}; - explicit ObjectEntryDescriptor(const rct_object_entry& newEntry) - { - Generation = ObjectGeneration::DAT; - Entry = newEntry; - } - - explicit ObjectEntryDescriptor(std::string_view newIdentifier) - { - Generation = ObjectGeneration::JSON; - Identifier = std::string(newIdentifier); - } + // JSON + ObjectType Type{}; + std::string Identifier; + std::string Version; + 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; @@ -254,12 +255,12 @@ class Object { private: std::string _identifier; - rct_object_entry _objectEntry{}; + ObjectEntryDescriptor _descriptor{}; StringTable _stringTable; ImageTable _imageTable; std::vector _sourceGames; std::vector _authors; - bool _isJsonObject{}; + ObjectGeneration _generation{}; protected: StringTable& GetStringTable() @@ -290,7 +291,6 @@ protected: std::string GetString(int32_t language, ObjectStringID index) const; public: - explicit Object(const rct_object_entry& entry); virtual ~Object() = default; std::string_view GetIdentifier() const @@ -304,22 +304,38 @@ public: void MarkAsJsonObject() { - _isJsonObject = true; + _generation = ObjectGeneration::JSON; } - bool IsJsonObject() const + ObjectGeneration GetGeneration() const { - return _isJsonObject; + return _generation; }; + ObjectType GetObjectType() const + { + return _descriptor.GetType(); + } + + ObjectEntryDescriptor GetDescriptor() const + { + return _descriptor; + } + void SetDescriptor(const ObjectEntryDescriptor& value) + { + _descriptor = value; + } + // Legacy data structures std::string_view GetLegacyIdentifier() const { - return _objectEntry.GetName(); + return _descriptor.GetName(); } - const rct_object_entry* GetObjectEntry() const + + // TODO remove this, we should no longer assume objects have a legacy object entry + const rct_object_entry& GetObjectEntry() const { - return &_objectEntry; + return _descriptor.Entry; } virtual void* GetLegacyData(); @@ -337,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; @@ -374,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/ObjectFactory.cpp b/src/openrct2/object/ObjectFactory.cpp index 854d3ed70b..bb011d0884 100644 --- a/src/openrct2/object/ObjectFactory.cpp +++ b/src/openrct2/object/ObjectFactory.cpp @@ -255,7 +255,8 @@ namespace ObjectFactory if (entry.GetType() != ObjectType::ScenarioText) { - result = CreateObject(entry); + result = CreateObject(entry.GetType()); + result->SetDescriptor(ObjectEntryDescriptor(entry)); utf8 objectName[DAT_NAME_LENGTH + 1] = { 0 }; object_entry_get_name_fixed(objectName, sizeof(objectName), &entry); @@ -287,9 +288,11 @@ namespace ObjectFactory Guard::ArgumentNotNull(entry, GUARD_LINE); Guard::ArgumentNotNull(data, GUARD_LINE); - auto result = CreateObject(*entry); + auto result = CreateObject(entry->GetType()); if (result != nullptr) { + result->SetDescriptor(ObjectEntryDescriptor(*entry)); + utf8 objectName[DAT_NAME_LENGTH + 1]; object_entry_get_name_fixed(objectName, sizeof(objectName), entry); @@ -309,60 +312,60 @@ namespace ObjectFactory return result; } - std::unique_ptr CreateObject(const rct_object_entry& entry) + std::unique_ptr CreateObject(ObjectType type) { std::unique_ptr result; - switch (entry.GetType()) + switch (type) { case ObjectType::Ride: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::SmallScenery: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::LargeScenery: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Walls: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Banners: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Paths: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::PathBits: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::SceneryGroup: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::ParkEntrance: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Water: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::ScenarioText: break; case ObjectType::TerrainSurface: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::TerrainEdge: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Station: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::Music: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::FootpathSurface: - result = std::make_unique(entry); + result = std::make_unique(); break; case ObjectType::FootpathRailings: - result = std::make_unique(entry); + result = std::make_unique(); break; default: throw std::runtime_error("Invalid object type"); @@ -497,22 +500,28 @@ namespace ObjectFactory { auto id = Json::GetString(jRoot["id"]); - rct_object_entry entry = {}; + ObjectEntryDescriptor descriptor; auto originalId = Json::GetString(jRoot["originalId"]); - auto originalName = originalId; if (originalId.length() == 8 + 1 + 8 + 1 + 8) { - entry.flags = std::stoul(originalId.substr(0, 8), nullptr, 16); - originalName = originalId.substr(9, 8); - entry.checksum = std::stoul(originalId.substr(18, 8), nullptr, 16); - } - // Always set, since originalId might be missing or incorrect. - entry.SetType(objectType); - auto minLength = std::min(8, originalName.length()); - std::memcpy(entry.name, originalName.c_str(), minLength); + auto originalName = originalId.substr(9, 8); - result = CreateObject(entry); + rct_object_entry entry = {}; + entry.flags = std::stoul(originalId.substr(0, 8), nullptr, 16); + entry.checksum = std::stoul(originalId.substr(18, 8), nullptr, 16); + entry.SetType(objectType); + auto minLength = std::min(8, originalName.length()); + std::memcpy(entry.name, originalName.c_str(), minLength); + descriptor = ObjectEntryDescriptor(entry); + } + else + { + descriptor = ObjectEntryDescriptor(objectType, id); + } + + result = CreateObject(objectType); result->SetIdentifier(id); + result->SetDescriptor(descriptor); result->MarkAsJsonObject(); auto readContext = ReadObjectContext(objectRepository, id, !gOpenRCT2NoGraphics, fileRetriever); result->ReadJson(&readContext, jRoot); diff --git a/src/openrct2/object/ObjectFactory.h b/src/openrct2/object/ObjectFactory.h index ecbdf9f611..6aef398f28 100644 --- a/src/openrct2/object/ObjectFactory.h +++ b/src/openrct2/object/ObjectFactory.h @@ -17,6 +17,7 @@ struct IObjectRepository; class Object; struct rct_object_entry; +enum class ObjectType : uint8_t; namespace ObjectFactory { @@ -24,7 +25,7 @@ namespace ObjectFactory [[nodiscard]] std::unique_ptr CreateObjectFromLegacyData( IObjectRepository& objectRepository, const rct_object_entry* entry, const void* data, size_t dataSize); [[nodiscard]] std::unique_ptr CreateObjectFromZipFile(IObjectRepository& objectRepository, std::string_view path); - [[nodiscard]] std::unique_ptr CreateObject(const rct_object_entry& entry); + [[nodiscard]] std::unique_ptr CreateObject(ObjectType type); [[nodiscard]] std::unique_ptr CreateObjectFromJsonFile( IObjectRepository& objectRepository, const std::string& path); diff --git a/src/openrct2/object/ObjectList.cpp b/src/openrct2/object/ObjectList.cpp index ab94ef62f4..808696ddcf 100644 --- a/src/openrct2/object/ObjectList.cpp +++ b/src/openrct2/object/ObjectList.cpp @@ -60,17 +60,134 @@ int32_t object_entry_group_encoding[] = { }; // clang-format on -bool object_entry_is_empty(const rct_object_entry* entry) +ObjectList::const_iterator::const_iterator(const ObjectList* parent, bool end) { - uint64_t a, b; - std::memcpy(&a, reinterpret_cast(entry), 8); - std::memcpy(&b, reinterpret_cast(entry) + 8, 8); + _parent = parent; + _subList = _parent->_subLists.size(); + _index = 0; +} - if (a == 0xFFFFFFFFFFFFFFFF && b == 0xFFFFFFFFFFFFFFFF) - return true; - if (a == 0 && b == 0) - return true; - return false; +void ObjectList::const_iterator::MoveToNextEntry() +{ + do + { + if (_subList < _parent->_subLists.size()) + { + auto subListSize = _parent->_subLists[_subList].size(); + if (_index < subListSize) + { + _index++; + if (_index == subListSize) + { + _subList++; + _index = 0; + } + } + } + else + { + break; + } + } while (!_parent->_subLists[_subList][_index].HasValue()); +} + +ObjectList::const_iterator& ObjectList::const_iterator::operator++() +{ + MoveToNextEntry(); + return *this; +} + +ObjectList::const_iterator ObjectList::const_iterator::operator++(int) +{ + return *this; +} + +const ObjectEntryDescriptor& ObjectList::const_iterator::operator*() +{ + return _parent->_subLists[_subList][_index]; +} + +bool ObjectList::const_iterator::operator==(const_iterator& rhs) +{ + return _parent == rhs._parent && _subList == rhs._subList && _index == rhs._index; +} + +bool ObjectList::const_iterator::operator!=(const_iterator& rhs) +{ + return !(*this == rhs); +} + +ObjectList::const_iterator ObjectList::begin() const +{ + return const_iterator(this, false); +} + +ObjectList::const_iterator ObjectList::end() const +{ + return const_iterator(this, true); +} + +std::vector& ObjectList::GetList(ObjectType type) +{ + auto index = static_cast(type); + while (_subLists.size() <= index) + { + _subLists.resize(static_cast(index) + 1); + } + return _subLists[index]; +} + +std::vector& ObjectList::GetList(ObjectType type) const +{ + return const_cast(this)->GetList(type); +} + +const ObjectEntryDescriptor& ObjectList::GetObject(ObjectType type, ObjectEntryIndex index) const +{ + const auto& subList = GetList(type); + if (subList.size() > index) + { + return subList[index]; + } + + static ObjectEntryDescriptor placeholder; + return placeholder; +} + +void ObjectList::Add(const ObjectEntryDescriptor& entry) +{ + auto& subList = GetList(entry.GetType()); + subList.push_back(entry); +} + +void ObjectList::SetObject(ObjectEntryIndex index, const ObjectEntryDescriptor& entry) +{ + auto& subList = GetList(entry.GetType()); + if (subList.size() <= index) + { + subList.resize(static_cast(index) + 1); + } + subList[index] = entry; +} + +void ObjectList::SetObject(ObjectType type, ObjectEntryIndex index, std::string_view identifier) +{ + auto entry = ObjectEntryDescriptor(identifier); + entry.Type = type; + SetObject(index, entry); +} + +ObjectEntryIndex ObjectList::Find(ObjectType type, std::string_view identifier) +{ + auto& subList = GetList(type); + for (size_t i = 0; i < subList.size(); i++) + { + if (subList[i].Identifier == identifier) + { + return static_cast(i); + } + } + return OBJECT_ENTRY_INDEX_NULL; } /** @@ -104,7 +221,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; diff --git a/src/openrct2/object/ObjectList.h b/src/openrct2/object/ObjectList.h index b1048fd82c..ceae5ec119 100644 --- a/src/openrct2/object/ObjectList.h +++ b/src/openrct2/object/ObjectList.h @@ -15,6 +15,45 @@ #include "../world/Footpath.h" #include "../world/Scenery.h" #include "../world/Water.h" +#include "Object.h" #include "ObjectLimits.h" +#include + +class ObjectList +{ +private: + std::vector> _subLists; + +public: + void Add(const ObjectEntryDescriptor& entry); + std::vector& GetList(ObjectType type); + std::vector& GetList(ObjectType type) const; + const ObjectEntryDescriptor& GetObject(ObjectType type, ObjectEntryIndex index) const; + void SetObject(ObjectEntryIndex index, const ObjectEntryDescriptor& entry); + void SetObject(ObjectType type, ObjectEntryIndex index, std::string_view identifier); + ObjectEntryIndex Find(ObjectType type, std::string_view identifier); + + struct const_iterator + { + private: + const ObjectList* _parent; + size_t _subList; + size_t _index; + + void MoveToNextEntry(); + + public: + const_iterator(const ObjectList* parent, bool end); + const ObjectEntryDescriptor& operator*(); + bool operator==(const_iterator& rhs); + bool operator!=(const_iterator& rhs); + const_iterator& operator++(); + const_iterator operator++(int); + }; + + const_iterator begin() const; + const_iterator end() const; +}; + void get_type_entry_index(size_t index, ObjectType* outObjectType, ObjectEntryIndex* outEntryIndex); diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index c823f2f4a9..b23e79a3b7 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -133,10 +133,16 @@ public: return RepositoryItemToObject(ori); } - void LoadObjects(const rct_object_entry* entries, size_t count) override + Object* LoadObject(const ObjectEntryDescriptor& descriptor) override + { + const ObjectRepositoryItem* ori = _objectRepository.FindObject(descriptor); + return RepositoryItemToObject(ori); + } + + void LoadObjects(const ObjectList& objectList) override { // Find all the required objects - auto requiredObjects = GetRequiredObjects(entries, count); + auto requiredObjects = GetRequiredObjects(objectList); // Load the required objects LoadObjects(requiredObjects); @@ -149,19 +155,19 @@ public: ResetTypeToRideEntryIndexMap(); } - void UnloadObjects(const std::vector& entries) override + void UnloadObjects(const std::vector& entries) override { // TODO there are two performance issues here: // - FindObject for every entry which is a dictionary lookup // - GetLoadedObjectIndex for every entry which enumerates _loadedList size_t numObjectsUnloaded = 0; - for (const auto& entry : entries) + for (const auto& descriptor : entries) { - const ObjectRepositoryItem* ori = _objectRepository.FindObject(&entry); + const auto* ori = _objectRepository.FindObject(descriptor); if (ori != nullptr) { - Object* loadedObject = ori->LoadedObject.get(); + auto* loadedObject = ori->LoadedObject.get(); if (loadedObject != nullptr) { UnloadObject(loadedObject); @@ -209,7 +215,7 @@ public: { const ObjectRepositoryItem* item = &_objectRepository.GetObjects()[i]; if (item->LoadedObject != nullptr && IsObjectCustom(item) && item->LoadedObject->GetLegacyData() != nullptr - && !item->LoadedObject->IsJsonObject()) + && item->LoadedObject->GetGeneration() == ObjectGeneration::DAT) { objects.push_back(item); } @@ -352,42 +358,42 @@ private: Object* RepositoryItemToObject(const ObjectRepositoryItem* ori, std::optional slot = {}) { - if (ori == nullptr) - return nullptr; - - Object* loadedObject = ori->LoadedObject.get(); - if (loadedObject != nullptr) - return loadedObject; - - ObjectType objectType = ori->ObjectEntry.GetType(); - if (slot) + Object* loadedObject = nullptr; + if (ori != nullptr) { - if (_loadedObjects.size() > static_cast(*slot) && _loadedObjects[*slot] != nullptr) + loadedObject = ori->LoadedObject.get(); + if (loadedObject == nullptr) { - // Slot already taken - return nullptr; - } - } - else - { - slot = FindSpareSlot(objectType); - } - if (slot.has_value()) - { - auto* object = GetOrLoadObject(ori); - if (object != nullptr) - { - if (_loadedObjects.size() <= static_cast(*slot)) + ObjectType objectType = ori->Type; + if (slot) { - _loadedObjects.resize(slot.value() + 1); + if (_loadedObjects.size() > static_cast(*slot) && _loadedObjects[*slot] != nullptr) + { + // Slot already taken + return nullptr; + } + } + else + { + slot = FindSpareSlot(objectType); + } + if (slot.has_value()) + { + auto object = GetOrLoadObject(ori); + if (object != nullptr) + { + if (_loadedObjects.size() <= static_cast(*slot)) + { + _loadedObjects.resize(slot.value() + 1); + } + loadedObject = object; + _loadedObjects[slot.value()] = std::move(object); + UpdateSceneryGroupIndexes(); + ResetTypeToRideEntryIndexMap(); + } } - loadedObject = object; - _loadedObjects[slot.value()] = object; - UpdateSceneryGroupIndexes(); - ResetTypeToRideEntryIndexMap(); } } - return loadedObject; } @@ -431,7 +437,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); @@ -535,25 +541,29 @@ private: return entryIndex; } - std::vector GetRequiredObjects(const rct_object_entry* entries, size_t count) + std::vector GetRequiredObjects(const ObjectList& objectList) { std::vector requiredObjects; - std::vector missingObjects; + std::vector missingObjects; - for (size_t i = 0; i < count; i++) + for (auto objectType = ObjectType::Ride; objectType < ObjectType::Count; objectType++) { - const rct_object_entry* entry = &entries[i]; - const ObjectRepositoryItem* ori = nullptr; - if (!object_entry_is_empty(entry)) + auto maxObjectsOfType = static_cast(object_entry_group_counts[EnumValue(objectType)]); + for (ObjectEntryIndex i = 0; i < maxObjectsOfType; i++) { - ori = _objectRepository.FindObject(entry); - if (ori == nullptr && entry->GetType() != ObjectType::ScenarioText) + const ObjectRepositoryItem* ori = nullptr; + const auto& entry = objectList.GetObject(objectType, i); + if (entry.HasValue()) { - missingObjects.push_back(*entry); - ReportMissingObject(entry); + ori = _objectRepository.FindObject(entry); + if (ori == nullptr && entry.GetType() != ObjectType::ScenarioText) + { + missingObjects.push_back(entry); + ReportMissingObject(entry); + } } + requiredObjects.push_back(ori); } - requiredObjects.push_back(ori); } if (!missingObjects.empty()) @@ -592,7 +602,7 @@ private: { std::vector objects; std::vector newLoadedObjects; - std::vector badObjects; + std::vector badObjects; objects.resize(OBJECT_ENTRY_COUNT); newLoadedObjects.reserve(OBJECT_ENTRY_COUNT); @@ -612,7 +622,7 @@ private: std::lock_guard guard(commonMutex); if (newObject == nullptr) { - badObjects.push_back(requiredObject->ObjectEntry); + badObjects.push_back(ObjectEntryDescriptor(requiredObject->ObjectEntry)); ReportObjectLoadProblem(&requiredObject->ObjectEntry); } else @@ -714,11 +724,10 @@ private: } } - static void ReportMissingObject(const rct_object_entry* entry) + static void ReportMissingObject(const ObjectEntryDescriptor& entry) { - utf8 objName[DAT_NAME_LENGTH + 1] = { 0 }; - std::copy_n(entry->name, DAT_NAME_LENGTH, objName); - Console::Error::WriteLine("[%s] Object not found.", objName); + std::string name(entry.GetName()); + Console::Error::WriteLine("[%s] Object not found.", name.c_str()); } void ReportObjectLoadProblem(const rct_object_entry* entry) @@ -771,7 +780,7 @@ Object* object_manager_load_object(const rct_object_entry* entry) return loadedObject; } -void object_manager_unload_objects(const std::vector& entries) +void object_manager_unload_objects(const std::vector& entries) { auto& objectManager = OpenRCT2::GetContext()->GetObjectManager(); objectManager.UnloadObjects(entries); diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index 09f2609403..e5083bfabc 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -16,6 +16,7 @@ struct IObjectRepository; class Object; +class ObjectList; struct ObjectRepositoryItem; struct IObjectManager @@ -33,9 +34,10 @@ struct IObjectManager virtual Object* LoadObject(std::string_view identifier) abstract; virtual Object* LoadObject(const rct_object_entry* entry) abstract; - virtual void LoadObjects(const rct_object_entry* entries, size_t count) abstract; + virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) abstract; + virtual void LoadObjects(const ObjectList& entries) abstract; virtual void LoadDefaultObjects() abstract; - virtual void UnloadObjects(const std::vector& entries) abstract; + virtual void UnloadObjects(const std::vector& entries) abstract; virtual void UnloadAll() abstract; virtual void ResetObjects() abstract; @@ -50,6 +52,6 @@ struct IObjectManager [[nodiscard]] ObjectEntryIndex object_manager_get_loaded_object_entry_index(const Object* loadedObject); [[nodiscard]] ObjectEntryIndex object_manager_get_loaded_object_entry_index(const ObjectEntryDescriptor& entry); Object* object_manager_load_object(const rct_object_entry* entry); -void object_manager_unload_objects(const std::vector& entries); +void object_manager_unload_objects(const std::vector& entries); void object_manager_unload_all_objects(); [[nodiscard]] rct_string_id object_manager_get_source_game_string(const ObjectSourceGame sourceGame); diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index 4c8329323e..3979bbf001 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -77,7 +77,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; @@ -114,8 +114,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(); @@ -129,6 +131,8 @@ public: protected: void Serialise(DataSerialiser& ds, ObjectRepositoryItem& item) const override { + ds << item.Type; + ds << item.Generation; ds << item.Identifier; ds << item.ObjectEntry; ds << item.Path; @@ -137,7 +141,7 @@ protected: ds << item.Sources; ds << item.Authors; - switch (item.ObjectEntry.GetType()) + switch (item.Type) { case ObjectType::Ride: ds << item.RideInfo.RideFlags; @@ -149,6 +153,9 @@ protected: ds << item.SceneryGroupInfo.Entries; break; } + case ObjectType::FootpathSurface: + ds << item.FootpathSurfaceInfo.Flags; + break; default: // Switch processes only ObjectType::Ride and ObjectType::SceneryGroup break; @@ -298,7 +305,7 @@ public: else { log_verbose("Adding object: [%s]", objectName); - auto path = GetPathForNewObject(objectName); + auto path = GetPathForNewObject(ObjectGeneration::DAT, objectName); try { SaveObject(path, objectEntry, data, dataSize); @@ -311,10 +318,10 @@ public: } } - void AddObjectFromFile(std::string_view objectName, const void* data, size_t dataSize) override + void AddObjectFromFile(ObjectGeneration generation, std::string_view objectName, const void* data, size_t dataSize) override { log_verbose("Adding object: [%s]", std::string(objectName).c_str()); - auto path = GetPathForNewObject(objectName); + auto path = GetPathForNewObject(generation, objectName); try { File::WriteAllBytes(path, data, dataSize); @@ -415,7 +422,15 @@ private: bool AddItem(const ObjectRepositoryItem& item) { - auto conflict = FindObject(&item.ObjectEntry); + const ObjectRepositoryItem* conflict{}; + if (item.ObjectEntry.name[0] != '\0') + { + conflict = FindObject(&item.ObjectEntry); + } + if (conflict == nullptr) + { + conflict = FindObject(item.Identifier); + } if (conflict == nullptr) { size_t index = _items.size(); @@ -426,7 +441,10 @@ private: { _newItemMap[item.Identifier] = index; } - _itemMap[item.ObjectEntry] = index; + if (!item.ObjectEntry.IsEmpty()) + { + _itemMap[item.ObjectEntry] = index; + } return true; } else @@ -555,45 +573,53 @@ private: return salt; } - std::string GetPathForNewObject(std::string_view name) + std::string GetPathForNewObject(ObjectGeneration generation, std::string_view name) { // Get object directory and create it if it doesn't exist auto userObjPath = _env->GetDirectoryPath(DIRBASE::USER, DIRID::OBJECT); Path::CreateDirectory(userObjPath); // Find a unique file name - auto fileName = GetFileNameForNewObject(name); - auto fullPath = Path::Combine(userObjPath, fileName + ".DAT"); + auto fileName = GetFileNameForNewObject(generation, name); + auto extension = (generation == ObjectGeneration::DAT ? ".DAT" : ".parkobj"); + auto fullPath = Path::Combine(userObjPath, fileName + extension); auto counter = 1U; while (File::Exists(fullPath)) { counter++; - fullPath = Path::Combine(userObjPath, String::StdFormat("%s-%02X.DAT", fileName.c_str(), counter)); + fullPath = Path::Combine(userObjPath, String::StdFormat("%s-%02X%s", fileName.c_str(), counter, extension)); } return fullPath; } - std::string GetFileNameForNewObject(std::string_view name) + std::string GetFileNameForNewObject(ObjectGeneration generation, std::string_view name) { - // Trim name - char normalisedName[9] = { 0 }; - auto maxLength = std::min(name.size(), 8); - for (size_t i = 0; i < maxLength; i++) + if (generation == ObjectGeneration::DAT) { - if (name[i] != ' ') + // Trim name + char normalisedName[9] = { 0 }; + auto maxLength = std::min(name.size(), 8); + for (size_t i = 0; i < maxLength; i++) { - normalisedName[i] = toupper(name[i]); + if (name[i] != ' ') + { + normalisedName[i] = toupper(name[i]); + } + else + { + normalisedName[i] = '\0'; + break; + } } - else - { - normalisedName[i] = '\0'; - break; - } - } - // Convert to UTF-8 filename - return String::Convert(normalisedName, CODE_PAGE::CP_1252, CODE_PAGE::CP_UTF8); + // Convert to UTF-8 filename + return String::Convert(normalisedName, CODE_PAGE::CP_1252, CODE_PAGE::CP_UTF8); + } + else + { + return std::string(name); + } } void WritePackedObject(OpenRCT2::IStream* stream, const rct_object_entry* entry) @@ -607,7 +633,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."); } @@ -632,14 +658,16 @@ bool IsObjectCustom(const ObjectRepositoryItem* object) // Do not count our new object types as custom yet, otherwise the game // will try to pack them into saved games. - auto type = object->ObjectEntry.GetType(); - if (type > ObjectType::ScenarioText) + if (object->Type > ObjectType::ScenarioText) { return false; } switch (object->GetFirstSourceGame()) { + case ObjectSourceGame::RCT1: + case ObjectSourceGame::AddedAttractions: + case ObjectSourceGame::LoopyLandscapes: case ObjectSourceGame::RCT2: case ObjectSourceGame::WackyWorlds: case ObjectSourceGame::TimeTwister: @@ -718,40 +746,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 e6de1cce14..d77c8ec2b1 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; @@ -86,7 +88,8 @@ struct IObjectRepository virtual void UnregisterLoadedObject(const ObjectRepositoryItem* ori, Object* object) abstract; virtual void AddObject(const rct_object_entry* objectEntry, const void* data, size_t dataSize) abstract; - virtual void AddObjectFromFile(std::string_view objectName, const void* data, size_t dataSize) abstract; + virtual void AddObjectFromFile( + ObjectGeneration generation, std::string_view objectName, const void* data, size_t dataSize) abstract; virtual void ExportPackedObject(OpenRCT2::IStream* stream) abstract; virtual void WritePackedObjects(OpenRCT2::IStream* stream, std::vector& objects) abstract; diff --git a/src/openrct2/object/RideObject.h b/src/openrct2/object/RideObject.h index c7c9202781..4c14deb6b5 100644 --- a/src/openrct2/object/RideObject.h +++ b/src/openrct2/object/RideObject.h @@ -24,11 +24,6 @@ private: std::vector> _peepLoadingWaypoints[MAX_VEHICLES_PER_RIDE_ENTRY]; public: - explicit RideObject(const rct_object_entry& entry) - : Object(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/SceneryGroupObject.h b/src/openrct2/object/SceneryGroupObject.h index 09ed0e6256..fed7094cb5 100644 --- a/src/openrct2/object/SceneryGroupObject.h +++ b/src/openrct2/object/SceneryGroupObject.h @@ -25,11 +25,6 @@ private: std::vector _items; public: - explicit SceneryGroupObject(const rct_object_entry& entry) - : Object(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/SceneryObject.h b/src/openrct2/object/SceneryObject.h index 9c15a1c397..e4e351a59c 100644 --- a/src/openrct2/object/SceneryObject.h +++ b/src/openrct2/object/SceneryObject.h @@ -19,10 +19,6 @@ private: ObjectEntryDescriptor _primarySceneryGroupEntry = {}; public: - explicit SceneryObject(const rct_object_entry& entry) - : Object(entry) - { - } virtual ~SceneryObject() = default; const ObjectEntryDescriptor& GetPrimarySceneryGroup() const diff --git a/src/openrct2/object/SmallSceneryObject.h b/src/openrct2/object/SmallSceneryObject.h index 48c8bdb2d7..c5684a965c 100644 --- a/src/openrct2/object/SmallSceneryObject.h +++ b/src/openrct2/object/SmallSceneryObject.h @@ -21,11 +21,6 @@ private: std::vector _frameOffsets; public: - explicit SmallSceneryObject(const rct_object_entry& entry) - : SceneryObject(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/StationObject.h b/src/openrct2/object/StationObject.h index 8f45a92405..6965a81592 100644 --- a/src/openrct2/object/StationObject.h +++ b/src/openrct2/object/StationObject.h @@ -29,11 +29,6 @@ public: int32_t Height{}; uint8_t ScrollingMode{}; - explicit StationObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/TerrainEdgeObject.h b/src/openrct2/object/TerrainEdgeObject.h index d55fb84d42..269acd67d3 100644 --- a/src/openrct2/object/TerrainEdgeObject.h +++ b/src/openrct2/object/TerrainEdgeObject.h @@ -21,11 +21,6 @@ public: uint32_t NumImagesLoaded{}; bool HasDoors{}; - explicit TerrainEdgeObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/TerrainSurfaceObject.h b/src/openrct2/object/TerrainSurfaceObject.h index c273ad42d6..fa50f885e9 100644 --- a/src/openrct2/object/TerrainSurfaceObject.h +++ b/src/openrct2/object/TerrainSurfaceObject.h @@ -56,11 +56,6 @@ public: uint32_t NumImagesLoaded{}; - explicit TerrainSurfaceObject(const rct_object_entry& entry) - : Object(entry) - { - } - void ReadJson(IReadObjectContext* context, json_t& root) override; void Load() override; void Unload() override; diff --git a/src/openrct2/object/WallObject.h b/src/openrct2/object/WallObject.h index a0948326ea..1189963071 100644 --- a/src/openrct2/object/WallObject.h +++ b/src/openrct2/object/WallObject.h @@ -18,11 +18,6 @@ private: WallSceneryEntry _legacyType = {}; public: - explicit WallObject(const rct_object_entry& entry) - : SceneryObject(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/object/WaterObject.h b/src/openrct2/object/WaterObject.h index bac5e274b5..34275a7ea4 100644 --- a/src/openrct2/object/WaterObject.h +++ b/src/openrct2/object/WaterObject.h @@ -20,11 +20,6 @@ private: rct_water_type _legacyType = {}; public: - explicit WaterObject(const rct_object_entry& entry) - : Object(entry) - { - } - void* GetLegacyData() override { return &_legacyType; diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index d3d898178b..af85318121 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -96,7 +96,6 @@ bool platform_ensure_directory_exists(const utf8* path); bool platform_directory_delete(const utf8* path); std::string platform_get_absolute_path(const utf8* relative_path, const utf8* base_path); bool platform_lock_single_instance(); -bool platform_place_string_on_clipboard(utf8* target); // Returns the bitmask of the GetLogicalDrives function for windows, 0 for other systems int32_t platform_get_drives(); diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index af331393da..9b92a32d41 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1464,7 +1464,7 @@ namespace RCT1 } } - std::vector GetRequiredObjects() + ObjectList GetRequiredObjects() { std::vector result; AppendRequiredObjects(result, ObjectType::Ride, _rideEntries); @@ -1489,7 +1489,14 @@ namespace RCT1 })); AppendRequiredObjects(result, ObjectType::ParkEntrance, std::vector({ "PKENT1 " })); AppendRequiredObjects(result, ObjectType::Water, _waterEntry); - return result; + + ObjectList objectList; + for (rct_object_entry entry : result) + { + objectList.Add(ObjectEntryDescriptor(entry)); + } + + return objectList; } void ImportTileElements() @@ -3056,7 +3063,7 @@ void load_from_sv4(const utf8* path) auto& objectMgr = GetContext()->GetObjectManager(); auto s4Importer = std::make_unique(); auto result = s4Importer->LoadSavedGame(path); - objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectMgr.LoadObjects(result.RequiredObjects); s4Importer->Import(); } @@ -3065,6 +3072,6 @@ void load_from_sc4(const utf8* path) auto& objectMgr = GetContext()->GetObjectManager(); auto s4Importer = std::make_unique(); auto result = s4Importer->LoadScenario(path); - objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectMgr.LoadObjects(result.RequiredObjects); s4Importer->Import(); } diff --git a/src/openrct2/rct1/T4Importer.cpp b/src/openrct2/rct1/T4Importer.cpp index 536eeabc4d..7905057840 100644 --- a/src/openrct2/rct1/T4Importer.cpp +++ b/src/openrct2/rct1/T4Importer.cpp @@ -161,7 +161,7 @@ namespace RCT1 assert(vehObjName != nullptr); std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), static_cast(8))); } - std::memcpy(&td->vehicle_object, &vehicleObject, sizeof(rct_object_entry)); + td->vehicle_object = ObjectEntryDescriptor(vehicleObject); td->vehicle_type = td4Base.vehicle_type; td->flags = td4Base.flags; diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 7a51974b18..aaad917ed9 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -266,14 +266,14 @@ static void ExportObjectList(IObjectManager& objMgr, T& objects) auto& dst = objects[i]; const auto* object = objMgr.GetLoadedObject(TObjectType, i); - if (object == nullptr || object->GetObjectEntry() == nullptr) + if (object == nullptr || object->GetGeneration() != ObjectGeneration::DAT) { // The sv6 format expects null/invalid entries to be filled with 0xFF. std::memset(&dst, 0xFF, sizeof(dst)); } else { - dst = *object->GetObjectEntry(); + dst = object->GetObjectEntry(); } } } diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index b92b7ed13c..4c1383cad6 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -32,6 +32,7 @@ #include "../management/Research.h" #include "../network/network.h" #include "../object/ObjectLimits.h" +#include "../object/ObjectList.h" #include "../object/ObjectManager.h" #include "../object/ObjectRepository.h" #include "../peep/Peep.h" @@ -1585,7 +1586,7 @@ public: } } - std::vector GetRequiredObjects() + ObjectList GetRequiredObjects() { std::vector result; @@ -1601,7 +1602,13 @@ public: AddRequiredObjects(result, _s6.WaterObjects); AddRequiredObjects(result, _s6.ScenarioTextObjects); - return result; + ObjectList objectList; + for (rct_object_entry entry : result) + { + objectList.Add(ObjectEntryDescriptor(entry)); + } + + return objectList; } }; @@ -1987,7 +1994,7 @@ void load_from_sv6(const char* path) { auto& objectMgr = context->GetObjectManager(); auto result = s6Importer->LoadSavedGame(path); - objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objectMgr.LoadObjects(result.RequiredObjects); s6Importer->Import(); game_fix_save_vars(); AutoCreateMapAnimations(); @@ -2027,7 +2034,7 @@ void load_from_sc6(const char* path) try { auto result = s6Importer->LoadScenario(path); - objManager.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); + objManager.LoadObjects(result.RequiredObjects); s6Importer->Import(); game_fix_save_vars(); AutoCreateMapAnimations(); diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp index d45d97aa73..5893b357f4 100644 --- a/src/openrct2/rct2/T6Importer.cpp +++ b/src/openrct2/rct2/T6Importer.cpp @@ -128,7 +128,7 @@ public: td->track_support_colour[i] = td6.track_support_colour[i]; } td->flags2 = td6.flags2; - td->vehicle_object = td6.vehicle_object; + td->vehicle_object = ObjectEntryDescriptor(td6.vehicle_object); td->space_required_x = td6.space_required_x; td->space_required_y = td6.space_required_y; td->lift_hill_speed = td6.lift_hill_speed_num_circuits & 0b00011111; @@ -224,7 +224,7 @@ public: if (RCT2RideTypeNeedsConversion(td->type)) { std::scoped_lock lock(_objectLookupMutex); - auto rawObject = object_repository_load_object(&td->vehicle_object); + auto rawObject = object_repository_load_object(&td->vehicle_object.Entry); if (rawObject != nullptr) { const auto* rideEntry = static_cast( diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index 3ea6e6d34d..7057a234d3 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -92,11 +92,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. - std::memcpy(&vehicle_object, object->GetObjectEntry(), sizeof(rct_object_entry)); + 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; @@ -595,7 +602,7 @@ void TrackDesign::Serialise(DataSerialiser& stream) stream << DS_TAG(track_rail_colour); stream << DS_TAG(track_support_colour); stream << DS_TAG(flags2); - stream << DS_TAG(vehicle_object); + stream << DS_TAG(vehicle_object.Entry); stream << DS_TAG(space_required_x); stream << DS_TAG(space_required_y); stream << DS_TAG(vehicle_additional_colour); @@ -632,11 +639,14 @@ 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; - 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) @@ -1886,12 +1896,8 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o *outRide = nullptr; *flags = 0; - ObjectType entry_type; - ObjectEntryIndex entry_index; - if (!find_object_in_entry_group(&td6->vehicle_object, &entry_type, &entry_index)) - { - entry_index = OBJECT_ENTRY_INDEX_NULL; - } + auto& objManager = GetContext()->GetObjectManager(); + auto entry_index = objManager.GetLoadedObjectEntryIndex(td6->vehicle_object); ride_id_t rideIndex; uint8_t rideCreateFlags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND; @@ -1956,7 +1962,7 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o if (resultCost != MONEY32_UNDEFINED) { - if (!find_object_in_entry_group(&td6->vehicle_object, &entry_type, &entry_index)) + if (entry_index == OBJECT_ENTRY_INDEX_NULL) { *flags |= TRACK_DESIGN_FLAG_VEHICLE_UNAVAILABLE; } diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index 4cf7615825..4bbfd49ed5 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -119,7 +119,7 @@ struct TrackDesign uint8_t track_rail_colour[RCT12_NUM_COLOUR_SCHEMES]; uint8_t track_support_colour[RCT12_NUM_COLOUR_SCHEMES]; uint32_t flags2; - rct_object_entry vehicle_object; + ObjectEntryDescriptor vehicle_object; uint8_t space_required_x; uint8_t space_required_y; uint8_t vehicle_additional_colour[RCT2_MAX_CARS_PER_TRAIN]; diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 9af88aa3b8..cde8188203 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -82,7 +82,7 @@ public: item.Name = GetNameFromTrackPath(path); item.Path = path; item.RideType = td6->type; - item.ObjectEntry = std::string(td6->vehicle_object.name, 8); + item.ObjectEntry = std::string(td6->vehicle_object.Entry.name, 8); item.Flags = 0; if (IsTrackReadOnly(path)) { diff --git a/src/openrct2/ride/TrackDesignSave.cpp b/src/openrct2/ride/TrackDesignSave.cpp index eba7b9031a..3bd391b3be 100644 --- a/src/openrct2/ride/TrackDesignSave.cpp +++ b/src/openrct2/ride/TrackDesignSave.cpp @@ -193,11 +193,11 @@ static void track_design_save_push_tile_element(const CoordsXY& loc, TileElement * 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 rct_object_entry& entry, const CoordsXYZ& loc, uint8_t flags, uint8_t primaryColour, uint8_t secondaryColour) { auto tileLoc = TileCoordsXYZ(loc); TrackDesignSceneryElement item{}; - item.scenery_object = *entry; + item.scenery_object = entry; item.x = tileLoc.x; item.y = tileLoc.y; item.z = tileLoc.z; @@ -370,7 +370,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 rct_object_entry& entry, const CoordsXYZ& loc, uint8_t flags) { size_t removeIndex = SIZE_MAX; auto tileLoc = TileCoordsXYZ(loc); @@ -385,7 +385,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;