• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <algorithm>
17 #include <charconv>
18 #include <cinttypes>
19 #include <functional>
20 
21 #include <3d/ecs/components/render_handle_component.h>
22 #include <base/util/base64_decode.h>
23 #include <base/util/base64_encode.h>
24 #include <base/util/uid_util.h>
25 #include <core/ecs/intf_component_manager.h>
26 #include <core/ecs/intf_entity_manager.h>
27 #include <core/image/intf_image_loader_manager.h>
28 #include <core/intf_engine.h>
29 #include <core/property/intf_property_handle.h>
30 #include <core/property/property_types.h>
31 #include <core/property_tools/property_data.h>
32 #include <ecs_serializer/ecs_property_util.h>
33 #include <render/device/intf_gpu_resource_manager.h>
34 #include <render/device/intf_shader_manager.h>
35 #include <render/intf_render_context.h>
36 #include <render/util/intf_render_util.h>
37 #include <util/json_util.h>
38 #include <util/path_util.h>
39 
40 #include <ecs_serializer/intf_ecs_serializer.h>
41 
42 using namespace BASE_NS;
43 using namespace CORE_NS;
44 using namespace RENDER_NS;
45 using namespace CORE3D_NS;
46 using namespace UTIL_NS;
47 
48 ECS_SERIALIZER_BEGIN_NAMESPACE()
49 
50 class EcsSerializer : public IEcsSerializer {
51 public:
52     explicit EcsSerializer(RENDER_NS::IRenderContext& renderContext);
53     // From IEcsSerializer
54     void SetListener(IListener* listener) override;
55 
56     void SetDefaultSerializers() override;
57     void SetSerializer(const CORE_NS::PropertyTypeDecl& type, IPropertySerializer& serializer) override;
58 
59     bool WriteEntityCollection(const IEntityCollection& ec, CORE_NS::json::standalone_value& jsonOut) const override;
60     bool WriteComponents(
61         const IEntityCollection& ec, CORE_NS::Entity entity, CORE_NS::json::standalone_value& jsonOut) const override;
62     bool WriteComponent(const IEntityCollection& ec, CORE_NS::Entity entity, const CORE_NS::IComponentManager& cm,
63         CORE_NS::IComponentManager::ComponentId id, CORE_NS::json::standalone_value& jsonOut) const override;
64     bool WriteProperty(const IEntityCollection& ec, const CORE_NS::Property& property, uintptr_t offset,
65         CORE_NS::json::standalone_value& jsonOut) const override;
66 
67     bool GatherExternalCollections(const CORE_NS::json::value& jsonIn, BASE_NS::string_view contextUri,
68         BASE_NS::vector<ExternalCollection>& externalCollectionsOut) const override;
69 
70     IoUtil::SerializationResult ReadEntityCollection(
71         IEntityCollection& ec, const CORE_NS::json::value& jsonIn, BASE_NS::string_view contextUri) const override;
72     bool ReadComponents(IEntityCollection& ec, const CORE_NS::json::value& jsonIn) const override;
73     bool ReadComponent(IEntityCollection& ec, const CORE_NS::json::value& jsonIn, CORE_NS::Entity entity,
74         CORE_NS::IComponentManager& component) const override;
75     bool ReadProperty(IEntityCollection& ec, const CORE_NS::json::value& jsonIn, const CORE_NS::Property& property,
76         uintptr_t offset) const override;
77 
78     RENDER_NS::RenderHandleReference LoadImageResource(BASE_NS::string_view uri) const override;
79 
80 protected:
81     void Destroy() override;
82 
83 private:
84     using PropertyToJsonFunc = std::function<bool(
85         const IEntityCollection& ec, const CORE_NS::Property&, uintptr_t, CORE_NS::json::standalone_value&)>;
86     using PropertyFromJsonFunc = std::function<bool(
87         const IEntityCollection& ec, const CORE_NS::json::value&, const CORE_NS::Property&, uintptr_t)>;
88     IPropertySerializer& Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson);
89 
90     // A little wrapper class to create a serializer with functions for writing and readig a value.
91     class SimpleJsonSerializer : public IPropertySerializer {
92     public:
93         bool ToJson(const IEntityCollection& ec, const CORE_NS::Property& property, uintptr_t offset,
94             CORE_NS::json::standalone_value& jsonOut) const override;
95         bool FromJson(const IEntityCollection& ec, const CORE_NS::json::value& jsonIn,
96             const CORE_NS::Property& property, uintptr_t offset) const override;
97 
98         SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson);
99         virtual ~SimpleJsonSerializer() = default;
100 
101     private:
102         const PropertyToJsonFunc PropertyToJson;
103         const PropertyFromJsonFunc PropertyFromJson;
104     };
105     BASE_NS::vector<BASE_NS::unique_ptr<SimpleJsonSerializer>> ownedSerializers_;
106 
107     RENDER_NS::IRenderContext& renderContext_;
108 
109     // Mapping from each property type to a apecific serializer.
110     using SerializerMap = BASE_NS::unordered_map<CORE_NS::PropertyTypeDecl, IPropertySerializer*>;
111     SerializerMap typetoSerializerMap_;
112 
113     IListener* listener_ {};
114 };
115 
116 namespace {
117 template<typename Type>
GetPropertyValue(uintptr_t ptr)118 Type& GetPropertyValue(uintptr_t ptr)
119 {
120     return *reinterpret_cast<Type*>(ptr);
121 }
122 } // namespace
123 
124 // EcsSerializer::SimpleJsonSerializer
ToJson(const IEntityCollection & ec,const Property & property,uintptr_t offset,json::standalone_value & jsonOut) const125 bool EcsSerializer::SimpleJsonSerializer::ToJson(
126     const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
127 {
128     return PropertyToJson(ec, property, offset, jsonOut);
129 }
130 
FromJson(const IEntityCollection & ec,const json::value & jsonIn,const Property & property,uintptr_t offset) const131 bool EcsSerializer::SimpleJsonSerializer::FromJson(
132     const IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
133 {
134     return PropertyFromJson(ec, jsonIn, property, offset);
135 }
136 
SimpleJsonSerializer(PropertyToJsonFunc toJson,PropertyFromJsonFunc fromJson)137 EcsSerializer::SimpleJsonSerializer::SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
138     : PropertyToJson(toJson), PropertyFromJson(fromJson)
139 {}
140 
141 // EcsSerializer
EcsSerializer(RENDER_NS::IRenderContext & renderContext)142 EcsSerializer::EcsSerializer(RENDER_NS::IRenderContext& renderContext) : renderContext_(renderContext) {}
143 
Add(PropertyToJsonFunc toJson,PropertyFromJsonFunc fromJson)144 EcsSerializer::IPropertySerializer& EcsSerializer::Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
145 {
146     auto serializer = new SimpleJsonSerializer(toJson, fromJson);
147     auto ptr = unique_ptr<SimpleJsonSerializer> { serializer };
148     ownedSerializers_.emplace_back(move(ptr));
149     return *ownedSerializers_.back();
150 }
151 
152 namespace {
153 template<class Type>
PropertyToJson(const IEntityCollection &,const Property &,uintptr_t offset,json::standalone_value & jsonOut)154 bool PropertyToJson(
155     const IEntityCollection& /*ec*/, const Property& /*property*/, uintptr_t offset, json::standalone_value& jsonOut)
156 {
157     jsonOut = ToJson(GetPropertyValue<Type>(offset));
158     return true;
159 }
160 
161 template<class Type>
PropertyFromJson(const IEntityCollection &,const json::value & jsonIn,const Property &,uintptr_t offset)162 bool PropertyFromJson(
163     const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset)
164 {
165     auto& value = GetPropertyValue<Type>(offset);
166     return FromJson(jsonIn, value);
167 }
168 
RenderHandleReferenceFromJson(const IEcsSerializer & ecsSerializer,IRenderContext & renderContext,const IEntityCollection & ec,const json::value & jsonIn,RenderHandleReference & handleRefOut)169 bool RenderHandleReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
170     const IEntityCollection& ec, const json::value& jsonIn, RenderHandleReference& handleRefOut)
171 {
172     CORE_UNUSED(ec);
173 
174     if (!jsonIn.is_object()) {
175         return false;
176     }
177 
178     string error;
179     RenderHandleDesc desc;
180 
181     // Casting to enum directly from numeric value is a bit fishy.
182     uint32_t type = 0;
183     if (!SafeGetJsonValue(jsonIn, "type", error, type)) {
184         return false;
185     }
186     desc.type = static_cast<RenderHandleType>(type);
187 
188     if (!SafeGetJsonValue(jsonIn, "name", error, desc.name)) {
189         return false;
190     }
191 
192     SafeGetJsonValue(jsonIn, "additionalName", error, desc.additionalName);
193     auto& renderUtil = renderContext.GetRenderUtil();
194     handleRefOut = renderUtil.GetRenderHandle(desc);
195     if (!handleRefOut && desc.type == RenderHandleType::GPU_IMAGE && !desc.name.empty()) {
196         // Special handling for images: Load the image if it was not already loaded.
197         // Note: assuming that the name is the image uri.
198         handleRefOut = ecsSerializer.LoadImageResource(desc.name);
199     }
200 
201     return true;
202 }
203 
RenderHandleReferenceToJson(IRenderContext & renderContext,const IEntityCollection & ec,const RenderHandleReference & handleRefIn,json::standalone_value & jsonOut)204 bool RenderHandleReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec,
205     const RenderHandleReference& handleRefIn, json::standalone_value& jsonOut)
206 {
207     CORE_UNUSED(ec);
208 
209     auto& renderUtil = renderContext.GetRenderUtil();
210     const auto desc = renderUtil.GetRenderHandleDesc(handleRefIn);
211 
212     jsonOut = json::standalone_value::object {};
213     jsonOut["type"] = static_cast<unsigned int>(desc.type);
214     jsonOut["name"] = string(desc.name);
215     if (!desc.additionalName.empty()) {
216         jsonOut["additionalName"] = string(desc.additionalName);
217     }
218 
219     return true;
220 }
221 
EntityFromJson(const IEntityCollection & ec,const json::value & jsonIn,Entity & entityOut)222 bool EntityFromJson(const IEntityCollection& ec, const json::value& jsonIn, Entity& entityOut)
223 {
224     // Figure out to what entity id a piece of json refers to and handle error cases.
225     Entity entity {};
226     if (jsonIn.is_unsigned_int()) {
227         const auto entityReference = static_cast<uint32_t>(jsonIn.unsigned_);
228         entity = ec.GetEntity(entityReference);
229         if (entity == Entity {}) {
230             CORE_LOG_W("Component entity not found for index: %u", entityReference);
231             return false;
232         }
233     } else if (jsonIn.is_string()) {
234         string entityReferenceString;
235         if (FromJson(jsonIn, entityReferenceString)) {
236             entity = ec.GetEntity(entityReferenceString);
237         }
238         if (entity == Entity {}) {
239             CORE_LOG_W("Component entity not found for id: '%s'", entityReferenceString.c_str());
240             return false;
241         }
242     } else if (jsonIn.is_object()) {
243         // Empty object means "null ref".
244         if (jsonIn.empty()) {
245             entityOut = Entity {};
246             return true;
247         }
248 
249         // Figure out the correct collection (Recursive).
250         const IEntityCollection* collection { nullptr };
251         const auto* collectionJson = jsonIn.find("collection");
252         if (collectionJson) {
253             if (collectionJson->is_string()) {
254                 string collectionName;
255                 if (FromJson(*collectionJson, collectionName)) {
256                     if (auto index = ec.GetSubCollectionIndex(collectionName); index >= 0) {
257                         collection = ec.GetSubCollection(static_cast<size_t>(index));
258                     }
259                 }
260                 if (!collection) {
261                     CORE_LOG_W("Collection not found: '%s'", collectionName.c_str());
262                     return false;
263                 }
264             } else if (collectionJson->is_unsigned_int()) {
265                 const auto collectionIndex = collectionJson->unsigned_;
266                 if (collectionIndex < ec.GetSubCollectionCount()) {
267                     collection = ec.GetSubCollection(collectionIndex);
268                 } else {
269                     CORE_LOG_W("Collection not found: %" PRIu64, collectionIndex);
270                     return false;
271                 }
272             } else {
273                 CORE_LOG_W("Invalid collection for a component.");
274                 return false;
275             }
276 
277             if (collection) {
278                 const auto* entityJson = jsonIn.find("entity");
279                 if (entityJson) {
280                     return EntityFromJson(*collection, *entityJson, entityOut);
281                 }
282             }
283             return false;
284         }
285     } else {
286         CORE_LOG_W("Component entity property must be an index to the entities array, an string id, or an object");
287         return false;
288     }
289 
290     entityOut = entity;
291     return true;
292 }
293 
EntityToJson(const IEntityCollection & ec,const Entity & entityIn,json::standalone_value & jsonOut)294 bool EntityToJson(const IEntityCollection& ec, const Entity& entityIn, json::standalone_value& jsonOut)
295 {
296     // Write entity index/name if part of this collection.
297     const auto entityCount = ec.GetEntityCount();
298     for (size_t i = 0; i < entityCount; ++i) {
299         if (entityIn == ec.GetEntity(i)) {
300             auto id = ec.GetId(entityIn);
301             if (!id.empty()) {
302                 jsonOut = string(id);
303             } else {
304                 jsonOut = i;
305             }
306             return true;
307         }
308     }
309 
310     // Otherwise check sub-collections recursively.
311     const auto collectionCount = ec.GetSubCollectionCount();
312     size_t collectionId = 0;
313     for (size_t i = 0; i < collectionCount; ++i) {
314         auto* collection = ec.GetSubCollection(i);
315         BASE_ASSERT(collection);
316         // NOTE: Skipping over destroyed collections (same needs to be done when writing the actual collections).
317         if (collection->IsMarkedDestroyed()) {
318             continue;
319         }
320         json::standalone_value entityJson;
321         if (EntityToJson(*collection, entityIn, entityJson)) {
322             jsonOut = json::standalone_value::object {};
323             jsonOut[string_view { "collection" }] = collectionId;
324             jsonOut[string_view { "entity" }] = move(entityJson);
325             return true;
326         }
327         collectionId++;
328     }
329 
330     return false;
331 }
332 
EntityReferenceToJson(IRenderContext & renderContext,const IEntityCollection & ec,const EntityReference & entityIn,json::standalone_value & jsonOut)333 bool EntityReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const EntityReference& entityIn,
334     json::standalone_value& jsonOut)
335 {
336     if (EntityToJson(ec, entityIn, jsonOut)) {
337         return true;
338     }
339 
340     // Write render handle reference as render handle desc.
341     auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
342     if (rhm) {
343         if (auto handle = rhm->Read(entityIn); handle) {
344             json::standalone_value renderHandleJson = json::standalone_value::object {};
345             if (RenderHandleReferenceToJson(renderContext, ec, handle->reference, renderHandleJson["renderHandle"])) {
346                 jsonOut = move(renderHandleJson);
347                 return true;
348             }
349         }
350     }
351 
352     return false;
353 }
354 
EntityReferenceFromJson(const IEcsSerializer & ecsSerializer,IRenderContext & renderContext,const IEntityCollection & ec,const json::value & jsonIn,EntityReference & entityOut)355 bool EntityReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
356     const IEntityCollection& ec, const json::value& jsonIn, EntityReference& entityOut)
357 {
358     // A generic handler for any uri to a render handle.
359     const auto* renderHandleJson = jsonIn.find("renderHandle");
360     if (renderHandleJson) {
361         // Try to find a named render handle by desc.
362         RenderHandleReference renderHandle;
363         if (RenderHandleReferenceFromJson(ecsSerializer, renderContext, ec, *renderHandleJson, renderHandle)) {
364             auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
365             if (rhm && renderHandle) {
366                 entityOut = GetOrCreateEntityReference(ec.GetEcs().GetEntityManager(), *rhm, renderHandle);
367                 return true;
368             }
369         }
370     }
371 
372     Entity temp;
373     if (!EntityFromJson(ec, jsonIn, temp)) {
374         return false;
375     }
376     entityOut = ec.GetEcs().GetEntityManager().GetReferenceCounted(temp);
377     return true;
378 }
379 } // namespace
380 
SetDefaultSerializers()381 void EcsSerializer::SetDefaultSerializers()
382 {
383     // Basic types (for types that nlohman knows how to serialize).
384     SetSerializer(PropertyType::FLOAT_T, Add(PropertyToJson<float>, PropertyFromJson<float>));
385     SetSerializer(PropertyType::DOUBLE_T, Add(PropertyToJson<double>, PropertyFromJson<double>));
386 
387     SetSerializer(PropertyType::UINT8_T, Add(PropertyToJson<uint8_t>, PropertyFromJson<uint8_t>));
388     SetSerializer(PropertyType::UINT16_T, Add(PropertyToJson<uint16_t>, PropertyFromJson<uint16_t>));
389     SetSerializer(PropertyType::UINT32_T, Add(PropertyToJson<uint32_t>, PropertyFromJson<uint32_t>));
390     SetSerializer(PropertyType::UINT64_T, Add(PropertyToJson<uint64_t>, PropertyFromJson<uint64_t>));
391 
392     SetSerializer(PropertyType::INT8_T, Add(PropertyToJson<int8_t>, PropertyFromJson<int8_t>));
393     SetSerializer(PropertyType::INT16_T, Add(PropertyToJson<int16_t>, PropertyFromJson<int16_t>));
394     SetSerializer(PropertyType::INT32_T, Add(PropertyToJson<int32_t>, PropertyFromJson<int32_t>));
395     SetSerializer(PropertyType::INT64_T, Add(PropertyToJson<int64_t>, PropertyFromJson<int64_t>));
396 
397     SetSerializer(PropertyType::VEC2_T, Add(PropertyToJson<Math::Vec2>, PropertyFromJson<Math::Vec2>));
398     SetSerializer(PropertyType::VEC3_T, Add(PropertyToJson<Math::Vec3>, PropertyFromJson<Math::Vec3>));
399     SetSerializer(PropertyType::VEC4_T, Add(PropertyToJson<Math::Vec4>, PropertyFromJson<Math::Vec4>));
400 
401     SetSerializer(PropertyType::UVEC2_T, Add(PropertyToJson<Math::UVec2>, PropertyFromJson<Math::UVec2>));
402     SetSerializer(PropertyType::UVEC3_T, Add(PropertyToJson<Math::UVec3>, PropertyFromJson<Math::UVec3>));
403     SetSerializer(PropertyType::UVEC4_T, Add(PropertyToJson<Math::UVec4>, PropertyFromJson<Math::UVec4>));
404 
405     SetSerializer(PropertyType::BOOL_T, Add(PropertyToJson<bool>, PropertyFromJson<bool>));
406     SetSerializer(PropertyType::QUAT_T, Add(PropertyToJson<Math::Quat>, PropertyFromJson<Math::Quat>));
407     SetSerializer(PropertyType::UID_T, Add(PropertyToJson<Uid>, PropertyFromJson<Uid>));
408 
409     SetSerializer(PropertyType::STRING_T, Add(PropertyToJson<string>, PropertyFromJson<string>));
410 
411     // Others
412     SetSerializer(PropertyType::CHAR_ARRAY_T,
413         Add(
414             [](const IEntityCollection& /*ec*/, const Property& property, uintptr_t offset,
415                 json::standalone_value& jsonOut) {
416                 const auto* value = &GetPropertyValue<char>(offset);
417                 // NOTE: a hacky way to calculate cstring size.
418                 string_view view(value);
419                 const size_t size = view.size() < property.size ? view.size() : property.size;
420                 jsonOut = ToJson(string(value, size));
421                 return true;
422             },
423             [](const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& property, uintptr_t offset) {
424                 string value;
425                 if (FromJson(jsonIn, value)) {
426                     char* charArray = &GetPropertyValue<char>(offset);
427                     charArray[value.copy(charArray, property.size - 1)] = '\0';
428                     return true;
429                 }
430                 return false;
431             }));
432 
433     SetSerializer(PropertyType::ENTITY_T,
434         Add(
435             [](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
436                 json::standalone_value& jsonOut) {
437                 return EntityToJson(ec, GetPropertyValue<const Entity>(offset), jsonOut);
438             },
439             [](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset) {
440                 return EntityFromJson(ec, jsonIn, GetPropertyValue<Entity>(offset));
441             }));
442 
443     SetSerializer(PropertyType::ENTITY_REFERENCE_T,
444         Add(
445             [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
446                 json::standalone_value& jsonOut) {
447                 return EntityReferenceToJson(
448                     renderContext_, ec, GetPropertyValue<const EntityReference>(offset), jsonOut);
449             },
450             [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
451                 uintptr_t offset) {
452                 return EntityReferenceFromJson(
453                     *this, renderContext_, ec, jsonIn, GetPropertyValue<EntityReference>(offset));
454             }));
455 
456     SetSerializer(PROPERTYTYPE(RenderHandleReference),
457         Add(
458             [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
459                 json::standalone_value& jsonOut) {
460                 return RenderHandleReferenceToJson(
461                     renderContext_, ec, GetPropertyValue<const RenderHandleReference>(offset), jsonOut);
462             },
463             [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
464                 uintptr_t offset) {
465                 return RenderHandleReferenceFromJson(
466                     *this, renderContext_, ec, jsonIn, GetPropertyValue<RenderHandleReference>(offset));
467             }));
468 }
469 
SetListener(IListener * listener)470 void EcsSerializer::SetListener(IListener* listener)
471 {
472     listener_ = listener;
473 }
474 
SetSerializer(const PropertyTypeDecl & type,IPropertySerializer & serializer)475 void EcsSerializer::SetSerializer(const PropertyTypeDecl& type, IPropertySerializer& serializer)
476 {
477     typetoSerializerMap_[type] = &serializer;
478 }
479 
WriteEntityCollection(const IEntityCollection & ec,json::standalone_value & jsonOut) const480 bool EcsSerializer::WriteEntityCollection(const IEntityCollection& ec, json::standalone_value& jsonOut) const
481 {
482     // NOTE: We make sure collections and entities are written in the output before entity-components to make it
483     // possible to parse the file in one go.
484 
485     jsonOut = json::standalone_value::object();
486 
487     string type = ec.GetType();
488     if (type.empty()) {
489         type = "entitycollection";
490     }
491     const IoUtil::CompatibilityInfo info { VERSION_MAJOR, VERSION_MINOR, type };
492     if (!IoUtil::WriteCompatibilityInfo(jsonOut, info)) {
493         return false;
494     }
495 
496     if (string rootSrc = ec.GetSrc(); !rootSrc.empty()) {
497         jsonOut["src"] = move(rootSrc);
498     }
499 
500     // Write external collections instanced in this collection.
501     const auto collectionCount = ec.GetSubCollectionCount();
502     if (collectionCount > 0) {
503         json::standalone_value collectionsJson = json::standalone_value::array();
504         collectionsJson.array_.reserve(collectionCount);
505 
506         for (size_t i = 0; i < collectionCount; ++i) {
507             json::standalone_value collectionJson = json::standalone_value::object();
508             auto* collection = ec.GetSubCollection(i);
509 
510             if (!collection || collection->IsMarkedDestroyed()) {
511                 // NOTE: Skipping over destroyed collections (same needs to be done when writing entity references).
512                 continue;
513             }
514 
515             if (string src = collection->GetSrc(); !src.empty()) {
516                 collectionJson["src"] = move(src);
517             }
518             if (string id = collection->GetUri(); !id.empty()) {
519                 collectionJson["id"] = move(id);
520             }
521 
522             collectionsJson.array_.emplace_back(move(collectionJson));
523         }
524 
525         if (!collectionsJson.array_.empty()) {
526             jsonOut["collections"] = move(collectionsJson);
527         }
528     }
529 
530     // Write bare entities without components.
531     const auto entityCount = ec.GetEntityCount();
532     if (entityCount > 0) {
533         auto& entitiesJson = (jsonOut["entities"] = json::standalone_value::array());
534         entitiesJson.array_.reserve(entityCount);
535         for (size_t i = 0; i < entityCount; ++i) {
536             json::standalone_value entityJson = json::standalone_value::object();
537 
538             // Write id if one is defined.
539             auto entity = ec.GetEntity(i);
540             const auto& id = ec.GetId(entity);
541             if (!id.empty()) {
542                 entityJson["id"] = string(id);
543             }
544 
545             entitiesJson.array_.emplace_back(move(entityJson));
546         }
547     }
548 
549     vector<EntityReference> allEntities;
550     ec.GetEntitiesRecursive(false, allEntities);
551     if (allEntities.size() > 0) {
552         auto& entityComponentsJson = (jsonOut["entity-components"] = json::standalone_value::array());
553         for (Entity entity : allEntities) {
554             json::standalone_value componentJson = json::standalone_value::object();
555             if (WriteComponents(ec, entity, componentJson["components"])) {
556                 json::standalone_value entityRefJson;
557                 if (EntityToJson(ec, entity, entityRefJson)) {
558                     componentJson["entity"] = move(entityRefJson);
559                 }
560                 entityComponentsJson.array_.emplace_back(move(componentJson));
561             }
562         }
563     }
564 
565     // NOTE: Always returns true even if parts of the writing failed.
566     return true;
567 }
568 
WriteComponents(const IEntityCollection & ec,Entity entity,json::standalone_value & jsonOut) const569 bool EcsSerializer::WriteComponents(const IEntityCollection& ec, Entity entity, json::standalone_value& jsonOut) const
570 {
571     // Write all entity components to json (Sorting by uid to keep the order more stable).
572     vector<string> managerUids;
573     jsonOut = json::standalone_value::object();
574     auto cms = ec.GetEcs().GetComponentManagers();
575     for (auto cm : cms) {
576         const auto componentId = cm->GetComponentId(entity);
577         if (componentId != IComponentManager::INVALID_COMPONENT_ID) {
578             auto uidString = to_string(cm->GetUid());
579             managerUids.emplace_back(string(uidString));
580         }
581     }
582     std::sort(managerUids.begin(), managerUids.end());
583 
584     for (const auto& uidString : managerUids) {
585         const auto* cm = ec.GetEcs().GetComponentManager(StringToUid(uidString));
586         const auto componentId = cm->GetComponentId(entity);
587 
588         json::standalone_value componentJson = json::standalone_value::object();
589         if (WriteComponent(ec, entity, *cm, componentId, componentJson)) {
590             jsonOut[uidString] = move(componentJson);
591         }
592     }
593 
594     return (!jsonOut.empty());
595 }
596 
WriteComponent(const IEntityCollection & ec,Entity entity,const IComponentManager & cm,IComponentManager::ComponentId id,json::standalone_value & jsonOut) const597 bool EcsSerializer::WriteComponent(const IEntityCollection& ec, Entity entity, const IComponentManager& cm,
598     IComponentManager::ComponentId id, json::standalone_value& jsonOut) const
599 {
600     // Write all properties to json.
601     json::standalone_value propertiesJson = json::standalone_value::object();
602     const auto* props = ec.GetSerializedProperties(entity, cm.GetUid());
603     if (props) {
604         const auto* propertyHandle = cm.GetData(id);
605         if (!propertyHandle) {
606             return false;
607         }
608         for (const auto& propertyPath : *props) {
609             const IPropertyHandle* handle = propertyHandle;
610 
611             PropertyData propertyData;
612             PropertyData::PropertyOffset propertyOffset;
613 
614             // Check if this is property container.
615             string path, name;
616             auto containerHandle = ResolveContainerProperty(*handle, propertyPath, path, name);
617             if (containerHandle) {
618                 propertyOffset = propertyData.RLock(*containerHandle, name);
619             } else {
620                 propertyOffset = propertyData.RLock(*propertyHandle, propertyPath);
621             }
622 
623             if (propertyOffset) {
624                 if ((propertyOffset.property->flags & static_cast<uint32_t>(PropertyFlags::NO_SERIALIZE)) != 0) {
625                     continue;
626                 }
627 
628                 json::standalone_value propertyJson;
629                 if (WriteProperty(ec, *propertyOffset.property, propertyOffset.offset, propertyJson)) {
630                     if (!propertyJson.is_null()) {
631                         propertiesJson[propertyPath] = move(propertyJson);
632                     }
633                 }
634             }
635         }
636 
637         // NOTE: optional name to make reading files easier.
638         jsonOut["name"] = string(cm.GetName());
639 
640         if (!propertiesJson.empty()) {
641             jsonOut["properties"] = move(propertiesJson);
642         }
643 
644         // NOTE: Maybe return false if any property write fails?
645         return true;
646     }
647     return false;
648 }
649 
WriteProperty(const IEntityCollection & ec,const Property & property,uintptr_t offset,json::standalone_value & jsonOut) const650 bool EcsSerializer::WriteProperty(
651     const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
652 {
653     auto serializer = typetoSerializerMap_.find(property.type);
654     if (serializer != typetoSerializerMap_.end()) {
655         return serializer->second->ToJson(ec, property, offset, jsonOut);
656     } else if (!property.metaData.enumMetaData.empty()) {
657         // Enum type property.
658         switch (property.size) {
659             case sizeof(uint8_t):
660                 jsonOut = GetPropertyValue<uint8_t>(offset);
661                 return true;
662             case sizeof(uint16_t):
663                 jsonOut = GetPropertyValue<uint16_t>(offset);
664                 return true;
665             case sizeof(uint32_t):
666                 jsonOut = GetPropertyValue<uint32_t>(offset);
667                 return true;
668             case sizeof(uint64_t):
669                 jsonOut = GetPropertyValue<uint64_t>(offset);
670                 return true;
671             default:
672                 return false;
673         }
674     } else if (property.metaData.containerMethods) {
675         const auto& container = *property.metaData.containerMethods;
676 
677         // Container type property.
678         if (property.type.isArray) {
679             // Special handling for byte arrays. Save as a base64 encoded string.
680             if (property.type == PropertyType::UINT8_ARRAY_T) {
681                 array_view<const uint8_t> bytes { (const uint8_t*)offset, property.size };
682                 jsonOut = BASE_NS::Base64Encode(bytes);
683                 return true;
684             }
685 
686             // C style array.
687             json::standalone_value array = json::standalone_value::array();
688             array.array_.reserve(property.count);
689             for (size_t i = 0; i < property.count; i++) {
690                 uintptr_t ptr = offset + i * container.property.size;
691                 // return false if any recurseive call fails?
692                 json::standalone_value elementJson;
693                 WriteProperty(ec, container.property, ptr, elementJson);
694                 array.array_.push_back(move(elementJson));
695             }
696             jsonOut = move(array);
697             return true;
698         } else {
699             // This is a "non trivial container".
700             const auto count = container.size(offset);
701 
702             // Special handling for byte arrays. Save as a base64 encoded string.
703             // NOTE: Only specifically vector so we can assume that the memory usage is linear.
704             if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
705                 if (count == 0) {
706                     jsonOut = "";
707                 } else {
708                     auto data = container.get(offset, 0);
709                     array_view<const uint8_t> bytes { reinterpret_cast<const uint8_t*>(data), count };
710                     jsonOut = BASE_NS::Base64Encode(bytes);
711                 }
712                 return true;
713             }
714 
715             json::standalone_value array = json::standalone_value::array();
716             array.array_.reserve(count);
717 
718             for (size_t i = 0; i < count; i++) {
719                 uintptr_t ptr = container.get(offset, i);
720                 // return false if any recurseive call fails?
721                 json::standalone_value elementJson;
722                 WriteProperty(ec, container.property, ptr, elementJson);
723                 array.array_.push_back(move(elementJson));
724             }
725             jsonOut = move(array);
726             return true;
727         }
728     } else if (!property.metaData.memberProperties.empty()) {
729         // Struct type property (ie. has sub properties).
730         json::standalone_value object = json::standalone_value::object();
731         for (const auto& subProperty : property.metaData.memberProperties) {
732             json::standalone_value subPropertyJson;
733             if (WriteProperty(ec, subProperty, offset + subProperty.offset, subPropertyJson)) {
734                 object[subProperty.name] = move(subPropertyJson);
735             }
736         }
737         // return false if any recurseive call fails?
738         jsonOut = move(object);
739         return true;
740     } else {
741         CORE_LOG_V("Ecs serializer not found for '%s'", string(property.type.name).c_str());
742     }
743     return false;
744 }
745 
GatherExternalCollections(const json::value & jsonIn,string_view contextUri,vector<ExternalCollection> & externalCollectionsOut) const746 bool EcsSerializer::GatherExternalCollections(
747     const json::value& jsonIn, string_view contextUri, vector<ExternalCollection>& externalCollectionsOut) const
748 {
749     const auto* srcJson = jsonIn.find("src");
750     string srcUri;
751     if (srcJson && FromJson(*srcJson, srcUri)) {
752 #ifdef VERBOSE_LOGGING
753         CORE_LOG_D("External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
754 #endif
755         externalCollectionsOut.emplace_back(ExternalCollection { srcUri, string { contextUri } });
756     }
757 
758     const auto* collectionsJson = jsonIn.find("collections");
759     if (collectionsJson && collectionsJson->is_array()) {
760         for (const auto& collectionJson : collectionsJson->array_) {
761             if (collectionJson.is_object()) {
762                 const auto* collectionSrcJson = collectionJson.find("src");
763                 if (collectionSrcJson && collectionSrcJson->is_string()) {
764                     string collectionSrcUri;
765                     FromJson(*collectionSrcJson, collectionSrcUri);
766                     externalCollectionsOut.emplace_back(ExternalCollection { collectionSrcUri, string { contextUri } });
767                 }
768             }
769         }
770     }
771     return true;
772 }
773 
ReadEntityCollection(IEntityCollection & ec,const json::value & jsonIn,string_view contextUri) const774 IoUtil::SerializationResult EcsSerializer::ReadEntityCollection(
775     IEntityCollection& ec, const json::value& jsonIn, string_view contextUri) const
776 {
777     // Move version check to be separately so it can be done before gathering the dependencies.
778     // NOTE: Only comparing the major version.
779     const auto minor = IoUtil::CompatibilityRange::IGNORE_VERSION;
780     // Type name was changed to be in line with engine naming. Allow the old type name for a while.
781     const IoUtil::CompatibilityRange validVersions[] {
782         { VERSION_MAJOR, VERSION_MAJOR, minor, minor, ec.GetType() },
783         { VERSION_MAJOR, VERSION_MAJOR, minor, minor, "entity_collection" },
784     };
785 
786     auto result = IoUtil::CheckCompatibility(jsonIn, validVersions);
787     if (result.compatibilityInfo.type == "entity_collection") {
788         CORE_LOG_W("Deprecated compatibility info found \"%s\": '%s'", result.compatibilityInfo.type.c_str(),
789             string(contextUri).c_str());
790     }
791 
792     const auto* srcJson = jsonIn.find("src");
793     string srcUri;
794     if (srcJson && FromJson(*srcJson, srcUri)) {
795 #ifdef VERBOSE_LOGGING
796         CORE_LOG_D("External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
797 #endif
798         auto* externalCollection = listener_->GetExternalCollection(ec.GetEcs(), srcUri, contextUri);
799         if (externalCollection) {
800             ec.CopyContents(*externalCollection);
801         }
802     }
803 
804     const auto* collectionsJson = jsonIn.find("collections");
805     if (collectionsJson && collectionsJson->is_array()) {
806         for (const auto& collectionJson : collectionsJson->array_) {
807             if (collectionJson.is_object()) {
808                 string id;
809                 const auto* idJson = collectionJson.find("id");
810                 if (idJson) {
811                     FromJson(*idJson, id);
812                 }
813 
814                 const auto* collectionSrcJson = collectionJson.find("src");
815                 if (collectionSrcJson && collectionSrcJson->is_string()) {
816                     string collectionSrcUri;
817                     FromJson(*collectionSrcJson, collectionSrcUri);
818 
819                     // Instantiate the collection pointed by src.
820 #ifdef VERBOSE_LOGGING
821                     CORE_LOG_D("External Collection: uri='%s' context='%s'", collectionSrcUri.c_str(),
822                         string(contextUri).c_str());
823 #endif
824                     auto* externalCollection =
825                         listener_->GetExternalCollection(ec.GetEcs(), collectionSrcUri, contextUri);
826                     if (externalCollection) {
827                         ec.AddSubCollectionClone(*externalCollection, id).SetSrc(collectionSrcUri);
828                         ;
829                     } else {
830                         CORE_LOG_E("Loading collection failed: '%s'", collectionSrcUri.c_str());
831                         // Just adding an empty collection as a placeholder.
832                         ec.AddSubCollection(id, collectionSrcUri).SetSrc(collectionSrcUri);
833                     }
834                 } else {
835                     ec.AddSubCollection(id, {});
836                 }
837             }
838         }
839     }
840 
841     const auto* entitiesJson = jsonIn.find("entities");
842     if (entitiesJson && entitiesJson->is_array()) {
843         auto& em = ec.GetEcs().GetEntityManager();
844 
845         // First create all entities so they can be referenced by components.
846         for (const auto& entityJson : entitiesJson->array_) {
847             // Create a new entity.
848             EntityReference entity = em.CreateReferenceCounted();
849             ec.AddEntity(entity);
850 
851             // Add with an id if one is defined.
852             const auto* idJson = entityJson.find("id");
853             string id;
854             if (idJson && FromJson(*idJson, id)) {
855                 ec.SetId(id, entity);
856             }
857         }
858     }
859 
860     const auto* ecJson = jsonIn.find("entity-components");
861     if (ecJson && ecJson->is_array()) {
862         // Then load entity contents (i.e. components).
863         for (size_t i = 0; i < ecJson->array_.size(); ++i) {
864             ReadComponents(ec, ecJson->array_.at(i));
865         }
866     }
867 
868     if (!ec.IsActive()) {
869         // If the ec is not active also make the newly loaded entities not active.
870         ec.SetActive(false);
871     }
872 
873     // NOTE: Always returns success, even if parts of the load failed.
874     return result;
875 }
876 
ReadComponents(IEntityCollection & ec,const json::value & jsonIn) const877 bool EcsSerializer::ReadComponents(IEntityCollection& ec, const json::value& jsonIn) const
878 {
879     // Figure out to which entity these components belong to.
880     const auto* entityJson = jsonIn.find("entity");
881     if (!entityJson) {
882         CORE_LOG_W("No entity defined for a component.");
883         return false;
884     }
885     Entity entity {};
886     if (!EntityFromJson(ec, *entityJson, entity)) {
887         return false;
888     }
889 
890     auto& ecs = ec.GetEcs();
891     const auto* componentsJson = jsonIn.find("components");
892     if (componentsJson) {
893         // Read all entity components from json.
894         for (auto& component : componentsJson->object_) {
895             auto& key = component.key;
896             const auto componentUid = StringToUid(key);
897 
898             auto& componentJson = component.value;
899             auto* cm = ecs.GetComponentManager(componentUid);
900             if (cm) {
901                 ReadComponent(ec, componentJson, entity, *cm);
902             } else {
903                 // Maybe we should try to find a matching component by name as a fallback
904                 CORE_LOG_W("Unrecognized component found: '%s'", string(key).c_str());
905             }
906         }
907     }
908 
909     return true;
910 }
911 
912 namespace {
913 // Helper function that makes sure that any dynamic arrays referenced in a property path are large enough to contain the
914 // referenced indices.
EnsureDynamicArraySize(IPropertyHandle * propertyHandle,string_view propertyPath)915 void EnsureDynamicArraySize(IPropertyHandle* propertyHandle, string_view propertyPath)
916 {
917     const auto separatorPosition = propertyPath.find('[');
918     if (separatorPosition == BASE_NS::string::npos) {
919         return;
920     }
921     const auto separatorEndPosition = propertyPath.find(']', separatorPosition);
922     if (separatorEndPosition == BASE_NS::string::npos) {
923         return;
924     }
925 
926     string arrayPath;
927     string arrayIndex;
928     arrayPath = propertyPath.substr(0, separatorPosition);
929     arrayIndex = propertyPath.substr(separatorPosition + 1, separatorEndPosition - separatorPosition - 1);
930 
931     char* end = nullptr;
932     const unsigned long index = std::strtoul(arrayIndex.c_str(), &end, 10); // 10: base
933     // Check that conversion stopped at the end of the string.
934     if (!end || *end != '\0') {
935         return;
936     }
937 
938     PropertyData propertyData;
939     PropertyData::PropertyOffset propertyOffset = propertyData.WLock(*propertyHandle, arrayPath);
940     if (propertyOffset) {
941         auto* containerMethods = propertyOffset.property->metaData.containerMethods;
942         if (containerMethods && containerMethods->resize) {
943             if (containerMethods->size(propertyOffset.offset) <= index) {
944                 containerMethods->resize(propertyOffset.offset, index + 1);
945             }
946         }
947     }
948 
949     const auto restOfThePath = propertyPath.substr(separatorEndPosition);
950     EnsureDynamicArraySize(&propertyData, restOfThePath);
951 }
952 } // namespace
953 
ReadComponent(IEntityCollection & ec,const json::value & jsonIn,Entity entity,IComponentManager & component) const954 bool EcsSerializer::ReadComponent(
955     IEntityCollection& ec, const json::value& jsonIn, Entity entity, IComponentManager& component) const
956 {
957     // Create the component if it does not exist yet.
958     auto componentId = component.GetComponentId(entity);
959     if (componentId == IComponentManager::INVALID_COMPONENT_ID) {
960         component.Create(entity);
961         componentId = component.GetComponentId(entity);
962     }
963 
964     ec.MarkComponentSerialized(entity, component.GetUid(), true);
965 
966     const auto* propertiesJson = jsonIn.find("properties");
967     if (!propertiesJson || propertiesJson->type != json::type::object) {
968         // No properties.
969         return true;
970     }
971 
972     auto* propertyHandle = component.GetData(componentId);
973     if (!propertyHandle) {
974         return false;
975     }
976 
977     for (auto& propertyJson : propertiesJson->object_) {
978         const auto& propertyPath = propertyJson.key;
979         const auto& propertyValueJson = propertyJson.value;
980         auto pathView = string_view(propertyPath.data(), propertyPath.size());
981 
982         // Find the property using the propertyName
983         {
984             const IPropertyHandle* handle = propertyHandle;
985             PropertyData propertyData;
986             PropertyData::PropertyOffset propertyOffset;
987 
988             // Check if this is property container.
989             string path, name;
990             auto containerHandle = ResolveContainerProperty(*handle, string(pathView), path, name);
991             if (containerHandle) {
992                 propertyOffset = propertyData.WLock(*containerHandle, name);
993             } else {
994                 // We can only ask for the property if we first make sure it exists (we may be referencing a dynamic
995                 // array).
996                 EnsureDynamicArraySize(propertyHandle, pathView);
997 
998                 propertyOffset = propertyData.WLock(*propertyHandle, pathView);
999             }
1000 
1001             if (propertyOffset) {
1002                 if (ReadProperty(ec, propertyValueJson, *propertyOffset.property, propertyOffset.offset)) {
1003                     // Mark this property value as serialized (instead of being a cloned from a prototype entity).
1004                     ec.MarkPropertySerialized(entity, component.GetUid(), pathView, true);
1005                 } else {
1006                     CORE_LOG_W("Unrecognized property: Component: '%s' Property: '%s'", component.GetName().data(),
1007                         string(pathView).c_str());
1008                 }
1009             }
1010         }
1011     }
1012     return true;
1013 }
1014 
ReadProperty(IEntityCollection & ec,const json::value & jsonIn,const Property & property,uintptr_t offset) const1015 bool EcsSerializer::ReadProperty(
1016     IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
1017 {
1018     // See if there is a serializer for this type. Otherwise recurse further.
1019     auto serializer = typetoSerializerMap_.find(property.type);
1020     if (serializer != typetoSerializerMap_.end()) {
1021         return serializer->second->FromJson(ec, jsonIn, property, offset);
1022     } else if (!property.metaData.enumMetaData.empty()) {
1023         // Enum type property.
1024         if (jsonIn.is_unsigned_int()) {
1025             switch (property.size) {
1026                 case sizeof(uint8_t):
1027                     GetPropertyValue<uint8_t>(offset) = static_cast<uint8_t>(jsonIn.unsigned_);
1028                     return true;
1029                 case sizeof(uint16_t):
1030                     GetPropertyValue<uint16_t>(offset) = static_cast<uint16_t>(jsonIn.unsigned_);
1031                     return true;
1032                 case sizeof(uint32_t):
1033                     GetPropertyValue<uint32_t>(offset) = static_cast<uint32_t>(jsonIn.unsigned_);
1034                     return true;
1035                 case sizeof(uint64_t):
1036                     GetPropertyValue<uint64_t>(offset) = static_cast<uint64_t>(jsonIn.unsigned_);
1037                     return true;
1038                 default:
1039                     return false;
1040             }
1041         }
1042     } else if (property.metaData.containerMethods) {
1043         // Special handling for byte data encoded as base64.
1044         if (jsonIn.is_string()) {
1045             // A base64 encoded string containing raw array data
1046             auto bytes = BASE_NS::Base64Decode(jsonIn.string_);
1047 
1048             // Only valid for byte data.
1049             if (property.type == PropertyType::UINT8_ARRAY_T) {
1050                 if (property.size != bytes.size()) {
1051                     CORE_LOG_W("Invalid base64 data size in: %s", string(property.name).c_str());
1052                     return false;
1053                 }
1054                 auto& dstValue = GetPropertyValue<uint8_t[]>(offset);
1055                 CloneData(dstValue, property.size, &bytes[0], bytes.size());
1056                 return true;
1057             } else if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
1058                 GetPropertyValue<vector<uint8_t>>(offset).swap(bytes);
1059                 return true;
1060             }
1061             return false;
1062         }
1063 
1064         // Container type property.
1065         if (property.type.isArray) {
1066             // C style array.
1067             if (jsonIn.is_array()) {
1068                 if (jsonIn.array_.size() != property.count) {
1069                     CORE_LOG_W("Expecting a json array of size %zu", property.count);
1070                     return false;
1071                 }
1072                 for (size_t i = 0; i < property.count; i++) {
1073                     uintptr_t ptr = offset + i * property.metaData.containerMethods->property.size;
1074                     // return false if any recurseive call fails?
1075                     ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1076                 }
1077                 return true;
1078             } else if (jsonIn.is_object()) {
1079                 // Allow "sparse arrays" by using objects with the array index as the key.
1080                 for (auto& element : jsonIn.object_) {
1081                     const auto& key = element.key;
1082                     const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1083                     if ((index == 0 && key != "0") || index >= property.count) {
1084                         CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1085                         continue;
1086                     }
1087                     uintptr_t ptr = offset + index * property.metaData.containerMethods->property.size;
1088                     ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1089                 }
1090                 return true;
1091             }
1092             return false;
1093         } else {
1094             // This is a "non trivial container".
1095             if (jsonIn.is_array()) {
1096                 const auto count = jsonIn.array_.size();
1097                 property.metaData.containerMethods->resize(offset, count);
1098                 for (size_t i = 0; i < count; i++) {
1099                     uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
1100                     ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
1101                 }
1102                 return true;
1103             } else if (jsonIn.is_object()) {
1104                 // Allow "sparse arrays" by using objects with the array index as the key.
1105                 for (auto& element : jsonIn.object_) {
1106                     const auto& key = element.key;
1107                     const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
1108                     if ((index == 0 && key != "0")) {
1109                         CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
1110                         continue;
1111                     }
1112 
1113                     const auto count = property.metaData.containerMethods->size(offset);
1114                     if (count <= index) {
1115                         property.metaData.containerMethods->resize(offset, index + 1);
1116                     }
1117                     uintptr_t ptr = property.metaData.containerMethods->get(offset, index);
1118                     ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
1119                 }
1120                 return true;
1121             }
1122             return false;
1123         }
1124     } else if (!property.metaData.memberProperties.empty()) {
1125         // Struct type property (ie. has sub properties).
1126         if (jsonIn.is_object()) {
1127             for (const auto& subProperty : property.metaData.memberProperties) {
1128                 // is there a way to not create the string
1129                 const string name(subProperty.name);
1130                 const auto* subJson = jsonIn.find(name);
1131                 if (subJson) {
1132                     ReadProperty(ec, *subJson, subProperty, offset + subProperty.offset);
1133                 }
1134             }
1135             // return false if any recurseive call fails?
1136             return true;
1137         }
1138     }
1139 
1140     return false;
1141 }
1142 
LoadImageResource(BASE_NS::string_view uri) const1143 RENDER_NS::RenderHandleReference EcsSerializer::LoadImageResource(BASE_NS::string_view uri) const
1144 {
1145     auto& gpuResourceMgr = renderContext_.GetDevice().GetGpuResourceManager();
1146     // Check if there's already a GPU image for this file and re-use if there is.
1147     RenderHandleReference imageHandle = gpuResourceMgr.GetImageHandle(uri);
1148     if (imageHandle) {
1149         return imageHandle;
1150     }
1151 
1152     // Image loading flags can be passed in the uri query string.
1153     uint64_t imageLoaderFlags {};
1154     const auto params = PathUtil::GetUriParameters(uri);
1155     const auto loaderFlags = params.find("loaderFlags");
1156     if (loaderFlags != params.end()) {
1157         const char* start = loaderFlags->second.data();
1158         const char* end = start + loaderFlags->second.size();
1159         std::from_chars(start, end, imageLoaderFlags);
1160     }
1161 
1162     // Get rid of a possible query string in the uri.
1163     const auto fileUri = PathUtil::ResolveUri({}, uri, false);
1164     auto imageLoadResult = renderContext_.GetEngine().GetImageLoaderManager().LoadImage(fileUri, imageLoaderFlags);
1165     if (imageLoadResult.success) {
1166         const GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(imageLoadResult.image->GetImageDesc());
1167         imageHandle = gpuResourceMgr.Create(uri, gpuDesc, std::move(imageLoadResult.image));
1168     } else {
1169         CORE_LOG_E("Could not load image asset: %s", imageLoadResult.error);
1170     }
1171 
1172     return imageHandle;
1173 }
1174 
Destroy()1175 void EcsSerializer::Destroy()
1176 {
1177     delete this;
1178 }
1179 
CreateEcsSerializer(RENDER_NS::IRenderContext & renderContext)1180 IEcsSerializer::Ptr CreateEcsSerializer(RENDER_NS::IRenderContext& renderContext)
1181 {
1182     return IEcsSerializer::Ptr { new EcsSerializer(renderContext) };
1183 }
1184 
1185 ECS_SERIALIZER_END_NAMESPACE()
1186