Modify object loading for JSON-only objects

This commit is contained in:
Gymnasiast 2021-09-12 21:25:22 +02:00
parent 37821ce25e
commit bdab3219cb
No known key found for this signature in database
GPG Key ID: DBFFF47AB2CA3EDD
52 changed files with 707 additions and 500 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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);

View File

@ -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());
}

View File

@ -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)

View File

@ -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();

View File

@ -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.

View File

@ -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)
{

View File

@ -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);
/**

View File

@ -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))
{
}

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -19,11 +19,6 @@ private:
BannerSceneryEntry _legacyType = {};
public:
explicit BannerObject(const rct_object_entry& entry)
: SceneryObject(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -18,11 +18,6 @@ private:
rct_entrance_type _legacyType = {};
public:
explicit EntranceObject(const rct_object_entry& entry)
: Object(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -18,11 +18,6 @@ private:
PathBitEntry _legacyType = {};
public:
explicit FootpathItemObject(const rct_object_entry& entry)
: SceneryObject(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -21,11 +21,6 @@ private:
PathRailingsDescriptor _pathRailingsDescriptor = {};
public:
explicit FootpathObject(const rct_object_entry& entry)
: Object(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -25,11 +25,6 @@ private:
std::vector<ObjectEntryDescriptor> _items;
public:
explicit SceneryGroupObject(const rct_object_entry& entry)
: Object(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -19,10 +19,6 @@ private:
ObjectEntryDescriptor _primarySceneryGroupEntry = {};
public:
explicit SceneryObject(const rct_object_entry& entry)
: Object(entry)
{
}
virtual ~SceneryObject() = default;
const ObjectEntryDescriptor& GetPrimarySceneryGroup() const

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -18,11 +18,6 @@ private:
WallSceneryEntry _legacyType = {};
public:
explicit WallObject(const rct_object_entry& entry)
: SceneryObject(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -20,11 +20,6 @@ private:
rct_water_type _legacyType = {};
public:
explicit WaterObject(const rct_object_entry& entry)
: Object(entry)
{
}
void* GetLegacyData() override
{
return &_legacyType;

View File

@ -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();

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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*>(

View File

@ -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;
}

View File

@ -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];

View File

@ -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))
{

View File

@ -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;