diff --git a/openrct2.vcxproj b/openrct2.vcxproj index df88b9b32d..d8f417eea1 100644 --- a/openrct2.vcxproj +++ b/openrct2.vcxproj @@ -127,6 +127,7 @@ + @@ -444,6 +445,7 @@ + diff --git a/src/object.h b/src/object.h index 157348e49f..d5a2dea50a 100644 --- a/src/object.h +++ b/src/object.h @@ -114,7 +114,7 @@ int object_load_packed(SDL_RWops* rw); bool object_saved_packed(SDL_RWops* rw, const rct_object_entry * entry); void object_unload_all(); -int check_object_entry(rct_object_entry *entry); +int check_object_entry(const rct_object_entry *entry); bool object_load_chunk(int groupIndex, const rct_object_entry * entry, int * outGroupIndex); bool object_entry_compare(const rct_object_entry *a, const rct_object_entry *b); int object_calculate_checksum(const rct_object_entry * entry, const void * data, size_t dataLength); diff --git a/src/object/ObjectManager.cpp b/src/object/ObjectManager.cpp new file mode 100644 index 0000000000..3a0a09ac31 --- /dev/null +++ b/src/object/ObjectManager.cpp @@ -0,0 +1,299 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#include +#include +#include "../core/Console.hpp" +#include "../core/Memory.hpp" +#include "Object.h" +#include "ObjectManager.h" +#include "ObjectRepository.h" + +extern "C" +{ + #include "../object_list.h" +} + +class ObjectManager : public IObjectManager +{ +private: + IObjectRepository * _objectRepository; + Object * * _loadedObjects = nullptr; + +public: + ObjectManager(IObjectRepository * objectRepository) + { + _objectRepository = objectRepository; + } + + ~ObjectManager() override + { + SetNewLoadedObjectList(nullptr); + } + + Object * GetLoadedObject(size_t index) override + { + if (_loadedObjects == nullptr) + { + return nullptr; + } + return _loadedObjects[index]; + } + + bool LoadObjects(const rct_object_entry * entries, size_t count) override + { + IObjectRepository * objectRepository = GetObjectRepository(); + + // Find all the required objects + size_t numRequiredObjects; + auto requiredObjects = new const ObjectRepositoryItem *[OBJECT_ENTRY_COUNT]; + if (!GetRequiredObjects(entries, requiredObjects, &numRequiredObjects)) + { + return false; + } + + // Create a new list of loaded objects + size_t numNewLoadedObjects; + Object * * loadedObjects = LoadObjects(requiredObjects, &numNewLoadedObjects); + + delete requiredObjects; + + if (loadedObjects == nullptr) + { + UnloadAll(); + return false; + } + else + { + SetNewLoadedObjectList(loadedObjects); + UpdateLegacyLoadedObjectList(); + reset_type_to_ride_entry_index_map(); + Console::WriteLine("%u / %u new objects loaded", numNewLoadedObjects, numRequiredObjects); + return true; + } + } + + void UnloadAll() override + { + if (_loadedObjects != nullptr) + { + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + UnloadObject(_loadedObjects[i]); + _loadedObjects[i] = nullptr; + } + } + UpdateLegacyLoadedObjectList(); + reset_type_to_ride_entry_index_map(); + } + +private: + void SetNewLoadedObjectList(Object * * newLoadedObjects) + { + if (newLoadedObjects == nullptr) + { + UnloadAll(); + } + else + { + UnloadObjectsExcept(newLoadedObjects); + } + Memory::Free(_loadedObjects); + _loadedObjects = newLoadedObjects; + } + + void UnloadObject(Object * object) + { + if (object != nullptr) + { + // TODO try to prevent doing a repository search + const ObjectRepositoryItem * ori = _objectRepository->FindObject(object->GetObjectEntry()); + if (ori != nullptr) + { + _objectRepository->UnregisterLoadedObject(ori, object); + } + + object->Unload(); + delete object; + } + } + + void UnloadObjectsExcept(Object * * newLoadedObjects) + { + if (_loadedObjects == nullptr) + { + return; + } + + // Build a hash set for quick checking + auto exceptSet = std::unordered_set(); + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + Object * object = newLoadedObjects[i]; + if (object != nullptr) + { + exceptSet.insert(object); + } + } + + // Unload objects that are not in the hash set + size_t totalObjectsLoaded = 0; + size_t numObjectsUnloaded = 0; + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + Object * object = _loadedObjects[i]; + if (object != nullptr) + { + totalObjectsLoaded++; + if (exceptSet.find(object) == exceptSet.end()) + { + UnloadObject(object); + _loadedObjects[i] = nullptr; + numObjectsUnloaded++; + } + } + } + + Console::WriteLine("%u / %u objects unloaded", numObjectsUnloaded, totalObjectsLoaded); + } + + void UpdateLegacyLoadedObjectList() + { + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + Object * loadedObject = nullptr; + if (_loadedObjects != nullptr) + { + loadedObject = _loadedObjects[i]; + } + + uint8 objectType, entryIndex; + get_type_entry_index(i, &objectType, &entryIndex); + + rct_object_entry_extended * legacyEntry = &object_entry_groups[objectType].entries[entryIndex]; + void * * legacyChunk = &object_entry_groups[objectType].chunks[entryIndex]; + if (loadedObject == nullptr) + { + Memory::Set(legacyEntry, 0xFF, sizeof(rct_object_entry_extended)); + *legacyChunk = (void *)-1; + } + else + { + legacyEntry->entry = *loadedObject->GetObjectEntry(); + legacyEntry->chunk_size = 0; + *legacyChunk = loadedObject->GetLegacyData(); + } + } + } + + bool GetRequiredObjects(const rct_object_entry * entries, + const ObjectRepositoryItem * * requiredObjects, + size_t * outNumRequiredObjects) + { + bool missingObjects = false; + size_t numRequiredObjects = 0; + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + const rct_object_entry * entry = &entries[i]; + const ObjectRepositoryItem * ori = nullptr; + if (check_object_entry(entry)) + { + ori = _objectRepository->FindObject(entry); + if (ori == nullptr) + { + missingObjects = true; + ReportMissingObject(entry); + } + numRequiredObjects++; + } + requiredObjects[i] = ori; + } + + if (outNumRequiredObjects != nullptr) + { + *outNumRequiredObjects = numRequiredObjects; + } + return !missingObjects; + } + + Object * * LoadObjects(const ObjectRepositoryItem * * requiredObjects, size_t * outNewObjectsLoaded) + { + size_t newObjectsLoaded = 0; + Object * * loadedObjects = Memory::AllocateArray(OBJECT_ENTRY_COUNT); + for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + { + Object * loadedObject = nullptr; + const ObjectRepositoryItem * ori = requiredObjects[i]; + if (ori != nullptr) + { + loadedObject = ori->LoadedObject; + if (loadedObject == nullptr) + { + // Try to load object + loadedObject = _objectRepository->LoadObject(ori); + if (loadedObject == nullptr) + { + ReportObjectLoadProblem(&ori->ObjectEntry); + Memory::Free(loadedObjects); + return nullptr; + } + else + { + loadedObject->Load(); + newObjectsLoaded++; + } + + // Connect the ori to the registered object + _objectRepository->RegisterLoadedObject(ori, loadedObject); + } + } + loadedObjects[i] = loadedObject; + } + if (outNewObjectsLoaded != nullptr) + { + *outNewObjectsLoaded = newObjectsLoaded; + } + return loadedObjects; + } + + static void ReportMissingObject(const rct_object_entry * entry) + { + utf8 objName[9] = { 0 }; + Memory::Copy(objName, entry->name, 8); + Console::Error::WriteFormat("[%s]: Object not found.", objName); + Console::Error::WriteLine(); + } + + static void ReportObjectLoadProblem(const rct_object_entry * entry) + { + utf8 objName[9] = { 0 }; + Memory::Copy(objName, entry->name, 8); + Console::Error::WriteFormat("[%s]: Object could not be loaded.", objName); + Console::Error::WriteLine(); + } +}; + +ObjectManager * _objectManager; + +IObjectManager * GetObjectManager() +{ + if (_objectManager == nullptr) + { + IObjectRepository * objectRepository = GetObjectRepository(); + _objectManager = new ObjectManager(objectRepository); + } + return _objectManager; +} diff --git a/src/object/ObjectManager.h b/src/object/ObjectManager.h new file mode 100644 index 0000000000..4e09950fee --- /dev/null +++ b/src/object/ObjectManager.h @@ -0,0 +1,40 @@ +#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers +/***************************************************************************** + * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. + * + * OpenRCT2 is the work of many authors, a full list can be found in contributors.md + * For more information, visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * A full copy of the GNU General Public License can be found in licence.txt + *****************************************************************************/ +#pragma endregion + +#pragma once + +#include "../common.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + #include "../object.h" +#ifdef __cplusplus +} +#endif + +interface IObjectManager +{ + virtual ~IObjectManager() { } + + virtual Object * GetLoadedObject(size_t index) abstract; + + virtual bool LoadObjects(const rct_object_entry * entries, size_t count) abstract; + virtual void UnloadAll() abstract; +}; + +IObjectManager * GetObjectManager(); diff --git a/src/object/ObjectRepository.cpp b/src/object/ObjectRepository.cpp index 7385d3cc1f..abc7264cd7 100644 --- a/src/object/ObjectRepository.cpp +++ b/src/object/ObjectRepository.cpp @@ -30,6 +30,7 @@ #include "../core/String.hpp" #include "Object.h" #include "ObjectFactory.h" +#include "ObjectManager.h" #include "ObjectRepository.h" #include "RideObject.h" #include "StexObject.h" @@ -39,6 +40,7 @@ extern "C" #include "../config.h" #include "../localisation/localisation.h" #include "../object.h" + #include "../object_list.h" #include "../platform/platform.h" #include "../scenario.h" #include "../util/sawyercoding.h" @@ -87,6 +89,8 @@ struct ObjectEntryEqual using ObjectEntryMap = std::unordered_map; +static void ReportMissingObject(const rct_object_entry * entry); + class ObjectRepository : public IObjectRepository { std::vector _items; @@ -163,6 +167,23 @@ public: return object; } + void RegisterLoadedObject(const ObjectRepositoryItem * ori, Object * object) override + { + ObjectRepositoryItem * item = &_items[ori->Id]; + + Guard::Assert(item->LoadedObject == nullptr); + item->LoadedObject = object; + } + + void UnregisterLoadedObject(const ObjectRepositoryItem * ori, Object * object) override + { + ObjectRepositoryItem * item = &_items[ori->Id]; + if (item->LoadedObject == object) + { + item->LoadedObject = nullptr; + } + } + void AddObject(const rct_object_entry * objectEntry, const void * data, size_t dataSize) override { char objectName[9] = { 0 }; @@ -364,8 +385,9 @@ private: const ObjectRepositoryItem * conflict = FindObject(&item->ObjectEntry); if (conflict == nullptr) { + size_t index = _items.size(); + item->Id = index; _items.push_back(*item); - size_t index = _items.size() - 1; _itemMap[item->ObjectEntry] = index; return true; } @@ -554,9 +576,7 @@ IObjectRepository * GetObjectRepository() return _objectRepository; } -Object * _loadedObjects[OBJECT_ENTRY_COUNT] = { nullptr }; - -int GetObjectEntryIndex(uint8 objectType, uint8 entryIndex) +static int GetObjectEntryIndex(uint8 objectType, uint8 entryIndex) { int result = 0; for (uint8 i = 0; i < objectType; i++) @@ -590,95 +610,62 @@ extern "C" bool object_load_chunk(int groupIndex, const rct_object_entry * entry, int * outGroupIndex) { - IObjectRepository * objectRepository = GetObjectRepository(); - const ObjectRepositoryItem * ori = objectRepository->FindObject(entry); - if (ori == nullptr) - { - utf8 objName[9] = { 0 }; - Memory::Copy(objName, ori->ObjectEntry.name, 8); - Console::Error::WriteFormat("[%s]: Object not found.", objName); - Console::Error::WriteLine(); - return false; - } + return false; - Object * object = objectRepository->LoadObject(ori); - if (object == nullptr) - { - utf8 objName[9] = { 0 }; - Memory::Copy(objName, ori->ObjectEntry.name, 8); - Console::Error::WriteFormat("[%s]: Object could not be loaded.", objName); - Console::Error::WriteLine(); - return false; - } - - uint8 objectType = object->GetObjectType(); - void * * chunkList = object_entry_groups[objectType].chunks; - if (groupIndex == -1) - { - for (groupIndex = 0; chunkList[groupIndex] != (void*)-1; groupIndex++) - { - if (groupIndex + 1 >= object_entry_group_counts[objectType]) - { - log_error("Object Load failed due to too many objects of a certain type."); - delete object; - return false; - } - } - } - chunkList[groupIndex] = object->GetLegacyData(); - if (outGroupIndex != nullptr) - { - *outGroupIndex = groupIndex; - } - - rct_object_entry_extended * extendedEntry = &object_entry_groups[objectType].entries[groupIndex]; - Memory::Copy(extendedEntry, object->GetObjectEntry(), sizeof(rct_object_entry)); - extendedEntry->chunk_size = 0; - - int loadedObjectIndex = GetObjectEntryIndex(objectType, groupIndex); - delete _loadedObjects[loadedObjectIndex]; - _loadedObjects[loadedObjectIndex] = object; - return true; + // IObjectRepository * objectRepository = GetObjectRepository(); + // const ObjectRepositoryItem * ori = objectRepository->FindObject(entry); + // if (ori == nullptr) + // { + // ReportMissingObject(entry); + // return false; + // } + // + // Object * object = objectRepository->LoadObject(ori); + // if (object == nullptr) + // { + // utf8 objName[9] = { 0 }; + // Memory::Copy(objName, ori->ObjectEntry.name, 8); + // Console::Error::WriteFormat("[%s]: Object could not be loaded.", objName); + // Console::Error::WriteLine(); + // return false; + // } + // + // uint8 objectType = object->GetObjectType(); + // void * * chunkList = object_entry_groups[objectType].chunks; + // if (groupIndex == -1) + // { + // for (groupIndex = 0; chunkList[groupIndex] != (void*)-1; groupIndex++) + // { + // if (groupIndex + 1 >= object_entry_group_counts[objectType]) + // { + // log_error("Object Load failed due to too many objects of a certain type."); + // delete object; + // return false; + // } + // } + // } + // chunkList[groupIndex] = object->GetLegacyData(); + // if (outGroupIndex != nullptr) + // { + // *outGroupIndex = groupIndex; + // } + // + // rct_object_entry_extended * extendedEntry = &object_entry_groups[objectType].entries[groupIndex]; + // Memory::Copy(extendedEntry, object->GetObjectEntry(), sizeof(rct_object_entry)); + // extendedEntry->chunk_size = 0; + // + // int loadedObjectIndex = GetObjectEntryIndex(objectType, groupIndex); + // delete _loadedObjects[loadedObjectIndex]; + // _loadedObjects[loadedObjectIndex] = object; + // return true; } bool object_load_entries(rct_object_entry * entries) { log_verbose("loading required objects"); - object_unload_all(); - - bool loadFailed = false; - - // Load each object - for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) - { - if (check_object_entry(&entries[i])) - { - // Get entry group index - int entryGroupIndex = i; - for (int j = 0; j < OBJECT_ENTRY_GROUP_COUNT; j++) - { - if (entryGroupIndex < object_entry_group_counts[j]) - { - break; - } - entryGroupIndex -= object_entry_group_counts[j]; - } - - // Load the obect - if (!object_load_chunk(entryGroupIndex, &entries[i], NULL)) { - // log_error("failed to load entry: %.8s", entries[i].name); - // memcpy(gCommonFormatArgs, &entries[i], sizeof(rct_object_entry)); - loadFailed = true; - } - } - } - - if (loadFailed) - { - object_unload_all(); - return false; - } + IObjectManager * objectManger = GetObjectManager(); + objectManger->LoadObjects(entries, OBJECT_ENTRY_COUNT); log_verbose("finished loading required objects"); return true; @@ -686,17 +673,20 @@ extern "C" void reset_loaded_objects() { - for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) - { - Object * object = _loadedObjects[i]; - if (object != nullptr) - { - object->Unload(); - object->Load(); - } - } - - reset_type_to_ride_entry_index_map(); + // if (_loadedObjects != nullptr) + // { + // for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) + // { + // Object * object = _loadedObjects[i]; + // if (object != nullptr) + // { + // object->Unload(); + // object->Load(); + // } + // } + // } + // + // reset_type_to_ride_entry_index_map(); } void * object_repository_load_object(const rct_object_entry * objectEntry) @@ -717,25 +707,24 @@ extern "C" void * object_repository_find_loaded_object(const rct_object_entry * objectEntry) { - for (size_t i = 0; i < OBJECT_ENTRY_COUNT; i++) + Object * object = nullptr; + + IObjectRepository * objectRepository = GetObjectRepository(); + const ObjectRepositoryItem * ori = objectRepository->FindObject(objectEntry); + if (ori != nullptr) { - Object * object = _loadedObjects[i]; - if (object != nullptr) - { - const rct_object_entry * entry = object->GetObjectEntry(); - if (memcmp(objectEntry->name, entry->name, 8) == 0) - { - return (void *)object; - } - } + object = ori->LoadedObject; } - return nullptr; + + return (void *)object; } void * object_repository_get_loaded_object(uint8 objectType, uint8 entryIndex) { int index = GetObjectEntryIndex(objectType, entryIndex); - return (void *)_loadedObjects[index]; + + IObjectManager * objectManager = GetObjectManager(); + return (void *)objectManager->GetLoadedObject(index); } void object_repository_unload(size_t itemIndex) @@ -745,24 +734,8 @@ extern "C" void object_unload_all() { - for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) - { - Object * object = _loadedObjects[i]; - if (object != nullptr) - { - object->Unload(); - delete object; - _loadedObjects[i] = nullptr; - } - } - for (int i = 0; i < OBJECT_ENTRY_GROUP_COUNT; i++) - { - for (int j = 0; j < object_entry_group_counts[i]; j++) - { - memset(&object_entry_groups[i].entries[j], 0xFF, sizeof(rct_object_entry_extended)); - object_entry_groups[i].chunks[j] = (uint8*)0xFFFFFFFF; - } - } + IObjectManager * objectManager = GetObjectManager(); + objectManager->UnloadAll(); } void scenario_translate(scenario_index_entry * scenarioEntry, const rct_object_entry * stexObjectEntry) @@ -996,3 +969,11 @@ extern "C" return (int)checksum; } } + +static void ReportMissingObject(const rct_object_entry * entry) +{ + utf8 objName[9] = { 0 }; + Memory::Copy(objName, entry->name, 8); + Console::Error::WriteFormat("[%s]: Object not found.", objName); + Console::Error::WriteLine(); +} diff --git a/src/object/ObjectRepository.h b/src/object/ObjectRepository.h index 7162056fd6..45e0602a13 100644 --- a/src/object/ObjectRepository.h +++ b/src/object/ObjectRepository.h @@ -35,6 +35,7 @@ extern "C" typedef struct ObjectRepositoryItem { + size_t Id; rct_object_entry ObjectEntry; utf8 * Path; utf8 * Name; @@ -67,6 +68,9 @@ interface IObjectRepository virtual const ObjectRepositoryItem * FindObject(const rct_object_entry * objectEntry) const abstract; virtual Object * LoadObject(const ObjectRepositoryItem * ori) abstract; + virtual void RegisterLoadedObject(const ObjectRepositoryItem * ori, Object * object) abstract; + virtual void UnregisterLoadedObject(const ObjectRepositoryItem * ori, Object * object) abstract; + virtual void AddObject(const rct_object_entry * objectEntry, const void * data, size_t dataSize) abstract; diff --git a/src/object_list.c b/src/object_list.c index a813515fd7..d43f0070fa 100644 --- a/src/object_list.c +++ b/src/object_list.c @@ -91,7 +91,7 @@ const rct_object_entry_group object_entry_groups[] = { (void**)(gStexEntries ), (rct_object_entry_extended*)(0x00F3F03C + (720 * 20)) // scenario text 0x009ADAE4, 0xF4287C }; -int check_object_entry(rct_object_entry *entry) +int check_object_entry(const rct_object_entry *entry) { uint32 *dwords = (uint32*)entry; return (0xFFFFFFFF & dwords[0] & dwords[1] & dwords[2] & dwords[3]) + 1 != 0; diff --git a/src/object_list.h b/src/object_list.h index 4277cb569e..040b1d6e3d 100644 --- a/src/object_list.h +++ b/src/object_list.h @@ -34,5 +34,6 @@ #endif void object_list_init(); +void get_type_entry_index(size_t index, uint8 * outObjectType, uint8 * outEntryIndex); const rct_object_entry * get_loaded_object_entry(size_t index); void * get_loaded_object_chunk(size_t index);