1 /*****************************************************************************
2  * Copyright (c) 2014-2020 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "ObjectManager.h"
11 
12 #include "../Context.h"
13 #include "../ParkImporter.h"
14 #include "../core/Console.hpp"
15 #include "../core/Memory.hpp"
16 #include "../localisation/StringIds.h"
17 #include "../util/Util.h"
18 #include "FootpathItemObject.h"
19 #include "LargeSceneryObject.h"
20 #include "Object.h"
21 #include "ObjectList.h"
22 #include "ObjectRepository.h"
23 #include "RideObject.h"
24 #include "SceneryGroupObject.h"
25 #include "SmallSceneryObject.h"
26 #include "WallObject.h"
27 
28 #include <algorithm>
29 #include <array>
30 #include <memory>
31 #include <mutex>
32 #include <thread>
33 #include <unordered_set>
34 
35 class ObjectManager final : public IObjectManager
36 {
37 private:
38     IObjectRepository& _objectRepository;
39 
40     std::vector<Object*> _loadedObjects;
41     std::array<std::vector<ObjectEntryIndex>, RIDE_TYPE_COUNT> _rideTypeToObjectMap;
42 
43     // Used to return a safe empty vector back from GetAllRideEntries, can be removed when std::span is available
44     std::vector<ObjectEntryIndex> _nullRideTypeEntries;
45 
46 public:
ObjectManager(IObjectRepository & objectRepository)47     explicit ObjectManager(IObjectRepository& objectRepository)
48         : _objectRepository(objectRepository)
49     {
50         _loadedObjects.resize(OBJECT_ENTRY_COUNT);
51 
52         UpdateSceneryGroupIndexes();
53         ResetTypeToRideEntryIndexMap();
54     }
55 
~ObjectManager()56     ~ObjectManager() override
57     {
58         UnloadAll();
59     }
60 
GetLoadedObject(size_t index)61     Object* GetLoadedObject(size_t index) override
62     {
63         if (index >= _loadedObjects.size())
64         {
65             return nullptr;
66         }
67         return _loadedObjects[index];
68     }
69 
GetLoadedObject(ObjectType objectType,size_t index)70     Object* GetLoadedObject(ObjectType objectType, size_t index) override
71     {
72         // This is sometimes done deliberately (to avoid boilerplate), so no need to log_warn for this.
73         if (index == OBJECT_ENTRY_INDEX_NULL)
74         {
75             return nullptr;
76         }
77 
78         if (index >= static_cast<size_t>(object_entry_group_counts[EnumValue(objectType)]))
79         {
80 #ifdef DEBUG
81             log_warning("Object index %u exceeds maximum for type %d.", index, objectType);
82 #endif
83             return nullptr;
84         }
85 
86         auto objectIndex = GetIndexFromTypeEntry(objectType, index);
87         return GetLoadedObject(objectIndex);
88     }
89 
GetLoadedObject(const ObjectEntryDescriptor & entry)90     Object* GetLoadedObject(const ObjectEntryDescriptor& entry) override
91     {
92         const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry);
93         if (ori == nullptr)
94             return nullptr;
95 
96         return ori->LoadedObject.get();
97     }
98 
GetLoadedObjectEntryIndex(std::string_view identifier)99     ObjectEntryIndex GetLoadedObjectEntryIndex(std::string_view identifier) override
100     {
101         const auto* obj = GetLoadedObject(ObjectEntryDescriptor(identifier));
102         if (obj != nullptr)
103         {
104             return GetLoadedObjectEntryIndex(obj);
105         }
106         return OBJECT_ENTRY_INDEX_NULL;
107     }
108 
GetLoadedObjectEntryIndex(const ObjectEntryDescriptor & descriptor)109     ObjectEntryIndex GetLoadedObjectEntryIndex(const ObjectEntryDescriptor& descriptor) override
110     {
111         auto obj = GetLoadedObject(descriptor);
112         if (obj != nullptr)
113         {
114             return GetLoadedObjectEntryIndex(obj);
115         }
116         return OBJECT_ENTRY_INDEX_NULL;
117     }
118 
GetLoadedObjectEntryIndex(const Object * object)119     ObjectEntryIndex GetLoadedObjectEntryIndex(const Object* object) override
120     {
121         ObjectEntryIndex result = OBJECT_ENTRY_INDEX_NULL;
122         size_t index = GetLoadedObjectIndex(object);
123         if (index != SIZE_MAX)
124         {
125             get_type_entry_index(index, nullptr, &result);
126         }
127         return result;
128     }
129 
LoadObject(std::string_view identifier)130     Object* LoadObject(std::string_view identifier) override
131     {
132         const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier);
133         return RepositoryItemToObject(ori);
134     }
135 
LoadObject(const rct_object_entry * entry)136     Object* LoadObject(const rct_object_entry* entry) override
137     {
138         const ObjectRepositoryItem* ori = _objectRepository.FindObject(entry);
139         return RepositoryItemToObject(ori);
140     }
141 
LoadObject(const ObjectEntryDescriptor & descriptor)142     Object* LoadObject(const ObjectEntryDescriptor& descriptor) override
143     {
144         const ObjectRepositoryItem* ori = _objectRepository.FindObject(descriptor);
145         return RepositoryItemToObject(ori);
146     }
147 
LoadObjects(const ObjectList & objectList)148     void LoadObjects(const ObjectList& objectList) override
149     {
150         // Find all the required objects
151         auto requiredObjects = GetRequiredObjects(objectList);
152 
153         // Load the required objects
154         LoadObjects(requiredObjects);
155 
156         // Load defaults.
157         LoadDefaultObjects();
158 
159         // Update indices.
160         UpdateSceneryGroupIndexes();
161         ResetTypeToRideEntryIndexMap();
162     }
163 
UnloadObjects(const std::vector<ObjectEntryDescriptor> & entries)164     void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) override
165     {
166         // TODO there are two performance issues here:
167         //        - FindObject for every entry which is a dictionary lookup
168         //        - GetLoadedObjectIndex for every entry which enumerates _loadedList
169 
170         size_t numObjectsUnloaded = 0;
171         for (const auto& descriptor : entries)
172         {
173             const auto* ori = _objectRepository.FindObject(descriptor);
174             if (ori != nullptr)
175             {
176                 auto* loadedObject = ori->LoadedObject.get();
177                 if (loadedObject != nullptr)
178                 {
179                     UnloadObject(loadedObject);
180                     numObjectsUnloaded++;
181                 }
182             }
183         }
184 
185         if (numObjectsUnloaded > 0)
186         {
187             UpdateSceneryGroupIndexes();
188             ResetTypeToRideEntryIndexMap();
189         }
190     }
191 
UnloadAll()192     void UnloadAll() override
193     {
194         for (auto* object : _loadedObjects)
195         {
196             UnloadObject(object);
197         }
198         UpdateSceneryGroupIndexes();
199         ResetTypeToRideEntryIndexMap();
200     }
201 
ResetObjects()202     void ResetObjects() override
203     {
204         for (auto& loadedObject : _loadedObjects)
205         {
206             if (loadedObject != nullptr)
207             {
208                 loadedObject->Unload();
209                 loadedObject->Load();
210             }
211         }
212         UpdateSceneryGroupIndexes();
213         ResetTypeToRideEntryIndexMap();
214     }
215 
GetPackableObjects()216     std::vector<const ObjectRepositoryItem*> GetPackableObjects() override
217     {
218         std::vector<const ObjectRepositoryItem*> objects;
219         size_t numObjects = _objectRepository.GetNumObjects();
220         for (size_t i = 0; i < numObjects; i++)
221         {
222             // TODO: remove ObjectGeneration::DAT check when the NSF is here
223             const ObjectRepositoryItem* item = &_objectRepository.GetObjects()[i];
224             if (item->LoadedObject != nullptr && IsObjectCustom(item) && item->LoadedObject->GetLegacyData() != nullptr
225                 && item->LoadedObject->GetGeneration() == ObjectGeneration::DAT)
226             {
227                 objects.push_back(item);
228             }
229         }
230         return objects;
231     }
232 
LoadDefaultObjects()233     void LoadDefaultObjects() override
234     {
235         // We currently will load new object types here that apply to all
236         // loaded RCT1 and RCT2 save files.
237 
238         // Surfaces
239         LoadObject("rct2.surface.grass");
240         LoadObject("rct2.surface.sand");
241         LoadObject("rct2.surface.dirt");
242         LoadObject("rct2.surface.rock");
243         LoadObject("rct2.surface.martian");
244         LoadObject("rct2.surface.chequerboard");
245         LoadObject("rct2.surface.grassclumps");
246         LoadObject("rct2.surface.ice");
247         LoadObject("rct2.surface.gridred");
248         LoadObject("rct2.surface.gridyellow");
249         LoadObject("rct2.surface.gridpurple");
250         LoadObject("rct2.surface.gridgreen");
251         LoadObject("rct2.surface.sandred");
252         LoadObject("rct2.surface.sandbrown");
253         LoadObject("rct1.aa.surface.roofred");
254         LoadObject("rct1.ll.surface.roofgrey");
255         LoadObject("rct1.ll.surface.rust");
256         LoadObject("rct1.ll.surface.wood");
257 
258         // Edges
259         LoadObject("rct2.edge.rock");
260         LoadObject("rct2.edge.woodred");
261         LoadObject("rct2.edge.woodblack");
262         LoadObject("rct2.edge.ice");
263         LoadObject("rct1.edge.brick");
264         LoadObject("rct1.edge.iron");
265         LoadObject("rct1.aa.edge.grey");
266         LoadObject("rct1.aa.edge.yellow");
267         LoadObject("rct1.aa.edge.red");
268         LoadObject("rct1.ll.edge.purple");
269         LoadObject("rct1.ll.edge.green");
270         LoadObject("rct1.ll.edge.stonebrown");
271         LoadObject("rct1.ll.edge.stonegrey");
272         LoadObject("rct1.ll.edge.skyscrapera");
273         LoadObject("rct1.ll.edge.skyscraperb");
274 
275         // Stations
276         LoadObject("rct2.station.plain");
277         LoadObject("rct2.station.wooden");
278         LoadObject("rct2.station.canvastent");
279         LoadObject("rct2.station.castlegrey");
280         LoadObject("rct2.station.castlebrown");
281         LoadObject("rct2.station.jungle");
282         LoadObject("rct2.station.log");
283         LoadObject("rct2.station.classical");
284         LoadObject("rct2.station.abstract");
285         LoadObject("rct2.station.snow");
286         LoadObject("rct2.station.pagoda");
287         LoadObject("rct2.station.space");
288         LoadObject("openrct2.station.noentrance");
289 
290         // Music
291         auto baseIndex = GetIndexFromTypeEntry(ObjectType::Music, 0);
292         LoadObject(baseIndex + MUSIC_STYLE_DODGEMS_BEAT, "rct2.music.dodgems");
293         LoadObject(baseIndex + MUSIC_STYLE_FAIRGROUND_ORGAN, "rct2.music.fairground");
294         LoadObject(baseIndex + MUSIC_STYLE_ROMAN_FANFARE, "rct2.music.roman");
295         LoadObject(baseIndex + MUSIC_STYLE_ORIENTAL, "rct2.music.oriental");
296         LoadObject(baseIndex + MUSIC_STYLE_MARTIAN, "rct2.music.martian");
297         LoadObject(baseIndex + MUSIC_STYLE_JUNGLE_DRUMS, "rct2.music.jungle");
298         LoadObject(baseIndex + MUSIC_STYLE_EGYPTIAN, "rct2.music.egyptian");
299         LoadObject(baseIndex + MUSIC_STYLE_TOYLAND, "rct2.music.toyland");
300         LoadObject(baseIndex + MUSIC_STYLE_SPACE, "rct2.music.space");
301         LoadObject(baseIndex + MUSIC_STYLE_HORROR, "rct2.music.horror");
302         LoadObject(baseIndex + MUSIC_STYLE_TECHNO, "rct2.music.techno");
303         LoadObject(baseIndex + MUSIC_STYLE_GENTLE, "rct2.music.gentle");
304         LoadObject(baseIndex + MUSIC_STYLE_SUMMER, "rct2.music.summer");
305         LoadObject(baseIndex + MUSIC_STYLE_WATER, "rct2.music.water");
306         LoadObject(baseIndex + MUSIC_STYLE_WILD_WEST, "rct2.music.wildwest");
307         LoadObject(baseIndex + MUSIC_STYLE_JURASSIC, "rct2.music.jurassic");
308         LoadObject(baseIndex + MUSIC_STYLE_ROCK, "rct2.music.rock1");
309         LoadObject(baseIndex + MUSIC_STYLE_RAGTIME, "rct2.music.ragtime");
310         LoadObject(baseIndex + MUSIC_STYLE_FANTASY, "rct2.music.fantasy");
311         LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_2, "rct2.music.rock2");
312         LoadObject(baseIndex + MUSIC_STYLE_ICE, "rct2.music.ice");
313         LoadObject(baseIndex + MUSIC_STYLE_SNOW, "rct2.music.snow");
314         LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_1, "rct2.music.custom1");
315         LoadObject(baseIndex + MUSIC_STYLE_CUSTOM_MUSIC_2, "rct2.music.custom2");
316         LoadObject(baseIndex + MUSIC_STYLE_MEDIEVAL, "rct2.music.medieval");
317         LoadObject(baseIndex + MUSIC_STYLE_URBAN, "rct2.music.urban");
318         LoadObject(baseIndex + MUSIC_STYLE_ORGAN, "rct2.music.organ");
319         LoadObject(baseIndex + MUSIC_STYLE_MECHANICAL, "rct2.music.mechanical");
320         LoadObject(baseIndex + MUSIC_STYLE_MODERN, "rct2.music.modern");
321         LoadObject(baseIndex + MUSIC_STYLE_PIRATES, "rct2.music.pirate");
322         LoadObject(baseIndex + MUSIC_STYLE_ROCK_STYLE_3, "rct2.music.rock3");
323         LoadObject(baseIndex + MUSIC_STYLE_CANDY_STYLE, "rct2.music.candy");
324     }
325 
GetObjectSourceGameString(const ObjectSourceGame sourceGame)326     static rct_string_id GetObjectSourceGameString(const ObjectSourceGame sourceGame)
327     {
328         switch (sourceGame)
329         {
330             case ObjectSourceGame::RCT1:
331                 return STR_SCENARIO_CATEGORY_RCT1;
332             case ObjectSourceGame::AddedAttractions:
333                 return STR_SCENARIO_CATEGORY_RCT1_AA;
334             case ObjectSourceGame::LoopyLandscapes:
335                 return STR_SCENARIO_CATEGORY_RCT1_LL;
336             case ObjectSourceGame::RCT2:
337                 return STR_ROLLERCOASTER_TYCOON_2_DROPDOWN;
338             case ObjectSourceGame::WackyWorlds:
339                 return STR_OBJECT_FILTER_WW;
340             case ObjectSourceGame::TimeTwister:
341                 return STR_OBJECT_FILTER_TT;
342             case ObjectSourceGame::OpenRCT2Official:
343                 return STR_OBJECT_FILTER_OPENRCT2_OFFICIAL;
344             default:
345                 return STR_OBJECT_FILTER_CUSTOM;
346         }
347     }
348 
GetAllRideEntries(uint8_t rideType)349     const std::vector<ObjectEntryIndex>& GetAllRideEntries(uint8_t rideType) override
350     {
351         if (rideType >= RIDE_TYPE_COUNT)
352         {
353             // Return an empty vector
354             return _nullRideTypeEntries;
355         }
356         return _rideTypeToObjectMap[rideType];
357     }
358 
359 private:
LoadObject(int32_t slot,std::string_view identifier)360     Object* LoadObject(int32_t slot, std::string_view identifier)
361     {
362         const ObjectRepositoryItem* ori = _objectRepository.FindObject(identifier);
363         return RepositoryItemToObject(ori, slot);
364     }
365 
RepositoryItemToObject(const ObjectRepositoryItem * ori,std::optional<int32_t> slot={})366     Object* RepositoryItemToObject(const ObjectRepositoryItem* ori, std::optional<int32_t> slot = {})
367     {
368         if (ori == nullptr)
369             return nullptr;
370 
371         Object* loadedObject = ori->LoadedObject.get();
372         if (loadedObject != nullptr)
373             return loadedObject;
374 
375         ObjectType objectType = ori->Type;
376         if (slot)
377         {
378             if (_loadedObjects.size() > static_cast<size_t>(*slot) && _loadedObjects[*slot] != nullptr)
379             {
380                 // Slot already taken
381                 return nullptr;
382             }
383         }
384         else
385         {
386             slot = FindSpareSlot(objectType);
387         }
388         if (slot.has_value())
389         {
390             auto* object = GetOrLoadObject(ori);
391             if (object != nullptr)
392             {
393                 if (_loadedObjects.size() <= static_cast<size_t>(*slot))
394                 {
395                     _loadedObjects.resize(slot.value() + 1);
396                 }
397                 loadedObject = object;
398                 _loadedObjects[slot.value()] = object;
399                 UpdateSceneryGroupIndexes();
400                 ResetTypeToRideEntryIndexMap();
401             }
402         }
403         return loadedObject;
404     }
405 
FindSpareSlot(ObjectType objectType)406     std::optional<int32_t> FindSpareSlot(ObjectType objectType)
407     {
408         size_t firstIndex = GetIndexFromTypeEntry(objectType, 0);
409         size_t endIndex = firstIndex + object_entry_group_counts[EnumValue(objectType)];
410         for (size_t i = firstIndex; i < endIndex; i++)
411         {
412             if (_loadedObjects.size() <= i)
413             {
414                 _loadedObjects.resize(i + 1);
415                 return static_cast<int32_t>(i);
416             }
417             if (_loadedObjects[i] == nullptr)
418             {
419                 return static_cast<int32_t>(i);
420             }
421         }
422         return std::nullopt;
423     }
424 
GetLoadedObjectIndex(const Object * object)425     size_t GetLoadedObjectIndex(const Object* object)
426     {
427         Guard::ArgumentNotNull(object, GUARD_LINE);
428 
429         auto result = std::numeric_limits<size_t>().max();
430         auto it = std::find(_loadedObjects.begin(), _loadedObjects.end(), object);
431         if (it != _loadedObjects.end())
432         {
433             result = std::distance(_loadedObjects.begin(), it);
434         }
435         return result;
436     }
437 
UnloadObject(Object * object)438     void UnloadObject(Object* object)
439     {
440         if (object == nullptr)
441             return;
442 
443         object->Unload();
444 
445         // TODO try to prevent doing a repository search
446         const auto* ori = _objectRepository.FindObject(object->GetDescriptor());
447         if (ori != nullptr)
448         {
449             _objectRepository.UnregisterLoadedObject(ori, object);
450         }
451 
452         // Because it's possible to have the same loaded object for multiple
453         // slots, we have to make sure find and set all of them to nullptr
454         std::replace(_loadedObjects.begin(), _loadedObjects.end(), object, static_cast<Object*>(nullptr));
455     }
456 
UnloadObjectsExcept(const std::vector<Object * > & newLoadedObjects)457     void UnloadObjectsExcept(const std::vector<Object*>& newLoadedObjects)
458     {
459         // Build a hash set for quick checking
460         auto exceptSet = std::unordered_set<Object*>();
461         for (auto& object : newLoadedObjects)
462         {
463             if (object != nullptr)
464             {
465                 exceptSet.insert(object);
466             }
467         }
468 
469         // Unload objects that are not in the hash set
470         size_t totalObjectsLoaded = 0;
471         size_t numObjectsUnloaded = 0;
472         for (auto* object : _loadedObjects)
473         {
474             if (object == nullptr)
475                 continue;
476 
477             totalObjectsLoaded++;
478             if (exceptSet.find(object) == exceptSet.end())
479             {
480                 UnloadObject(object);
481                 numObjectsUnloaded++;
482             }
483         }
484 
485         log_verbose("%u / %u objects unloaded", numObjectsUnloaded, totalObjectsLoaded);
486     }
487 
UpdateSceneryGroupIndexes(Object * object)488     template<typename T> void UpdateSceneryGroupIndexes(Object* object)
489     {
490         auto* sceneryEntry = static_cast<T*>(object->GetLegacyData());
491         sceneryEntry->scenery_tab_id = GetPrimarySceneryGroupEntryIndex(object);
492     }
493 
UpdateSceneryGroupIndexes()494     void UpdateSceneryGroupIndexes()
495     {
496         for (auto* loadedObject : _loadedObjects)
497         {
498             // The list can contain unused slots, skip them.
499             if (loadedObject == nullptr)
500                 continue;
501 
502             switch (loadedObject->GetObjectType())
503             {
504                 case ObjectType::SmallScenery:
505                     UpdateSceneryGroupIndexes<SmallSceneryEntry>(loadedObject);
506                     break;
507                 case ObjectType::LargeScenery:
508                     UpdateSceneryGroupIndexes<LargeSceneryEntry>(loadedObject);
509                     break;
510                 case ObjectType::Walls:
511                     UpdateSceneryGroupIndexes<WallSceneryEntry>(loadedObject);
512                     break;
513                 case ObjectType::Banners:
514                     UpdateSceneryGroupIndexes<BannerSceneryEntry>(loadedObject);
515                     break;
516                 case ObjectType::PathBits:
517                     UpdateSceneryGroupIndexes<PathBitEntry>(loadedObject);
518                     break;
519                 case ObjectType::SceneryGroup:
520                 {
521                     auto sgObject = dynamic_cast<SceneryGroupObject*>(loadedObject);
522                     sgObject->UpdateEntryIndexes();
523                     break;
524                 }
525                 default:
526                     // This switch only handles scenery ObjectTypes.
527                     break;
528             }
529         }
530 
531         // HACK Scenery window will lose its tabs after changing the scenery group indexing
532         //      for now just close it, but it will be better to later tell it to invalidate the tabs
533         window_close_by_class(WC_SCENERY);
534     }
535 
GetPrimarySceneryGroupEntryIndex(Object * loadedObject)536     ObjectEntryIndex GetPrimarySceneryGroupEntryIndex(Object* loadedObject)
537     {
538         auto* sceneryObject = dynamic_cast<SceneryObject*>(loadedObject);
539         const auto& primarySGEntry = sceneryObject->GetPrimarySceneryGroup();
540         Object* sgObject = GetLoadedObject(primarySGEntry);
541 
542         auto entryIndex = OBJECT_ENTRY_INDEX_NULL;
543         if (sgObject != nullptr)
544         {
545             entryIndex = GetLoadedObjectEntryIndex(sgObject);
546         }
547         return entryIndex;
548     }
549 
GetRequiredObjects(const ObjectList & objectList)550     std::vector<const ObjectRepositoryItem*> GetRequiredObjects(const ObjectList& objectList)
551     {
552         std::vector<const ObjectRepositoryItem*> requiredObjects;
553         std::vector<ObjectEntryDescriptor> missingObjects;
554 
555         for (auto objectType = ObjectType::Ride; objectType < ObjectType::Count; objectType++)
556         {
557             auto maxObjectsOfType = static_cast<ObjectEntryIndex>(object_entry_group_counts[EnumValue(objectType)]);
558             for (ObjectEntryIndex i = 0; i < maxObjectsOfType; i++)
559             {
560                 const ObjectRepositoryItem* ori = nullptr;
561                 const auto& entry = objectList.GetObject(objectType, i);
562                 if (entry.HasValue())
563                 {
564                     ori = _objectRepository.FindObject(entry);
565                     if (ori == nullptr && entry.GetType() != ObjectType::ScenarioText)
566                     {
567                         missingObjects.push_back(entry);
568                         ReportMissingObject(entry);
569                     }
570                 }
571                 requiredObjects.push_back(ori);
572             }
573         }
574 
575         if (!missingObjects.empty())
576         {
577             throw ObjectLoadException(std::move(missingObjects));
578         }
579 
580         return requiredObjects;
581     }
582 
ParallelFor(const std::vector<T> & items,TFunc func)583     template<typename T, typename TFunc> static void ParallelFor(const std::vector<T>& items, TFunc func)
584     {
585         auto partitions = std::thread::hardware_concurrency();
586         auto partitionSize = (items.size() + (partitions - 1)) / partitions;
587         std::vector<std::thread> threads;
588         for (size_t n = 0; n < partitions; n++)
589         {
590             auto begin = n * partitionSize;
591             auto end = std::min(items.size(), begin + partitionSize);
592             threads.emplace_back(
593                 [func](size_t pbegin, size_t pend) {
594                     for (size_t i = pbegin; i < pend; i++)
595                     {
596                         func(i);
597                     }
598                 },
599                 begin, end);
600         }
601         for (auto& t : threads)
602         {
603             t.join();
604         }
605     }
606 
LoadObjects(std::vector<const ObjectRepositoryItem * > & requiredObjects)607     void LoadObjects(std::vector<const ObjectRepositoryItem*>& requiredObjects)
608     {
609         std::vector<Object*> objects;
610         std::vector<Object*> newLoadedObjects;
611         std::vector<ObjectEntryDescriptor> badObjects;
612         objects.resize(OBJECT_ENTRY_COUNT);
613         newLoadedObjects.reserve(OBJECT_ENTRY_COUNT);
614 
615         // Read objects
616         std::mutex commonMutex;
617         ParallelFor(requiredObjects, [this, &commonMutex, requiredObjects, &objects, &badObjects, &newLoadedObjects](size_t i) {
618             auto* requiredObject = requiredObjects[i];
619             Object* object = nullptr;
620             if (requiredObject != nullptr)
621             {
622                 auto* loadedObject = requiredObject->LoadedObject.get();
623                 if (loadedObject == nullptr)
624                 {
625                     // Object requires to be loaded, if the object successfully loads it will register it
626                     // as a loaded object otherwise placed into the badObjects list.
627                     auto newObject = _objectRepository.LoadObject(requiredObject);
628                     std::lock_guard<std::mutex> guard(commonMutex);
629                     if (newObject == nullptr)
630                     {
631                         badObjects.push_back(ObjectEntryDescriptor(requiredObject->ObjectEntry));
632                         ReportObjectLoadProblem(&requiredObject->ObjectEntry);
633                     }
634                     else
635                     {
636                         object = newObject.get();
637                         newLoadedObjects.push_back(object);
638                         // Connect the ori to the registered object
639                         _objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject));
640                     }
641                 }
642                 else
643                 {
644                     object = loadedObject;
645                 }
646             }
647             objects[i] = object;
648         });
649 
650         // Load objects
651         for (auto* obj : newLoadedObjects)
652         {
653             obj->Load();
654         }
655 
656         if (!badObjects.empty())
657         {
658             // Unload all the new objects we loaded
659             for (auto* object : newLoadedObjects)
660             {
661                 UnloadObject(object);
662             }
663             throw ObjectLoadException(std::move(badObjects));
664         }
665 
666         // Unload objects which are not in the required list.
667         if (objects.empty())
668         {
669             UnloadAll();
670         }
671         else
672         {
673             UnloadObjectsExcept(objects);
674         }
675 
676         _loadedObjects = std::move(objects);
677 
678         log_verbose("%u / %u new objects loaded", newLoadedObjects.size(), requiredObjects.size());
679     }
680 
GetOrLoadObject(const ObjectRepositoryItem * ori)681     Object* GetOrLoadObject(const ObjectRepositoryItem* ori)
682     {
683         auto* loadedObject = ori->LoadedObject.get();
684         if (loadedObject != nullptr)
685             return loadedObject;
686 
687         // Try to load object
688         auto object = _objectRepository.LoadObject(ori);
689         if (object != nullptr)
690         {
691             loadedObject = object.get();
692 
693             object->Load();
694 
695             // Connect the ori to the registered object
696             _objectRepository.RegisterLoadedObject(ori, std::move(object));
697         }
698 
699         return loadedObject;
700     }
701 
ResetTypeToRideEntryIndexMap()702     void ResetTypeToRideEntryIndexMap()
703     {
704         // Clear all ride objects
705         for (auto& v : _rideTypeToObjectMap)
706         {
707             v.clear();
708         }
709 
710         // Build object lists
711         const auto maxRideObjects = static_cast<size_t>(object_entry_group_counts[EnumValue(ObjectType::Ride)]);
712         for (size_t i = 0; i < maxRideObjects; i++)
713         {
714             auto* rideObject = static_cast<RideObject*>(GetLoadedObject(ObjectType::Ride, i));
715             if (rideObject == nullptr)
716                 continue;
717 
718             const auto* entry = static_cast<rct_ride_entry*>(rideObject->GetLegacyData());
719             if (entry == nullptr)
720                 continue;
721 
722             for (auto rideType : entry->ride_type)
723             {
724                 if (rideType < _rideTypeToObjectMap.size())
725                 {
726                     auto& v = _rideTypeToObjectMap[rideType];
727                     v.push_back(static_cast<ObjectEntryIndex>(i));
728                 }
729             }
730         }
731     }
732 
ReportMissingObject(const ObjectEntryDescriptor & entry)733     static void ReportMissingObject(const ObjectEntryDescriptor& entry)
734     {
735         std::string name(entry.GetName());
736         Console::Error::WriteLine("[%s] Object not found.", name.c_str());
737     }
738 
ReportObjectLoadProblem(const rct_object_entry * entry)739     void ReportObjectLoadProblem(const rct_object_entry* entry)
740     {
741         utf8 objName[DAT_NAME_LENGTH + 1] = { 0 };
742         std::copy_n(entry->name, DAT_NAME_LENGTH, objName);
743         Console::Error::WriteLine("[%s] Object could not be loaded.", objName);
744     }
745 
GetIndexFromTypeEntry(ObjectType objectType,size_t entryIndex)746     static int32_t GetIndexFromTypeEntry(ObjectType objectType, size_t entryIndex)
747     {
748         int32_t result = 0;
749         for (int32_t i = 0; i < EnumValue(objectType); i++)
750         {
751             result += object_entry_group_counts[i];
752         }
753         result += static_cast<int32_t>(entryIndex);
754         return result;
755     }
756 };
757 
CreateObjectManager(IObjectRepository & objectRepository)758 std::unique_ptr<IObjectManager> CreateObjectManager(IObjectRepository& objectRepository)
759 {
760     return std::make_unique<ObjectManager>(objectRepository);
761 }
762 
object_manager_get_loaded_object(const ObjectEntryDescriptor & entry)763 Object* object_manager_get_loaded_object(const ObjectEntryDescriptor& entry)
764 {
765     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
766     Object* loadedObject = objectManager.GetLoadedObject(entry);
767     return loadedObject;
768 }
769 
object_manager_get_loaded_object_entry_index(const Object * loadedObject)770 ObjectEntryIndex object_manager_get_loaded_object_entry_index(const Object* loadedObject)
771 {
772     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
773     auto entryIndex = objectManager.GetLoadedObjectEntryIndex(loadedObject);
774     return entryIndex;
775 }
776 
object_manager_get_loaded_object_entry_index(const ObjectEntryDescriptor & entry)777 ObjectEntryIndex object_manager_get_loaded_object_entry_index(const ObjectEntryDescriptor& entry)
778 {
779     return object_manager_get_loaded_object_entry_index(object_manager_get_loaded_object(entry));
780 }
781 
object_manager_load_object(const rct_object_entry * entry)782 Object* object_manager_load_object(const rct_object_entry* entry)
783 {
784     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
785     Object* loadedObject = objectManager.LoadObject(entry);
786     return loadedObject;
787 }
788 
object_manager_unload_objects(const std::vector<ObjectEntryDescriptor> & entries)789 void object_manager_unload_objects(const std::vector<ObjectEntryDescriptor>& entries)
790 {
791     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
792     objectManager.UnloadObjects(entries);
793 }
794 
object_manager_unload_all_objects()795 void object_manager_unload_all_objects()
796 {
797     auto& objectManager = OpenRCT2::GetContext()->GetObjectManager();
798     objectManager.UnloadAll();
799 }
800 
object_manager_get_source_game_string(const ObjectSourceGame sourceGame)801 rct_string_id object_manager_get_source_game_string(const ObjectSourceGame sourceGame)
802 {
803     return ObjectManager::GetObjectSourceGameString(sourceGame);
804 }
805