diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 9cf8fee65c..ba2b229c39 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -2340,7 +2340,7 @@ void NetworkBase::Client_Handle_OBJECTS_LIST(NetworkConnection& connection, Netw uint32_t flags = 0; packet >> checksum >> flags; - const auto* object = repo.FindObject(objectName); + const auto* object = repo.FindObjectLegacy(objectName); // This could potentially request the object if checksums don't match, but since client // won't replace its version with server-provided one, we don't do that. if (object == nullptr) @@ -2482,7 +2482,7 @@ void NetworkBase::Server_Handle_MAPREQUEST(NetworkConnection& connection, Networ // This is required, as packet does not have null terminator std::string s(name, name + 8); log_verbose("Client requested object %s", s.c_str()); - const ObjectRepositoryItem* item = repo.FindObject(s.c_str()); + const ObjectRepositoryItem* item = repo.FindObjectLegacy(s.c_str()); if (item == nullptr) { log_warning("Client tried getting non-existent object %s from us.", s.c_str()); diff --git a/src/openrct2/object/BannerObject.cpp b/src/openrct2/object/BannerObject.cpp index eb1493b48c..e81b172aa1 100644 --- a/src/openrct2/object/BannerObject.cpp +++ b/src/openrct2/object/BannerObject.cpp @@ -44,7 +44,7 @@ void BannerObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStream* st auto identifier = GetLegacyIdentifier(); auto& objectRepository = context->GetObjectRepository(); - auto item = objectRepository.FindObject(identifier); + auto item = objectRepository.FindObjectLegacy(identifier); if (item != nullptr) { auto sourceGame = item->GetFirstSourceGame(); diff --git a/src/openrct2/object/FootpathItemObject.cpp b/src/openrct2/object/FootpathItemObject.cpp index ee076b2f48..970f9d0806 100644 --- a/src/openrct2/object/FootpathItemObject.cpp +++ b/src/openrct2/object/FootpathItemObject.cpp @@ -48,7 +48,7 @@ void FootpathItemObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStre auto identifier = GetLegacyIdentifier(); auto& objectRepository = context->GetObjectRepository(); - auto item = objectRepository.FindObject(identifier); + auto item = objectRepository.FindObjectLegacy(identifier); if (item != nullptr) { auto sourceGame = item->GetFirstSourceGame(); diff --git a/src/openrct2/object/ObjectManager.cpp b/src/openrct2/object/ObjectManager.cpp index ba93821ce9..3763f5a590 100644 --- a/src/openrct2/object/ObjectManager.cpp +++ b/src/openrct2/object/ObjectManager.cpp @@ -101,10 +101,9 @@ public: return result; } - Object* LoadObject(const rct_object_entry* entry) override + Object* RepositoryItemToObject(const ObjectRepositoryItem* ori) { Object* loadedObject = nullptr; - const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry); if (ori != nullptr) { loadedObject = ori->LoadedObject; @@ -132,6 +131,18 @@ public: return loadedObject; } + Object* LoadObject(std::string_view identifier) override + { + const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier); + return RepositoryItemToObject(ori); + } + + Object* LoadObject(const rct_object_entry* entry) override + { + const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry); + return RepositoryItemToObject(ori); + } + void LoadObjects(const rct_object_entry* entries, size_t count) override { // Find all the required objects @@ -222,52 +233,52 @@ public: // loaded RCT1 and RCT2 save files. // Surfaces - LoadObject("#RCT2SGR"); - LoadObject("#RCT2SSY"); - LoadObject("#RCT2SDI"); - LoadObject("#RCT2SRO"); - LoadObject("#RCT2SMA"); - LoadObject("#RCT2SCH"); - LoadObject("#RCT2SGC"); - LoadObject("#RCT2SIC"); - LoadObject("#RCT2SIR"); - LoadObject("#RCT2SIY"); - LoadObject("#RCT2SIP"); - LoadObject("#RCT2SIG"); - LoadObject("#RCT2SSR"); - LoadObject("#RCT2SSA"); + LoadObject("rct2.surface.grass"); + LoadObject("rct2.surface.sand"); + LoadObject("rct2.surface.dirt"); + LoadObject("rct2.surface.rock"); + LoadObject("rct2.surface.martian"); + LoadObject("rct2.surface.chequerboard"); + LoadObject("rct2.surface.grassclumps"); + LoadObject("rct2.surface.ice"); + LoadObject("rct2.surface.gridred"); + LoadObject("rct2.surface.gridyellow"); + LoadObject("rct2.surface.gridpurple"); + LoadObject("rct2.surface.gridgreen"); + LoadObject("rct2.surface.sandred"); + LoadObject("rct2.surface.sandbrown"); // Edges - LoadObject("#RCT2ERO"); - LoadObject("#RCT2EWR"); - LoadObject("#RCT2EWB"); - LoadObject("#RCT2EIC"); - LoadObject("#RCT1EBR"); - LoadObject("#RCT1EIR"); - LoadObject("#RCT1EGY"); - LoadObject("#RCT1EYE"); - LoadObject("#RCT1ERE"); - LoadObject("#RCT1EPU"); - LoadObject("#RCT1EGR"); - LoadObject("#RCT1ESN"); - LoadObject("#RCT1ESG"); - LoadObject("#RCT1ESA"); - LoadObject("#RCT1ESB"); + LoadObject("rct2.edge.rock"); + LoadObject("rct2.edge.woodred"); + LoadObject("rct2.edge.woodblack"); + LoadObject("rct2.edge.ice"); + LoadObject("rct1.edge.brick"); + LoadObject("rct1.edge.iron"); + LoadObject("rct1.aa.edge.grey"); + LoadObject("rct1.aa.edge.yellow"); + LoadObject("rct1.aa.edge.red"); + LoadObject("rct1.ll.edge.purple"); + LoadObject("rct1.ll.edge.green"); + LoadObject("rct1.ll.edge.stonebrown"); + LoadObject("rct1.ll.edge.stonegrey"); + LoadObject("rct1.ll.edge.skyscrapera"); + LoadObject("rct1.ll.edge.skyscraperb"); // Stations - LoadObject("#RCT2STN"); - LoadObject("#RCT2STW"); - LoadObject("#RCT2STV"); - LoadObject("#RCT2ST3"); - LoadObject("#RCT2ST4"); - LoadObject("#RCT2STJ"); - LoadObject("#RCT2STL"); - LoadObject("#RCT2STC"); - LoadObject("#RCT2STA"); - LoadObject("#RCT2STS"); - LoadObject("#RCT2STP"); - LoadObject("#RCT2STE"); - LoadObject("#ORCT2SN"); + LoadObject("rct2.station.plain"); + LoadObject("rct2.station.wooden"); + LoadObject("rct2.station.canvastent"); + LoadObject("rct2.station.castlegrey"); + LoadObject("rct2.station.castlebrown"); + LoadObject("rct2.station.jungle"); + LoadObject("rct2.station.log"); + LoadObject("rct2.station.classical"); + LoadObject("rct2.station.abstract"); + LoadObject("rct2.station.snow"); + LoadObject("rct2.station.pagoda"); + LoadObject("rct2.station.space"); + LoadObject("openrct2.station.noentrance"); } static rct_string_id GetObjectSourceGameString(const ObjectSourceGame sourceGame) @@ -304,13 +315,6 @@ public: } private: - Object* LoadObject(const std::string& name) - { - rct_object_entry entry{}; - std::copy_n(name.c_str(), 8, entry.name); - return LoadObject(&entry); - } - int32_t FindSpareSlot(uint8_t objectType) { size_t firstIndex = GetIndexFromTypeEntry(objectType, 0); diff --git a/src/openrct2/object/ObjectManager.h b/src/openrct2/object/ObjectManager.h index dda14a4172..85e8051eb6 100644 --- a/src/openrct2/object/ObjectManager.h +++ b/src/openrct2/object/ObjectManager.h @@ -30,6 +30,7 @@ struct IObjectManager virtual ObjectEntryIndex GetLoadedObjectEntryIndex(const Object* object) abstract; virtual std::vector GetInvalidObjects(const rct_object_entry* entries) abstract; + 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 void LoadDefaultObjects() abstract; diff --git a/src/openrct2/object/ObjectRepository.cpp b/src/openrct2/object/ObjectRepository.cpp index e0884aaecd..cda814a397 100644 --- a/src/openrct2/object/ObjectRepository.cpp +++ b/src/openrct2/object/ObjectRepository.cpp @@ -68,13 +68,14 @@ struct ObjectEntryEqual } }; +using ObjectIdentifierMap = std::unordered_map; using ObjectEntryMap = std::unordered_map; class ObjectFileIndex final : public FileIndex { private: static constexpr uint32_t MAGIC_NUMBER = 0x5844494F; // OIDX - static constexpr uint16_t VERSION = 21; + static constexpr uint16_t VERSION = 22; static constexpr auto PATTERN = "*.dat;*.pob;*.json;*.parkobj"; IObjectRepository& _objectRepository; @@ -111,6 +112,7 @@ public: if (object != nullptr) { ObjectRepositoryItem item = {}; + item.Identifier = object->GetIdentifier(); item.ObjectEntry = *object->GetObjectEntry(); item.Path = path; item.Name = object->GetName(); @@ -125,6 +127,7 @@ public: protected: void Serialise(IStream* stream, const ObjectRepositoryItem& item) const override { + stream->WriteValue(item.Identifier); stream->WriteValue(item.ObjectEntry); stream->WriteString(item.Path); stream->WriteString(item.Name); @@ -170,6 +173,7 @@ protected: { ObjectRepositoryItem item; + item.Identifier = stream->ReadStdString(); item.ObjectEntry = stream->ReadValue(); item.Path = stream->ReadStdString(); item.Name = stream->ReadStdString(); @@ -227,6 +231,7 @@ class ObjectRepository final : public IObjectRepository std::shared_ptr const _env; ObjectFileIndex const _fileIndex; std::vector _items; + ObjectIdentifierMap _newItemMap; ObjectEntryMap _itemMap; public: @@ -266,7 +271,7 @@ public: return _items.data(); } - const ObjectRepositoryItem* FindObject(const std::string_view& legacyIdentifier) const override + const ObjectRepositoryItem* FindObjectLegacy(const std::string_view& legacyIdentifier) const override { rct_object_entry entry = {}; entry.SetName(legacyIdentifier); @@ -279,6 +284,16 @@ public: return nullptr; } + const ObjectRepositoryItem* FindObject(std::string_view identifier) const override final + { + auto kvp = _newItemMap.find(std::string(identifier)); + if (kvp != _newItemMap.end()) + { + return &_items[kvp->second]; + } + return nullptr; + } + const ObjectRepositoryItem* FindObject(const rct_object_entry* objectEntry) const override final { auto kvp = _itemMap.find(*objectEntry); @@ -408,6 +423,7 @@ private: void ClearItems() { _items.clear(); + _newItemMap.clear(); _itemMap.clear(); } @@ -425,10 +441,15 @@ private: // Rebuild item map _itemMap.clear(); + _newItemMap.clear(); for (size_t i = 0; i < _items.size(); i++) { rct_object_entry entry = _items[i].ObjectEntry; _itemMap[entry] = i; + if (!_items[i].Identifier.empty()) + { + _newItemMap[_items[i].Identifier] = i; + } } } @@ -457,6 +478,10 @@ private: auto copy = item; copy.Id = index; _items.push_back(copy); + if (!item.Identifier.empty()) + { + _newItemMap[item.Identifier] = index; + } _itemMap[item.ObjectEntry] = index; return true; } @@ -746,7 +771,7 @@ const ObjectRepositoryItem* object_repository_find_object_by_entry(const rct_obj const ObjectRepositoryItem* object_repository_find_object_by_name(const char* name) { auto& objectRepository = GetContext()->GetObjectRepository(); - return objectRepository.FindObject(name); + return objectRepository.FindObjectLegacy(name); } bool object_entry_compare(const rct_object_entry* a, const rct_object_entry* b) diff --git a/src/openrct2/object/ObjectRepository.h b/src/openrct2/object/ObjectRepository.h index 503e282d21..5b3a7cf877 100644 --- a/src/openrct2/object/ObjectRepository.h +++ b/src/openrct2/object/ObjectRepository.h @@ -37,6 +37,7 @@ struct rct_drawpixelinfo; struct ObjectRepositoryItem { size_t Id; + std::string Identifier; // e.g. rct2.c3d rct_object_entry ObjectEntry; std::string Path; std::string Name; @@ -71,7 +72,8 @@ struct IObjectRepository virtual void Construct(int32_t language) abstract; virtual size_t GetNumObjects() const abstract; virtual const ObjectRepositoryItem* GetObjects() const abstract; - virtual const ObjectRepositoryItem* FindObject(const std::string_view& legacyIdentifier) const abstract; + virtual const ObjectRepositoryItem* FindObjectLegacy(const std::string_view& legacyIdentifier) const abstract; + virtual const ObjectRepositoryItem* FindObject(std::string_view identifier) const abstract; virtual const ObjectRepositoryItem* FindObject(const rct_object_entry* objectEntry) const abstract; virtual std::unique_ptr LoadObject(const ObjectRepositoryItem* ori) abstract; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 3327bd4a1a..b39ee18d53 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -526,7 +526,7 @@ private: for (const char* objectName : objects) { auto& objectRepository = OpenRCT2::GetContext()->GetObjectRepository(); - auto foundObject = objectRepository.FindObject(objectName); + auto foundObject = objectRepository.FindObjectLegacy(objectName); if (foundObject != nullptr) { uint8_t objectType = foundObject->ObjectEntry.GetType(); diff --git a/src/openrct2/rct1/Tables.cpp b/src/openrct2/rct1/Tables.cpp index 536c41c3c1..0982ebd626 100644 --- a/src/openrct2/rct1/Tables.cpp +++ b/src/openrct2/rct1/Tables.cpp @@ -143,22 +143,22 @@ namespace RCT1 { static constexpr const uint8_t map[] = { - TERRAIN_EDGE_ROCK, // #RCT2ERO - TERRAIN_EDGE_BRICK, // #RCT1EBR - TERRAIN_EDGE_IRON, // #RCT1EIR - TERRAIN_EDGE_WOOD_RED, // #RCT2EWR - TERRAIN_EDGE_GREY, // #RCT1EGY - TERRAIN_EDGE_YELLOW, // #RCT1EYE - TERRAIN_EDGE_WOOD_BLACK, // #RCT2EWB - TERRAIN_EDGE_RED, // #RCT1ERE - TERRAIN_EDGE_ICE, // #RCT2EIC - TERRAIN_EDGE_PURPLE, // #RCT2EIC - TERRAIN_EDGE_GREEN, // #RCT1EGR - TERRAIN_EDGE_STONE_BROWN, // #RCT1ESN - TERRAIN_EDGE_STONE_GREY, // #RCT1ESG - TERRAIN_EDGE_SKYSCRAPER_A, // #RCT1ESA - TERRAIN_EDGE_SKYSCRAPER_B, // #RCT1ESB - TERRAIN_EDGE_ROCK // #RCT2ERO (Unused) + TERRAIN_EDGE_ROCK, // rct2.edge.rock + TERRAIN_EDGE_BRICK, // rct1.edge.brick + TERRAIN_EDGE_IRON, // rct1.edge.iron + TERRAIN_EDGE_WOOD_RED, // rct2.edge.woodred + TERRAIN_EDGE_GREY, // rct1.aa.edge.grey + TERRAIN_EDGE_YELLOW, // rct1.aa.edge.yellow + TERRAIN_EDGE_WOOD_BLACK, // rct2.edge.woodblack + TERRAIN_EDGE_RED, // rct1.aa.edge.red + TERRAIN_EDGE_ICE, // rct2.edge.ice + TERRAIN_EDGE_PURPLE, // rct1.ll.edge.purple + TERRAIN_EDGE_GREEN, // rct1.ll.edge.green + TERRAIN_EDGE_STONE_BROWN, // rct1.ll.edge.stonebrown + TERRAIN_EDGE_STONE_GREY, // rct1.ll.edge.stonegrey + TERRAIN_EDGE_SKYSCRAPER_A, // rct1.ll.edge.skyscrapera + TERRAIN_EDGE_SKYSCRAPER_B, // rct1.ll.edge.skyscraperb + TERRAIN_EDGE_ROCK // rct2.edge.rock (Unused) }; Guard::ArgumentInRange(terrainEdge, 0, std::size(map), "Unsupported RCT1 terrain edge."); return map[terrainEdge]; diff --git a/src/openrct2/ride/TrackDesignRepository.cpp b/src/openrct2/ride/TrackDesignRepository.cpp index 3f676e4e3c..a032ad6f9d 100644 --- a/src/openrct2/ride/TrackDesignRepository.cpp +++ b/src/openrct2/ride/TrackDesignRepository.cpp @@ -164,7 +164,7 @@ public: bool entryIsNotSeparate = false; if (entry.empty()) { - const ObjectRepositoryItem* ori = repo.FindObject(item.ObjectEntry.c_str()); + const ObjectRepositoryItem* ori = repo.FindObjectLegacy(item.ObjectEntry.c_str()); if (ori == nullptr || !RideTypeDescriptors[rideType].HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) entryIsNotSeparate = true; @@ -198,7 +198,7 @@ public: bool entryIsNotSeparate = false; if (entry.empty()) { - const ObjectRepositoryItem* ori = repo.FindObject(item.ObjectEntry.c_str()); + const ObjectRepositoryItem* ori = repo.FindObjectLegacy(item.ObjectEntry.c_str()); if (ori == nullptr || !RideTypeDescriptors[rideType].HasFlag(RIDE_TYPE_FLAG_LIST_VEHICLES_SEPARATELY)) entryIsNotSeparate = true;