mirror of https://github.com/OpenRCT2/OpenRCT2.git
Modify object loading for JSON-only objects
This commit is contained in:
parent
37821ce25e
commit
bdab3219cb
|
@ -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<rct_object_entry*>(intent->GetPointerExtra(INTENT_EXTRA_LIST));
|
||||
auto objects = static_cast<const ObjectEntryDescriptor*>(intent->GetPointerExtra(INTENT_EXTRA_LIST));
|
||||
size_t count = intent->GetUIntExtra(INTENT_EXTRA_LIST_COUNT);
|
||||
window_object_load_error_open(const_cast<utf8*>(path.c_str()), count, objects);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
#include <openrct2/object/ObjectManager.h>
|
||||
#include <openrct2/object/ObjectRepository.h>
|
||||
#include <openrct2/platform/platform.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
#include <openrct2/windows/Intent.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -48,8 +50,8 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
std::vector<rct_object_entry> _entries;
|
||||
std::vector<rct_object_entry> _downloadedEntries;
|
||||
std::vector<ObjectEntryDescriptor> _entries;
|
||||
std::vector<ObjectEntryDescriptor> _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<rct_object_entry>& entries)
|
||||
void Begin(const std::vector<ObjectEntryDescriptor>& entries)
|
||||
{
|
||||
_lastDownloadStatusInfo = {};
|
||||
_downloadStatusInfo = {};
|
||||
|
@ -80,7 +82,7 @@ public:
|
|||
return _downloadingObjects;
|
||||
}
|
||||
|
||||
std::vector<rct_object_entry> GetDownloadedEntries()
|
||||
std::vector<ObjectEntryDescriptor> GetDownloadedEntries()
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::mutex> 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<rct_object_entry> _invalid_entries;
|
||||
static std::vector<ObjectEntryDescriptor> _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<rct_object_entry>(missingObjects, missingObjects + numMissingObjects);
|
||||
_invalid_entries = std::vector<ObjectEntryDescriptor>(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<uint16_t>(_invalid_entries.size());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<void(std::string_view)> okCallback, std::function<void()> 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();
|
||||
|
|
|
@ -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<rct_object_entry*>(e.MissingObjects.data()));
|
||||
intent.putExtra(INTENT_EXTRA_LIST, const_cast<ObjectEntryDescriptor*>(e.MissingObjects.data()));
|
||||
intent.putExtra(INTENT_EXTRA_LIST_COUNT, static_cast<uint32_t>(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.
|
||||
|
|
|
@ -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<int32_t>(object_repository_get_items_count());
|
||||
const ObjectRepositoryItem* items = object_repository_get_items();
|
||||
std::vector<rct_object_entry> objectsToUnload;
|
||||
auto numItems = static_cast<int32_t>(object_repository_get_items_count());
|
||||
const auto* items = object_repository_get_items();
|
||||
std::vector<ObjectEntryDescriptor> 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<rct_object_entry> oldEntries = { *(oldPalette->GetObjectEntry()) };
|
||||
const std::vector<ObjectEntryDescriptor> 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)
|
||||
{
|
||||
|
|
|
@ -28,14 +28,13 @@ extern std::vector<uint8_t> _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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "common.h"
|
||||
#include "core/String.hpp"
|
||||
#include "object/Object.h"
|
||||
#include "object/ObjectList.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -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<rct_object_entry> RequiredObjects;
|
||||
ObjectList RequiredObjects;
|
||||
|
||||
explicit ParkLoadResult(std::vector<rct_object_entry>&& requiredObjects)
|
||||
explicit ParkLoadResult(ObjectList&& requiredObjects)
|
||||
: RequiredObjects(std::move(requiredObjects))
|
||||
{
|
||||
}
|
||||
|
@ -68,9 +70,9 @@ namespace ParkImporter
|
|||
class ObjectLoadException : public std::exception
|
||||
{
|
||||
public:
|
||||
std::vector<rct_object_entry> const MissingObjects;
|
||||
std::vector<ObjectEntryDescriptor> const MissingObjects;
|
||||
|
||||
explicit ObjectLoadException(std::vector<rct_object_entry>&& missingObjects)
|
||||
explicit ObjectLoadException(std::vector<ObjectEntryDescriptor>&& missingObjects)
|
||||
: MissingObjects(std::move(missingObjects))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -19,11 +19,6 @@ private:
|
|||
BannerSceneryEntry _legacyType = {};
|
||||
|
||||
public:
|
||||
explicit BannerObject(const rct_object_entry& entry)
|
||||
: SceneryObject(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -18,11 +18,6 @@ private:
|
|||
rct_entrance_type _legacyType = {};
|
||||
|
||||
public:
|
||||
explicit EntranceObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -18,11 +18,6 @@ private:
|
|||
PathBitEntry _legacyType = {};
|
||||
|
||||
public:
|
||||
explicit FootpathItemObject(const rct_object_entry& entry)
|
||||
: SceneryObject(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -21,11 +21,6 @@ private:
|
|||
PathRailingsDescriptor _pathRailingsDescriptor = {};
|
||||
|
||||
public:
|
||||
explicit FootpathObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -24,11 +24,6 @@ private:
|
|||
std::unique_ptr<LargeSceneryText> _3dFont;
|
||||
|
||||
public:
|
||||
explicit LargeSceneryObject(const rct_object_entry& entry)
|
||||
: SceneryObject(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -33,9 +33,74 @@ ObjectType& operator++(ObjectType& d, int)
|
|||
return d = (d == ObjectType::Count) ? ObjectType::Ride : static_cast<ObjectType>(static_cast<uint8_t>(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<uint8_t> rct_object_entry::GetSceneryType() const
|
|||
}
|
||||
}
|
||||
|
||||
bool rct_object_entry::IsEmpty() const
|
||||
{
|
||||
uint64_t a, b;
|
||||
std::memcpy(&a, reinterpret_cast<const uint8_t*>(this), 8);
|
||||
std::memcpy(&b, reinterpret_cast<const uint8_t*>(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<IStream> 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
|
||||
|
|
|
@ -125,6 +125,10 @@ struct rct_object_entry
|
|||
{
|
||||
return static_cast<ObjectSourceGame>((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<ObjectSourceGame> _sourceGames;
|
||||
std::vector<std::string> _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);
|
||||
|
|
|
@ -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<Object> CreateObject(const rct_object_entry& entry)
|
||||
std::unique_ptr<Object> CreateObject(ObjectType type)
|
||||
{
|
||||
std::unique_ptr<Object> result;
|
||||
switch (entry.GetType())
|
||||
switch (type)
|
||||
{
|
||||
case ObjectType::Ride:
|
||||
result = std::make_unique<RideObject>(entry);
|
||||
result = std::make_unique<RideObject>();
|
||||
break;
|
||||
case ObjectType::SmallScenery:
|
||||
result = std::make_unique<SmallSceneryObject>(entry);
|
||||
result = std::make_unique<SmallSceneryObject>();
|
||||
break;
|
||||
case ObjectType::LargeScenery:
|
||||
result = std::make_unique<LargeSceneryObject>(entry);
|
||||
result = std::make_unique<LargeSceneryObject>();
|
||||
break;
|
||||
case ObjectType::Walls:
|
||||
result = std::make_unique<WallObject>(entry);
|
||||
result = std::make_unique<WallObject>();
|
||||
break;
|
||||
case ObjectType::Banners:
|
||||
result = std::make_unique<BannerObject>(entry);
|
||||
result = std::make_unique<BannerObject>();
|
||||
break;
|
||||
case ObjectType::Paths:
|
||||
result = std::make_unique<FootpathObject>(entry);
|
||||
result = std::make_unique<FootpathObject>();
|
||||
break;
|
||||
case ObjectType::PathBits:
|
||||
result = std::make_unique<FootpathItemObject>(entry);
|
||||
result = std::make_unique<FootpathItemObject>();
|
||||
break;
|
||||
case ObjectType::SceneryGroup:
|
||||
result = std::make_unique<SceneryGroupObject>(entry);
|
||||
result = std::make_unique<SceneryGroupObject>();
|
||||
break;
|
||||
case ObjectType::ParkEntrance:
|
||||
result = std::make_unique<EntranceObject>(entry);
|
||||
result = std::make_unique<EntranceObject>();
|
||||
break;
|
||||
case ObjectType::Water:
|
||||
result = std::make_unique<WaterObject>(entry);
|
||||
result = std::make_unique<WaterObject>();
|
||||
break;
|
||||
case ObjectType::ScenarioText:
|
||||
break;
|
||||
case ObjectType::TerrainSurface:
|
||||
result = std::make_unique<TerrainSurfaceObject>(entry);
|
||||
result = std::make_unique<TerrainSurfaceObject>();
|
||||
break;
|
||||
case ObjectType::TerrainEdge:
|
||||
result = std::make_unique<TerrainEdgeObject>(entry);
|
||||
result = std::make_unique<TerrainEdgeObject>();
|
||||
break;
|
||||
case ObjectType::Station:
|
||||
result = std::make_unique<StationObject>(entry);
|
||||
result = std::make_unique<StationObject>();
|
||||
break;
|
||||
case ObjectType::Music:
|
||||
result = std::make_unique<MusicObject>(entry);
|
||||
result = std::make_unique<MusicObject>();
|
||||
break;
|
||||
case ObjectType::FootpathSurface:
|
||||
result = std::make_unique<FootpathSurfaceObject>(entry);
|
||||
result = std::make_unique<FootpathSurfaceObject>();
|
||||
break;
|
||||
case ObjectType::FootpathRailings:
|
||||
result = std::make_unique<FootpathRailingsObject>(entry);
|
||||
result = std::make_unique<FootpathRailingsObject>();
|
||||
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<size_t>(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<size_t>(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);
|
||||
|
|
|
@ -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<Object> CreateObjectFromLegacyData(
|
||||
IObjectRepository& objectRepository, const rct_object_entry* entry, const void* data, size_t dataSize);
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObjectFromZipFile(IObjectRepository& objectRepository, std::string_view path);
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObject(const rct_object_entry& entry);
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObject(ObjectType type);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Object> CreateObjectFromJsonFile(
|
||||
IObjectRepository& objectRepository, const std::string& path);
|
||||
|
|
|
@ -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<const uint8_t*>(entry), 8);
|
||||
std::memcpy(&b, reinterpret_cast<const uint8_t*>(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<ObjectEntryDescriptor>& ObjectList::GetList(ObjectType type)
|
||||
{
|
||||
auto index = static_cast<size_t>(type);
|
||||
while (_subLists.size() <= index)
|
||||
{
|
||||
_subLists.resize(static_cast<size_t>(index) + 1);
|
||||
}
|
||||
return _subLists[index];
|
||||
}
|
||||
|
||||
std::vector<ObjectEntryDescriptor>& ObjectList::GetList(ObjectType type) const
|
||||
{
|
||||
return const_cast<ObjectList*>(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<size_t>(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<ObjectEntryIndex>(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;
|
||||
|
|
|
@ -15,6 +15,45 @@
|
|||
#include "../world/Footpath.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "../world/Water.h"
|
||||
#include "Object.h"
|
||||
#include "ObjectLimits.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class ObjectList
|
||||
{
|
||||
private:
|
||||
std::vector<std::vector<ObjectEntryDescriptor>> _subLists;
|
||||
|
||||
public:
|
||||
void Add(const ObjectEntryDescriptor& entry);
|
||||
std::vector<ObjectEntryDescriptor>& GetList(ObjectType type);
|
||||
std::vector<ObjectEntryDescriptor>& 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);
|
||||
|
|
|
@ -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<rct_object_entry>& entries) override
|
||||
void UnloadObjects(const std::vector<ObjectEntryDescriptor>& 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<int32_t> 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<size_t>(*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<size_t>(*slot))
|
||||
ObjectType objectType = ori->Type;
|
||||
if (slot)
|
||||
{
|
||||
_loadedObjects.resize(slot.value() + 1);
|
||||
if (_loadedObjects.size() > static_cast<size_t>(*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<size_t>(*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<const ObjectRepositoryItem*> GetRequiredObjects(const rct_object_entry* entries, size_t count)
|
||||
std::vector<const ObjectRepositoryItem*> GetRequiredObjects(const ObjectList& objectList)
|
||||
{
|
||||
std::vector<const ObjectRepositoryItem*> requiredObjects;
|
||||
std::vector<rct_object_entry> missingObjects;
|
||||
std::vector<ObjectEntryDescriptor> 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<ObjectEntryIndex>(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<Object*> objects;
|
||||
std::vector<Object*> newLoadedObjects;
|
||||
std::vector<rct_object_entry> badObjects;
|
||||
std::vector<ObjectEntryDescriptor> badObjects;
|
||||
objects.resize(OBJECT_ENTRY_COUNT);
|
||||
newLoadedObjects.reserve(OBJECT_ENTRY_COUNT);
|
||||
|
||||
|
@ -612,7 +622,7 @@ private:
|
|||
std::lock_guard<std::mutex> 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<rct_object_entry>& entries)
|
||||
void object_manager_unload_objects(const std::vector<ObjectEntryDescriptor>& entries)
|
||||
{
|
||||
auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
|
||||
objectManager.UnloadObjects(entries);
|
||||
|
|
|
@ -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<rct_object_entry>& entries) abstract;
|
||||
virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& 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<rct_object_entry>& entries);
|
||||
void object_manager_unload_objects(const std::vector<ObjectEntryDescriptor>& entries);
|
||||
void object_manager_unload_all_objects();
|
||||
[[nodiscard]] rct_string_id object_manager_get_source_game_string(const ObjectSourceGame sourceGame);
|
||||
|
|
|
@ -77,7 +77,7 @@ class ObjectFileIndex final : public FileIndex<ObjectRepositoryItem>
|
|||
{
|
||||
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<size_t>(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<size_t>(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<rct_object_entry>();
|
||||
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<const uint8_t*>(entry);
|
||||
|
|
|
@ -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<const ObjectRepositoryItem*>& objects) abstract;
|
||||
|
|
|
@ -24,11 +24,6 @@ private:
|
|||
std::vector<std::array<CoordsXY, 3>> _peepLoadingWaypoints[MAX_VEHICLES_PER_RIDE_ENTRY];
|
||||
|
||||
public:
|
||||
explicit RideObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -25,11 +25,6 @@ private:
|
|||
std::vector<ObjectEntryDescriptor> _items;
|
||||
|
||||
public:
|
||||
explicit SceneryGroupObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -19,10 +19,6 @@ private:
|
|||
ObjectEntryDescriptor _primarySceneryGroupEntry = {};
|
||||
|
||||
public:
|
||||
explicit SceneryObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
virtual ~SceneryObject() = default;
|
||||
|
||||
const ObjectEntryDescriptor& GetPrimarySceneryGroup() const
|
||||
|
|
|
@ -21,11 +21,6 @@ private:
|
|||
std::vector<uint8_t> _frameOffsets;
|
||||
|
||||
public:
|
||||
explicit SmallSceneryObject(const rct_object_entry& entry)
|
||||
: SceneryObject(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,11 +18,6 @@ private:
|
|||
WallSceneryEntry _legacyType = {};
|
||||
|
||||
public:
|
||||
explicit WallObject(const rct_object_entry& entry)
|
||||
: SceneryObject(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -20,11 +20,6 @@ private:
|
|||
rct_water_type _legacyType = {};
|
||||
|
||||
public:
|
||||
explicit WaterObject(const rct_object_entry& entry)
|
||||
: Object(entry)
|
||||
{
|
||||
}
|
||||
|
||||
void* GetLegacyData() override
|
||||
{
|
||||
return &_legacyType;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1464,7 +1464,7 @@ namespace RCT1
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<rct_object_entry> GetRequiredObjects()
|
||||
ObjectList GetRequiredObjects()
|
||||
{
|
||||
std::vector<rct_object_entry> result;
|
||||
AppendRequiredObjects(result, ObjectType::Ride, _rideEntries);
|
||||
|
@ -1489,7 +1489,14 @@ namespace RCT1
|
|||
}));
|
||||
AppendRequiredObjects(result, ObjectType::ParkEntrance, std::vector<const char*>({ "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<RCT1::S4Importer>();
|
||||
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<RCT1::S4Importer>();
|
||||
auto result = s4Importer->LoadScenario(path);
|
||||
objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size());
|
||||
objectMgr.LoadObjects(result.RequiredObjects);
|
||||
s4Importer->Import();
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ namespace RCT1
|
|||
assert(vehObjName != nullptr);
|
||||
std::memcpy(vehicleObject.name, vehObjName, std::min(String::SizeOf(vehObjName), static_cast<size_t>(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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<rct_object_entry> GetRequiredObjects()
|
||||
ObjectList GetRequiredObjects()
|
||||
{
|
||||
std::vector<rct_object_entry> result;
|
||||
|
||||
|
@ -1601,7 +1602,13 @@ public:
|
|||
AddRequiredObjects<MAX_WATER_OBJECTS>(result, _s6.WaterObjects);
|
||||
AddRequiredObjects<MAX_SCENARIO_TEXT_OBJECTS>(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();
|
||||
|
|
|
@ -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<std::mutex> 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<const rct_ride_entry*>(
|
||||
|
|
|
@ -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<TrackDesign> 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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue