18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci// This is bit unfortunate construct, clean up once there is one agreed implementation of JSON somewhere
178bf80f4bSopenharmony_ci#include <PropertyTools/property_data.h>
188bf80f4bSopenharmony_ci#define JSON_IMPL // have template specialisation defined once
198bf80f4bSopenharmony_ci#include <algorithm>
208bf80f4bSopenharmony_ci#include <charconv>
218bf80f4bSopenharmony_ci#include <cinttypes>
228bf80f4bSopenharmony_ci
238bf80f4bSopenharmony_ci#include <3d/ecs/components/name_component.h>
248bf80f4bSopenharmony_ci#include <3d/ecs/components/render_handle_component.h>
258bf80f4bSopenharmony_ci#include <base/util/base64_decode.h>
268bf80f4bSopenharmony_ci#include <base/util/base64_encode.h>
278bf80f4bSopenharmony_ci#include <base/util/uid_util.h>
288bf80f4bSopenharmony_ci#include <core/ecs/intf_component_manager.h>
298bf80f4bSopenharmony_ci#include <core/ecs/intf_entity_manager.h>
308bf80f4bSopenharmony_ci#include <core/image/intf_image_loader_manager.h>
318bf80f4bSopenharmony_ci#include <core/intf_engine.h>
328bf80f4bSopenharmony_ci#include <core/property/intf_property_handle.h>
338bf80f4bSopenharmony_ci#include <render/device/intf_gpu_resource_manager.h>
348bf80f4bSopenharmony_ci#include <render/device/intf_shader_manager.h>
358bf80f4bSopenharmony_ci#include <render/util/intf_render_util.h>
368bf80f4bSopenharmony_ci
378bf80f4bSopenharmony_ci#include "asset_loader.h"
388bf80f4bSopenharmony_ci#include "ecs_serializer.h"
398bf80f4bSopenharmony_ci#include "ecs_util.h"
408bf80f4bSopenharmony_ci#include "entity_collection.h"
418bf80f4bSopenharmony_ci#include "json.h"
428bf80f4bSopenharmony_ci#include "json_util.h"
438bf80f4bSopenharmony_ci
448bf80f4bSopenharmony_ciusing namespace BASE_NS;
458bf80f4bSopenharmony_ciusing namespace CORE_NS;
468bf80f4bSopenharmony_ciusing namespace RENDER_NS;
478bf80f4bSopenharmony_ciusing namespace CORE3D_NS;
488bf80f4bSopenharmony_ci
498bf80f4bSopenharmony_ci// #define VERBOSE_LOGGING
508bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
518bf80f4bSopenharmony_ci
528bf80f4bSopenharmony_cinamespace {
538bf80f4bSopenharmony_ci
548bf80f4bSopenharmony_ci// Helper function that makes sure that any dynamic arrays referenced in a property path are large enough to contain the
558bf80f4bSopenharmony_ci// referenced indices.
568bf80f4bSopenharmony_civoid EnsureDynamicArraySize(IPropertyHandle* propertyHandle, string_view propertyPath)
578bf80f4bSopenharmony_ci{
588bf80f4bSopenharmony_ci    const auto separatorPosition = propertyPath.find('[');
598bf80f4bSopenharmony_ci    if (separatorPosition == BASE_NS::string::npos) {
608bf80f4bSopenharmony_ci        return;
618bf80f4bSopenharmony_ci    }
628bf80f4bSopenharmony_ci    const auto separatorEndPosition = propertyPath.find(']', separatorPosition);
638bf80f4bSopenharmony_ci    if (separatorEndPosition == BASE_NS::string::npos) {
648bf80f4bSopenharmony_ci        return;
658bf80f4bSopenharmony_ci    }
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_ci    string arrayPath, arrayIndex;
688bf80f4bSopenharmony_ci    arrayPath = propertyPath.substr(0, separatorPosition);
698bf80f4bSopenharmony_ci    arrayIndex = propertyPath.substr(separatorPosition + 1, separatorEndPosition - separatorPosition - 1);
708bf80f4bSopenharmony_ci
718bf80f4bSopenharmony_ci    char* end = nullptr;
728bf80f4bSopenharmony_ci    const unsigned long index = std::strtoul(arrayIndex.c_str(), &end, 10); // 10: base
738bf80f4bSopenharmony_ci    // Check that conversion stopped at the end of the string.
748bf80f4bSopenharmony_ci    if (!end || *end != '\0') {
758bf80f4bSopenharmony_ci        return;
768bf80f4bSopenharmony_ci    }
778bf80f4bSopenharmony_ci
788bf80f4bSopenharmony_ci    PropertyData propertyData;
798bf80f4bSopenharmony_ci    PropertyData::PropertyOffset propertyOffset = propertyData.WLock(*propertyHandle, arrayPath);
808bf80f4bSopenharmony_ci    if (propertyOffset) {
818bf80f4bSopenharmony_ci        auto* containerMethods = propertyOffset.property->metaData.containerMethods;
828bf80f4bSopenharmony_ci        if (containerMethods && containerMethods->resize) {
838bf80f4bSopenharmony_ci            if (containerMethods->size(propertyOffset.offset) <= index) {
848bf80f4bSopenharmony_ci                containerMethods->resize(propertyOffset.offset, index + 1);
858bf80f4bSopenharmony_ci            }
868bf80f4bSopenharmony_ci        }
878bf80f4bSopenharmony_ci    }
888bf80f4bSopenharmony_ci
898bf80f4bSopenharmony_ci    const auto restOfThePath = propertyPath.substr(separatorEndPosition);
908bf80f4bSopenharmony_ci    EnsureDynamicArraySize(&propertyData, restOfThePath);
918bf80f4bSopenharmony_ci}
928bf80f4bSopenharmony_ci
938bf80f4bSopenharmony_citemplate<typename Type>
948bf80f4bSopenharmony_ciType& GetPropertyValue(uintptr_t ptr)
958bf80f4bSopenharmony_ci{
968bf80f4bSopenharmony_ci    return *reinterpret_cast<Type*>(ptr);
978bf80f4bSopenharmony_ci}
988bf80f4bSopenharmony_ci
998bf80f4bSopenharmony_cistruct IoUtil {
1008bf80f4bSopenharmony_ci    struct CompatibilityInfo {
1018bf80f4bSopenharmony_ci        uint32_t versionMajor { 0 };
1028bf80f4bSopenharmony_ci        uint32_t versionMinor { 0 };
1038bf80f4bSopenharmony_ci        BASE_NS::string type;
1048bf80f4bSopenharmony_ci    };
1058bf80f4bSopenharmony_ci
1068bf80f4bSopenharmony_ci    struct CompatibilityRange {
1078bf80f4bSopenharmony_ci        static const uint32_t IGNORE_VERSION = { ~0u };
1088bf80f4bSopenharmony_ci
1098bf80f4bSopenharmony_ci        uint32_t versionMajorMin { IGNORE_VERSION };
1108bf80f4bSopenharmony_ci        uint32_t versionMajorMax { IGNORE_VERSION };
1118bf80f4bSopenharmony_ci        uint32_t versionMinorMin { IGNORE_VERSION };
1128bf80f4bSopenharmony_ci        uint32_t versionMinorMax { IGNORE_VERSION };
1138bf80f4bSopenharmony_ci        BASE_NS::string type {};
1148bf80f4bSopenharmony_ci    };
1158bf80f4bSopenharmony_ci
1168bf80f4bSopenharmony_ci    static bool WriteCompatibilityInfo(json::standalone_value& jsonOut, const CompatibilityInfo& info)
1178bf80f4bSopenharmony_ci    {
1188bf80f4bSopenharmony_ci        jsonOut["compatibility_info"] = json::standalone_value::object();
1198bf80f4bSopenharmony_ci        jsonOut["compatibility_info"]["version"] =
1208bf80f4bSopenharmony_ci            string(to_string(info.versionMajor) + "." + to_string(info.versionMinor));
1218bf80f4bSopenharmony_ci        jsonOut["compatibility_info"]["type"] = string(info.type);
1228bf80f4bSopenharmony_ci        return true;
1238bf80f4bSopenharmony_ci    }
1248bf80f4bSopenharmony_ci
1258bf80f4bSopenharmony_ci    static bool CheckCompatibility(const json::value& json, array_view<CompatibilityRange const> validVersions)
1268bf80f4bSopenharmony_ci    {
1278bf80f4bSopenharmony_ci        string type;
1288bf80f4bSopenharmony_ci        string version;
1298bf80f4bSopenharmony_ci        if (const json::value* iter = json.find("compatibility_info"); iter) {
1308bf80f4bSopenharmony_ci            string parseError;
1318bf80f4bSopenharmony_ci            SafeGetJsonValue(*iter, "type", parseError, type);
1328bf80f4bSopenharmony_ci            SafeGetJsonValue(*iter, "version", parseError, version);
1338bf80f4bSopenharmony_ci
1348bf80f4bSopenharmony_ci            uint32_t versionMajor { 0 };
1358bf80f4bSopenharmony_ci            uint32_t versionMinor { 0 };
1368bf80f4bSopenharmony_ci            if (const auto delim = version.find('.'); delim != string::npos) {
1378bf80f4bSopenharmony_ci                std::from_chars(version.data(), version.data() + delim, versionMajor);
1388bf80f4bSopenharmony_ci                const size_t minorStart = delim + 1;
1398bf80f4bSopenharmony_ci                std::from_chars(version.data() + minorStart, version.data() + version.size(), versionMinor);
1408bf80f4bSopenharmony_ci            } else {
1418bf80f4bSopenharmony_ci                std::from_chars(version.data(), version.data() + version.size(), versionMajor);
1428bf80f4bSopenharmony_ci            }
1438bf80f4bSopenharmony_ci
1448bf80f4bSopenharmony_ci            for (const auto& range : validVersions) {
1458bf80f4bSopenharmony_ci                if (type != range.type) {
1468bf80f4bSopenharmony_ci                    continue;
1478bf80f4bSopenharmony_ci                }
1488bf80f4bSopenharmony_ci                if ((range.versionMajorMin != CompatibilityRange::IGNORE_VERSION) &&
1498bf80f4bSopenharmony_ci                    (versionMajor < range.versionMajorMin)) {
1508bf80f4bSopenharmony_ci                    continue;
1518bf80f4bSopenharmony_ci                }
1528bf80f4bSopenharmony_ci                if ((range.versionMajorMax != CompatibilityRange::IGNORE_VERSION) &&
1538bf80f4bSopenharmony_ci                    (versionMajor > range.versionMajorMax)) {
1548bf80f4bSopenharmony_ci                    continue;
1558bf80f4bSopenharmony_ci                }
1568bf80f4bSopenharmony_ci                if ((range.versionMinorMin != CompatibilityRange::IGNORE_VERSION) &&
1578bf80f4bSopenharmony_ci                    (versionMinor < range.versionMinorMin)) {
1588bf80f4bSopenharmony_ci                    continue;
1598bf80f4bSopenharmony_ci                }
1608bf80f4bSopenharmony_ci                if ((range.versionMinorMax != CompatibilityRange::IGNORE_VERSION) &&
1618bf80f4bSopenharmony_ci                    (versionMinor > range.versionMinorMax)) {
1628bf80f4bSopenharmony_ci                    continue;
1638bf80f4bSopenharmony_ci                }
1648bf80f4bSopenharmony_ci
1658bf80f4bSopenharmony_ci                // A compatible version was found from the list of valid versions.
1668bf80f4bSopenharmony_ci                return true;
1678bf80f4bSopenharmony_ci            }
1688bf80f4bSopenharmony_ci        }
1698bf80f4bSopenharmony_ci
1708bf80f4bSopenharmony_ci        // Not a compatible version.
1718bf80f4bSopenharmony_ci        return false;
1728bf80f4bSopenharmony_ci    }
1738bf80f4bSopenharmony_ci};
1748bf80f4bSopenharmony_ci} // namespace
1758bf80f4bSopenharmony_ci
1768bf80f4bSopenharmony_ci//
1778bf80f4bSopenharmony_ci// EcsSerializer::SimpleJsonSerializer
1788bf80f4bSopenharmony_ci//
1798bf80f4bSopenharmony_cibool EcsSerializer::SimpleJsonSerializer::ToJson(
1808bf80f4bSopenharmony_ci    const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
1818bf80f4bSopenharmony_ci{
1828bf80f4bSopenharmony_ci    return PropertyToJson(ec, property, offset, jsonOut);
1838bf80f4bSopenharmony_ci}
1848bf80f4bSopenharmony_ci
1858bf80f4bSopenharmony_cibool EcsSerializer::SimpleJsonSerializer::FromJson(
1868bf80f4bSopenharmony_ci    const IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
1878bf80f4bSopenharmony_ci{
1888bf80f4bSopenharmony_ci    return PropertyFromJson(ec, jsonIn, property, offset);
1898bf80f4bSopenharmony_ci}
1908bf80f4bSopenharmony_ci
1918bf80f4bSopenharmony_ciEcsSerializer::SimpleJsonSerializer::SimpleJsonSerializer(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
1928bf80f4bSopenharmony_ci    : PropertyToJson(toJson), PropertyFromJson(fromJson)
1938bf80f4bSopenharmony_ci{}
1948bf80f4bSopenharmony_ci
1958bf80f4bSopenharmony_ci//
1968bf80f4bSopenharmony_ci// EcsSerializer
1978bf80f4bSopenharmony_ci//
1988bf80f4bSopenharmony_ciEcsSerializer::EcsSerializer(RENDER_NS::IRenderContext& renderContext) : renderContext_(renderContext) {}
1998bf80f4bSopenharmony_ci
2008bf80f4bSopenharmony_ciEcsSerializer::IPropertySerializer& EcsSerializer::Add(PropertyToJsonFunc toJson, PropertyFromJsonFunc fromJson)
2018bf80f4bSopenharmony_ci{
2028bf80f4bSopenharmony_ci    auto serializer = new SimpleJsonSerializer(toJson, fromJson);
2038bf80f4bSopenharmony_ci    auto ptr = unique_ptr<SimpleJsonSerializer> { serializer };
2048bf80f4bSopenharmony_ci    ownedSerializers_.emplace_back(move(ptr));
2058bf80f4bSopenharmony_ci    return *ownedSerializers_.back();
2068bf80f4bSopenharmony_ci}
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_cinamespace {
2098bf80f4bSopenharmony_citemplate<class Type>
2108bf80f4bSopenharmony_cibool PropertyToJson(
2118bf80f4bSopenharmony_ci    const IEntityCollection& /*ec*/, const Property& /*property*/, uintptr_t offset, json::standalone_value& jsonOut)
2128bf80f4bSopenharmony_ci{
2138bf80f4bSopenharmony_ci    jsonOut = ToJson(GetPropertyValue<Type>(offset));
2148bf80f4bSopenharmony_ci    return true;
2158bf80f4bSopenharmony_ci}
2168bf80f4bSopenharmony_ci
2178bf80f4bSopenharmony_citemplate<class Type>
2188bf80f4bSopenharmony_cibool PropertyFromJson(
2198bf80f4bSopenharmony_ci    const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset)
2208bf80f4bSopenharmony_ci{
2218bf80f4bSopenharmony_ci    auto& value = GetPropertyValue<Type>(offset);
2228bf80f4bSopenharmony_ci    return FromJson(jsonIn, value);
2238bf80f4bSopenharmony_ci}
2248bf80f4bSopenharmony_ci
2258bf80f4bSopenharmony_cibool RenderHandleReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
2268bf80f4bSopenharmony_ci    const IEntityCollection& ec, const json::value& jsonIn, RenderHandleReference& handleRefOut)
2278bf80f4bSopenharmony_ci{
2288bf80f4bSopenharmony_ci    CORE_UNUSED(ec);
2298bf80f4bSopenharmony_ci
2308bf80f4bSopenharmony_ci    if (!jsonIn.is_object()) {
2318bf80f4bSopenharmony_ci        return false;
2328bf80f4bSopenharmony_ci    }
2338bf80f4bSopenharmony_ci
2348bf80f4bSopenharmony_ci    string error;
2358bf80f4bSopenharmony_ci    RenderHandleDesc desc;
2368bf80f4bSopenharmony_ci
2378bf80f4bSopenharmony_ci    // Casting to enum directly from numeric value is a bit fishy.
2388bf80f4bSopenharmony_ci    uint32_t type = 0;
2398bf80f4bSopenharmony_ci    if (!SafeGetJsonValue(jsonIn, "type", error, type)) {
2408bf80f4bSopenharmony_ci        return false;
2418bf80f4bSopenharmony_ci    }
2428bf80f4bSopenharmony_ci    desc.type = static_cast<RenderHandleType>(type);
2438bf80f4bSopenharmony_ci
2448bf80f4bSopenharmony_ci    if (!SafeGetJsonValue(jsonIn, "name", error, desc.name)) {
2458bf80f4bSopenharmony_ci        return false;
2468bf80f4bSopenharmony_ci    }
2478bf80f4bSopenharmony_ci
2488bf80f4bSopenharmony_ci    SafeGetJsonValue(jsonIn, "additionalName", error, desc.additionalName);
2498bf80f4bSopenharmony_ci
2508bf80f4bSopenharmony_ci    auto& renderUtil = renderContext.GetRenderUtil();
2518bf80f4bSopenharmony_ci    handleRefOut = renderUtil.GetRenderHandle(desc);
2528bf80f4bSopenharmony_ci
2538bf80f4bSopenharmony_ci    if (!handleRefOut && desc.type == RenderHandleType::GPU_IMAGE && !desc.name.empty()) {
2548bf80f4bSopenharmony_ci        // Special handling for images: Load the image if it was not already loaded.
2558bf80f4bSopenharmony_ci        // Note: assuming that the name is the image uri.
2568bf80f4bSopenharmony_ci        handleRefOut = ecsSerializer.LoadImageResource(desc.name);
2578bf80f4bSopenharmony_ci    }
2588bf80f4bSopenharmony_ci
2598bf80f4bSopenharmony_ci    return true;
2608bf80f4bSopenharmony_ci}
2618bf80f4bSopenharmony_ci
2628bf80f4bSopenharmony_cibool RenderHandleReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec,
2638bf80f4bSopenharmony_ci    const RenderHandleReference& handleRefIn, json::standalone_value& jsonOut)
2648bf80f4bSopenharmony_ci{
2658bf80f4bSopenharmony_ci    CORE_UNUSED(ec);
2668bf80f4bSopenharmony_ci
2678bf80f4bSopenharmony_ci    auto& renderUtil = renderContext.GetRenderUtil();
2688bf80f4bSopenharmony_ci    const auto desc = renderUtil.GetRenderHandleDesc(handleRefIn);
2698bf80f4bSopenharmony_ci
2708bf80f4bSopenharmony_ci    jsonOut = json::standalone_value::object {};
2718bf80f4bSopenharmony_ci    jsonOut["type"] = static_cast<unsigned int>(desc.type);
2728bf80f4bSopenharmony_ci    jsonOut["name"] = string(desc.name);
2738bf80f4bSopenharmony_ci    if (!desc.additionalName.empty()) {
2748bf80f4bSopenharmony_ci        jsonOut["additionalName"] = string(desc.additionalName);
2758bf80f4bSopenharmony_ci    }
2768bf80f4bSopenharmony_ci
2778bf80f4bSopenharmony_ci    return true;
2788bf80f4bSopenharmony_ci}
2798bf80f4bSopenharmony_ci
2808bf80f4bSopenharmony_cibool EntityFromJson(const IEntityCollection& ec, const json::value& jsonIn, Entity& entityOut)
2818bf80f4bSopenharmony_ci{
2828bf80f4bSopenharmony_ci    // Figure out to what entity id a piece of json refers to and handle error cases.
2838bf80f4bSopenharmony_ci    Entity entity {};
2848bf80f4bSopenharmony_ci    if (jsonIn.is_unsigned_int()) {
2858bf80f4bSopenharmony_ci        const auto entityReference = static_cast<uint32_t>(jsonIn.unsigned_);
2868bf80f4bSopenharmony_ci        entity = ec.GetEntity(entityReference);
2878bf80f4bSopenharmony_ci        if (entity == Entity {}) {
2888bf80f4bSopenharmony_ci            CORE_LOG_W("Component entity not found for index: %u", entityReference);
2898bf80f4bSopenharmony_ci            return false;
2908bf80f4bSopenharmony_ci        }
2918bf80f4bSopenharmony_ci    } else if (jsonIn.is_string()) {
2928bf80f4bSopenharmony_ci        string entityReferenceString;
2938bf80f4bSopenharmony_ci        if (FromJson(jsonIn, entityReferenceString)) {
2948bf80f4bSopenharmony_ci            entity = ec.GetEntity(entityReferenceString);
2958bf80f4bSopenharmony_ci        }
2968bf80f4bSopenharmony_ci        if (entity == Entity {}) {
2978bf80f4bSopenharmony_ci            CORE_LOG_W("Component entity not found for id: '%s'", entityReferenceString.c_str());
2988bf80f4bSopenharmony_ci            return false;
2998bf80f4bSopenharmony_ci        }
3008bf80f4bSopenharmony_ci    } else if (jsonIn.is_object()) {
3018bf80f4bSopenharmony_ci        // Empty object means "null ref".
3028bf80f4bSopenharmony_ci        if (jsonIn.empty()) {
3038bf80f4bSopenharmony_ci            entityOut = Entity {};
3048bf80f4bSopenharmony_ci            return true;
3058bf80f4bSopenharmony_ci        }
3068bf80f4bSopenharmony_ci
3078bf80f4bSopenharmony_ci        // Figure out the correct collection (Recursive).
3088bf80f4bSopenharmony_ci        const IEntityCollection* collection { nullptr };
3098bf80f4bSopenharmony_ci        const auto* collectionJson = jsonIn.find("collection");
3108bf80f4bSopenharmony_ci        if (collectionJson) {
3118bf80f4bSopenharmony_ci            if (collectionJson->is_string()) {
3128bf80f4bSopenharmony_ci                string collectionName;
3138bf80f4bSopenharmony_ci                if (FromJson(*collectionJson, collectionName)) {
3148bf80f4bSopenharmony_ci                    if (auto index = ec.GetSubCollectionIndex(collectionName); index >= 0) {
3158bf80f4bSopenharmony_ci                        collection = ec.GetSubCollection(static_cast<size_t>(index));
3168bf80f4bSopenharmony_ci                    }
3178bf80f4bSopenharmony_ci                }
3188bf80f4bSopenharmony_ci                if (!collection) {
3198bf80f4bSopenharmony_ci                    CORE_LOG_W("Collection not found: '%s'", collectionName.c_str());
3208bf80f4bSopenharmony_ci                    return false;
3218bf80f4bSopenharmony_ci                }
3228bf80f4bSopenharmony_ci
3238bf80f4bSopenharmony_ci            } else if (collectionJson->is_unsigned_int()) {
3248bf80f4bSopenharmony_ci                const auto collectionIndex = collectionJson->unsigned_;
3258bf80f4bSopenharmony_ci                if (collectionIndex < ec.GetSubCollectionCount()) {
3268bf80f4bSopenharmony_ci                    collection = ec.GetSubCollection(collectionIndex);
3278bf80f4bSopenharmony_ci                } else {
3288bf80f4bSopenharmony_ci                    CORE_LOG_W("Collection not found: %" PRIu64, collectionIndex);
3298bf80f4bSopenharmony_ci                    return false;
3308bf80f4bSopenharmony_ci                }
3318bf80f4bSopenharmony_ci
3328bf80f4bSopenharmony_ci            } else {
3338bf80f4bSopenharmony_ci                CORE_LOG_W("Invalid collection for a component.");
3348bf80f4bSopenharmony_ci                return false;
3358bf80f4bSopenharmony_ci            }
3368bf80f4bSopenharmony_ci
3378bf80f4bSopenharmony_ci            if (collection) {
3388bf80f4bSopenharmony_ci                const auto* entityJson = jsonIn.find("entity");
3398bf80f4bSopenharmony_ci                if (entityJson) {
3408bf80f4bSopenharmony_ci                    return EntityFromJson(*collection, *entityJson, entityOut);
3418bf80f4bSopenharmony_ci                }
3428bf80f4bSopenharmony_ci            }
3438bf80f4bSopenharmony_ci            return false;
3448bf80f4bSopenharmony_ci        }
3458bf80f4bSopenharmony_ci
3468bf80f4bSopenharmony_ci    } else {
3478bf80f4bSopenharmony_ci        CORE_LOG_W("Component entity property must be an index to the entities array, an string id, or an object");
3488bf80f4bSopenharmony_ci        return false;
3498bf80f4bSopenharmony_ci    }
3508bf80f4bSopenharmony_ci
3518bf80f4bSopenharmony_ci    entityOut = entity;
3528bf80f4bSopenharmony_ci    return true;
3538bf80f4bSopenharmony_ci}
3548bf80f4bSopenharmony_ci
3558bf80f4bSopenharmony_cibool EntityToJson(const IEntityCollection& ec, const Entity& entityIn, json::standalone_value& jsonOut)
3568bf80f4bSopenharmony_ci{
3578bf80f4bSopenharmony_ci    // Write entity index/name if part of this collection.
3588bf80f4bSopenharmony_ci    const auto entityCount = ec.GetEntityCount();
3598bf80f4bSopenharmony_ci    for (size_t i = 0; i < entityCount; ++i) {
3608bf80f4bSopenharmony_ci        if (entityIn == ec.GetEntity(i)) {
3618bf80f4bSopenharmony_ci            auto id = ec.GetId(entityIn);
3628bf80f4bSopenharmony_ci            if (!id.empty()) {
3638bf80f4bSopenharmony_ci                jsonOut = string(id);
3648bf80f4bSopenharmony_ci            } else {
3658bf80f4bSopenharmony_ci                jsonOut = i;
3668bf80f4bSopenharmony_ci            }
3678bf80f4bSopenharmony_ci            return true;
3688bf80f4bSopenharmony_ci        }
3698bf80f4bSopenharmony_ci    }
3708bf80f4bSopenharmony_ci
3718bf80f4bSopenharmony_ci    // Otherwise check sub-collections recursively.
3728bf80f4bSopenharmony_ci    const auto collectionCount = ec.GetSubCollectionCount();
3738bf80f4bSopenharmony_ci    size_t collectionId = 0;
3748bf80f4bSopenharmony_ci    for (size_t i = 0; i < collectionCount; ++i) {
3758bf80f4bSopenharmony_ci        auto* collection = ec.GetSubCollection(i);
3768bf80f4bSopenharmony_ci        BASE_ASSERT(collection);
3778bf80f4bSopenharmony_ci        // NOTE: Skipping over destroyed collections (same needs to be done when writing the actual collections).
3788bf80f4bSopenharmony_ci        if (collection->IsMarkedDestroyed() || !collection->IsSerialized()) {
3798bf80f4bSopenharmony_ci            continue;
3808bf80f4bSopenharmony_ci        }
3818bf80f4bSopenharmony_ci        json::standalone_value entityJson;
3828bf80f4bSopenharmony_ci        if (EntityToJson(*collection, entityIn, entityJson)) {
3838bf80f4bSopenharmony_ci            jsonOut = json::standalone_value::object {};
3848bf80f4bSopenharmony_ci            jsonOut[string_view { "collection" }] = collectionId;
3858bf80f4bSopenharmony_ci            jsonOut[string_view { "entity" }] = move(entityJson);
3868bf80f4bSopenharmony_ci            return true;
3878bf80f4bSopenharmony_ci        }
3888bf80f4bSopenharmony_ci        collectionId++;
3898bf80f4bSopenharmony_ci    }
3908bf80f4bSopenharmony_ci
3918bf80f4bSopenharmony_ci    return false;
3928bf80f4bSopenharmony_ci}
3938bf80f4bSopenharmony_ci
3948bf80f4bSopenharmony_cibool EntityReferenceToJson(IRenderContext& renderContext, const IEntityCollection& ec, const EntityReference& entityIn,
3958bf80f4bSopenharmony_ci    json::standalone_value& jsonOut)
3968bf80f4bSopenharmony_ci{
3978bf80f4bSopenharmony_ci    if (EntityToJson(ec, entityIn, jsonOut)) {
3988bf80f4bSopenharmony_ci        return true;
3998bf80f4bSopenharmony_ci    }
4008bf80f4bSopenharmony_ci
4018bf80f4bSopenharmony_ci    // Write render handle reference as render handle desc.
4028bf80f4bSopenharmony_ci    auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
4038bf80f4bSopenharmony_ci    if (rhm) {
4048bf80f4bSopenharmony_ci        if (auto handle = rhm->Read(entityIn); handle) {
4058bf80f4bSopenharmony_ci            json::standalone_value renderHandleJson = json::standalone_value::object {};
4068bf80f4bSopenharmony_ci            if (RenderHandleReferenceToJson(renderContext, ec, handle->reference, renderHandleJson["renderHandle"])) {
4078bf80f4bSopenharmony_ci                jsonOut = move(renderHandleJson);
4088bf80f4bSopenharmony_ci                return true;
4098bf80f4bSopenharmony_ci            }
4108bf80f4bSopenharmony_ci        }
4118bf80f4bSopenharmony_ci    }
4128bf80f4bSopenharmony_ci
4138bf80f4bSopenharmony_ci    return false;
4148bf80f4bSopenharmony_ci}
4158bf80f4bSopenharmony_ci
4168bf80f4bSopenharmony_cibool EntityReferenceFromJson(const IEcsSerializer& ecsSerializer, IRenderContext& renderContext,
4178bf80f4bSopenharmony_ci    const IEntityCollection& ec, const json::value& jsonIn, EntityReference& entityOut)
4188bf80f4bSopenharmony_ci{
4198bf80f4bSopenharmony_ci    // A generic handler for any uri to a render handle.
4208bf80f4bSopenharmony_ci    const auto* renderHandleJson = jsonIn.find("renderHandle");
4218bf80f4bSopenharmony_ci    if (renderHandleJson) {
4228bf80f4bSopenharmony_ci        // Find a shader render handle by desc.
4238bf80f4bSopenharmony_ci        RenderHandleReference renderHandle;
4248bf80f4bSopenharmony_ci        if (RenderHandleReferenceFromJson(ecsSerializer, renderContext, ec, *renderHandleJson, renderHandle)) {
4258bf80f4bSopenharmony_ci            auto* rhm = GetManager<IRenderHandleComponentManager>(ec.GetEcs());
4268bf80f4bSopenharmony_ci            if (rhm && renderHandle) {
4278bf80f4bSopenharmony_ci                entityOut = GetOrCreateEntityReference(ec.GetEcs().GetEntityManager(), *rhm, renderHandle);
4288bf80f4bSopenharmony_ci                return true;
4298bf80f4bSopenharmony_ci            }
4308bf80f4bSopenharmony_ci        }
4318bf80f4bSopenharmony_ci    }
4328bf80f4bSopenharmony_ci
4338bf80f4bSopenharmony_ci    Entity temp;
4348bf80f4bSopenharmony_ci    if (!EntityFromJson(ec, jsonIn, temp)) {
4358bf80f4bSopenharmony_ci        return false;
4368bf80f4bSopenharmony_ci    }
4378bf80f4bSopenharmony_ci    entityOut = ec.GetEcs().GetEntityManager().GetReferenceCounted(temp);
4388bf80f4bSopenharmony_ci    return true;
4398bf80f4bSopenharmony_ci}
4408bf80f4bSopenharmony_ci} // namespace
4418bf80f4bSopenharmony_ci
4428bf80f4bSopenharmony_civoid EcsSerializer::SetDefaultSerializers()
4438bf80f4bSopenharmony_ci{
4448bf80f4bSopenharmony_ci    //
4458bf80f4bSopenharmony_ci    // Basic types (for types that nlohman knows how to serialize).
4468bf80f4bSopenharmony_ci    //
4478bf80f4bSopenharmony_ci
4488bf80f4bSopenharmony_ci    SetSerializer(PropertyType::FLOAT_T, Add(PropertyToJson<float>, PropertyFromJson<float>));
4498bf80f4bSopenharmony_ci    SetSerializer(PropertyType::DOUBLE_T, Add(PropertyToJson<double>, PropertyFromJson<double>));
4508bf80f4bSopenharmony_ci
4518bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UINT8_T, Add(PropertyToJson<uint8_t>, PropertyFromJson<uint8_t>));
4528bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UINT16_T, Add(PropertyToJson<uint16_t>, PropertyFromJson<uint16_t>));
4538bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UINT32_T, Add(PropertyToJson<uint32_t>, PropertyFromJson<uint32_t>));
4548bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UINT64_T, Add(PropertyToJson<uint64_t>, PropertyFromJson<uint64_t>));
4558bf80f4bSopenharmony_ci
4568bf80f4bSopenharmony_ci    SetSerializer(PropertyType::INT8_T, Add(PropertyToJson<int8_t>, PropertyFromJson<int8_t>));
4578bf80f4bSopenharmony_ci    SetSerializer(PropertyType::INT16_T, Add(PropertyToJson<int16_t>, PropertyFromJson<int16_t>));
4588bf80f4bSopenharmony_ci    SetSerializer(PropertyType::INT32_T, Add(PropertyToJson<int32_t>, PropertyFromJson<int32_t>));
4598bf80f4bSopenharmony_ci    SetSerializer(PropertyType::INT64_T, Add(PropertyToJson<int64_t>, PropertyFromJson<int64_t>));
4608bf80f4bSopenharmony_ci
4618bf80f4bSopenharmony_ci    SetSerializer(PropertyType::VEC2_T, Add(PropertyToJson<Math::Vec2>, PropertyFromJson<Math::Vec2>));
4628bf80f4bSopenharmony_ci    SetSerializer(PropertyType::VEC3_T, Add(PropertyToJson<Math::Vec3>, PropertyFromJson<Math::Vec3>));
4638bf80f4bSopenharmony_ci    SetSerializer(PropertyType::VEC4_T, Add(PropertyToJson<Math::Vec4>, PropertyFromJson<Math::Vec4>));
4648bf80f4bSopenharmony_ci
4658bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UVEC2_T, Add(PropertyToJson<Math::UVec2>, PropertyFromJson<Math::UVec2>));
4668bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UVEC3_T, Add(PropertyToJson<Math::UVec3>, PropertyFromJson<Math::UVec3>));
4678bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UVEC4_T, Add(PropertyToJson<Math::UVec4>, PropertyFromJson<Math::UVec4>));
4688bf80f4bSopenharmony_ci
4698bf80f4bSopenharmony_ci    SetSerializer(PropertyType::BOOL_T, Add(PropertyToJson<bool>, PropertyFromJson<bool>));
4708bf80f4bSopenharmony_ci    SetSerializer(PropertyType::QUAT_T, Add(PropertyToJson<Math::Quat>, PropertyFromJson<Math::Quat>));
4718bf80f4bSopenharmony_ci    SetSerializer(PropertyType::UID_T, Add(PropertyToJson<Uid>, PropertyFromJson<Uid>));
4728bf80f4bSopenharmony_ci
4738bf80f4bSopenharmony_ci    SetSerializer(PropertyType::STRING_T, Add(PropertyToJson<string>, PropertyFromJson<string>));
4748bf80f4bSopenharmony_ci
4758bf80f4bSopenharmony_ci    //
4768bf80f4bSopenharmony_ci    // Others
4778bf80f4bSopenharmony_ci    //
4788bf80f4bSopenharmony_ci
4798bf80f4bSopenharmony_ci    SetSerializer(PropertyType::CHAR_ARRAY_T,
4808bf80f4bSopenharmony_ci        Add(
4818bf80f4bSopenharmony_ci            [](const IEntityCollection& /*ec*/, const Property& property, uintptr_t offset,
4828bf80f4bSopenharmony_ci                json::standalone_value& jsonOut) {
4838bf80f4bSopenharmony_ci                const auto* value = &GetPropertyValue<char>(offset);
4848bf80f4bSopenharmony_ci                // NOTE: a hacky way to calculate cstring size.
4858bf80f4bSopenharmony_ci                string_view view(value);
4868bf80f4bSopenharmony_ci                const size_t size = view.size() < property.size ? view.size() : property.size;
4878bf80f4bSopenharmony_ci                jsonOut = ToJson(string(value, size));
4888bf80f4bSopenharmony_ci                return true;
4898bf80f4bSopenharmony_ci            },
4908bf80f4bSopenharmony_ci            [](const IEntityCollection& /*ec*/, const json::value& jsonIn, const Property& property, uintptr_t offset) {
4918bf80f4bSopenharmony_ci                string value;
4928bf80f4bSopenharmony_ci                if (FromJson(jsonIn, value)) {
4938bf80f4bSopenharmony_ci                    char* charArray = &GetPropertyValue<char>(offset);
4948bf80f4bSopenharmony_ci                    charArray[value.copy(charArray, property.size - 1)] = '\0';
4958bf80f4bSopenharmony_ci                    return true;
4968bf80f4bSopenharmony_ci                }
4978bf80f4bSopenharmony_ci                return false;
4988bf80f4bSopenharmony_ci            }));
4998bf80f4bSopenharmony_ci
5008bf80f4bSopenharmony_ci    SetSerializer(PropertyType::ENTITY_T,
5018bf80f4bSopenharmony_ci        Add(
5028bf80f4bSopenharmony_ci            [](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
5038bf80f4bSopenharmony_ci                json::standalone_value& jsonOut) {
5048bf80f4bSopenharmony_ci                return EntityToJson(ec, GetPropertyValue<const Entity>(offset), jsonOut);
5058bf80f4bSopenharmony_ci            },
5068bf80f4bSopenharmony_ci            [](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/, uintptr_t offset) {
5078bf80f4bSopenharmony_ci                return EntityFromJson(ec, jsonIn, GetPropertyValue<Entity>(offset));
5088bf80f4bSopenharmony_ci            }));
5098bf80f4bSopenharmony_ci
5108bf80f4bSopenharmony_ci    SetSerializer(PropertyType::ENTITY_REFERENCE_T,
5118bf80f4bSopenharmony_ci        Add(
5128bf80f4bSopenharmony_ci            [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
5138bf80f4bSopenharmony_ci                json::standalone_value& jsonOut) {
5148bf80f4bSopenharmony_ci                return EntityReferenceToJson(
5158bf80f4bSopenharmony_ci                    renderContext_, ec, GetPropertyValue<const EntityReference>(offset), jsonOut);
5168bf80f4bSopenharmony_ci            },
5178bf80f4bSopenharmony_ci            [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
5188bf80f4bSopenharmony_ci                uintptr_t offset) {
5198bf80f4bSopenharmony_ci                return EntityReferenceFromJson(
5208bf80f4bSopenharmony_ci                    *this, renderContext_, ec, jsonIn, GetPropertyValue<EntityReference>(offset));
5218bf80f4bSopenharmony_ci            }));
5228bf80f4bSopenharmony_ci
5238bf80f4bSopenharmony_ci    SetSerializer(PROPERTYTYPE(RenderHandleReference),
5248bf80f4bSopenharmony_ci        Add(
5258bf80f4bSopenharmony_ci            [this](const IEntityCollection& ec, const Property& /*property*/, uintptr_t offset,
5268bf80f4bSopenharmony_ci                json::standalone_value& jsonOut) {
5278bf80f4bSopenharmony_ci                return RenderHandleReferenceToJson(
5288bf80f4bSopenharmony_ci                    renderContext_, ec, GetPropertyValue<const RenderHandleReference>(offset), jsonOut);
5298bf80f4bSopenharmony_ci            },
5308bf80f4bSopenharmony_ci            [this](const IEntityCollection& ec, const json::value& jsonIn, const Property& /*property*/,
5318bf80f4bSopenharmony_ci                uintptr_t offset) {
5328bf80f4bSopenharmony_ci                return RenderHandleReferenceFromJson(
5338bf80f4bSopenharmony_ci                    *this, renderContext_, ec, jsonIn, GetPropertyValue<RenderHandleReference>(offset));
5348bf80f4bSopenharmony_ci            }));
5358bf80f4bSopenharmony_ci}
5368bf80f4bSopenharmony_ci
5378bf80f4bSopenharmony_civoid EcsSerializer::SetListener(IListener* listener)
5388bf80f4bSopenharmony_ci{
5398bf80f4bSopenharmony_ci    listener_ = listener;
5408bf80f4bSopenharmony_ci}
5418bf80f4bSopenharmony_ci
5428bf80f4bSopenharmony_civoid EcsSerializer::SetSerializer(const PropertyTypeDecl& type, IPropertySerializer& serializer)
5438bf80f4bSopenharmony_ci{
5448bf80f4bSopenharmony_ci    typetoSerializerMap_[type] = &serializer;
5458bf80f4bSopenharmony_ci}
5468bf80f4bSopenharmony_ci
5478bf80f4bSopenharmony_cibool EcsSerializer::WriteEntityCollection(const IEntityCollection& ec, json::standalone_value& jsonOut) const
5488bf80f4bSopenharmony_ci{
5498bf80f4bSopenharmony_ci    // NOTE: We make sure collections and entities are written in the output before entity-components to make it
5508bf80f4bSopenharmony_ci    // possible to parse the file in one go.
5518bf80f4bSopenharmony_ci
5528bf80f4bSopenharmony_ci    jsonOut = json::standalone_value::object();
5538bf80f4bSopenharmony_ci
5548bf80f4bSopenharmony_ci    string type = ec.GetType();
5558bf80f4bSopenharmony_ci    if (type.empty()) {
5568bf80f4bSopenharmony_ci        type = "entitycollection";
5578bf80f4bSopenharmony_ci    }
5588bf80f4bSopenharmony_ci
5598bf80f4bSopenharmony_ci    const IoUtil::CompatibilityInfo info { VERSION_MAJOR, VERSION_MINOR, type };
5608bf80f4bSopenharmony_ci    if (!IoUtil::WriteCompatibilityInfo(jsonOut, info)) {
5618bf80f4bSopenharmony_ci        return false;
5628bf80f4bSopenharmony_ci    }
5638bf80f4bSopenharmony_ci
5648bf80f4bSopenharmony_ci    if (string rootSrc = ec.GetSrc(); !rootSrc.empty()) {
5658bf80f4bSopenharmony_ci        jsonOut["src"] = move(rootSrc);
5668bf80f4bSopenharmony_ci    }
5678bf80f4bSopenharmony_ci
5688bf80f4bSopenharmony_ci    // Write external collections instanced in this collection.
5698bf80f4bSopenharmony_ci    const auto collectionCount = ec.GetSubCollectionCount();
5708bf80f4bSopenharmony_ci    if (collectionCount > 0) {
5718bf80f4bSopenharmony_ci        json::standalone_value collectionsJson = json::standalone_value::array();
5728bf80f4bSopenharmony_ci        collectionsJson.array_.reserve(collectionCount);
5738bf80f4bSopenharmony_ci
5748bf80f4bSopenharmony_ci        for (size_t i = 0; i < collectionCount; ++i) {
5758bf80f4bSopenharmony_ci            json::standalone_value collectionJson = json::standalone_value::object();
5768bf80f4bSopenharmony_ci            auto* collection = ec.GetSubCollection(i);
5778bf80f4bSopenharmony_ci
5788bf80f4bSopenharmony_ci            if (!collection || collection->IsMarkedDestroyed() || !collection->IsSerialized()) {
5798bf80f4bSopenharmony_ci                // NOTE: Skipping over destroyed collections (same needs to be done when writing entity references).
5808bf80f4bSopenharmony_ci                continue;
5818bf80f4bSopenharmony_ci            }
5828bf80f4bSopenharmony_ci
5838bf80f4bSopenharmony_ci            if (string src = collection->GetSrc(); !src.empty()) {
5848bf80f4bSopenharmony_ci                collectionJson["src"] = move(src);
5858bf80f4bSopenharmony_ci            }
5868bf80f4bSopenharmony_ci            if (string id = collection->GetUri(); !id.empty()) {
5878bf80f4bSopenharmony_ci                collectionJson["id"] = move(id);
5888bf80f4bSopenharmony_ci            }
5898bf80f4bSopenharmony_ci
5908bf80f4bSopenharmony_ci            collectionsJson.array_.emplace_back(move(collectionJson));
5918bf80f4bSopenharmony_ci        }
5928bf80f4bSopenharmony_ci
5938bf80f4bSopenharmony_ci        if (!collectionsJson.array_.empty()) {
5948bf80f4bSopenharmony_ci            jsonOut["collections"] = move(collectionsJson);
5958bf80f4bSopenharmony_ci        }
5968bf80f4bSopenharmony_ci    }
5978bf80f4bSopenharmony_ci
5988bf80f4bSopenharmony_ci    // Write bare entities without components.
5998bf80f4bSopenharmony_ci    const auto entityCount = ec.GetEntityCount();
6008bf80f4bSopenharmony_ci    if (entityCount > 0) {
6018bf80f4bSopenharmony_ci        auto& entitiesJson = (jsonOut["entities"] = json::standalone_value::array());
6028bf80f4bSopenharmony_ci        entitiesJson.array_.reserve(entityCount);
6038bf80f4bSopenharmony_ci        for (size_t i = 0; i < entityCount; ++i) {
6048bf80f4bSopenharmony_ci            json::standalone_value entityJson = json::standalone_value::object();
6058bf80f4bSopenharmony_ci
6068bf80f4bSopenharmony_ci            // Write id if one is defined.
6078bf80f4bSopenharmony_ci            auto entity = ec.GetEntity(i);
6088bf80f4bSopenharmony_ci            const auto& id = ec.GetId(entity);
6098bf80f4bSopenharmony_ci            if (!id.empty()) {
6108bf80f4bSopenharmony_ci                entityJson["id"] = string(id);
6118bf80f4bSopenharmony_ci            }
6128bf80f4bSopenharmony_ci
6138bf80f4bSopenharmony_ci            entitiesJson.array_.emplace_back(move(entityJson));
6148bf80f4bSopenharmony_ci        }
6158bf80f4bSopenharmony_ci    }
6168bf80f4bSopenharmony_ci
6178bf80f4bSopenharmony_ci    vector<EntityReference> allEntities;
6188bf80f4bSopenharmony_ci    ec.GetEntitiesRecursive(false, allEntities, false);
6198bf80f4bSopenharmony_ci    if (allEntities.size() > 0) {
6208bf80f4bSopenharmony_ci        auto& entityComponentsJson = (jsonOut["entity-components"] = json::standalone_value::array());
6218bf80f4bSopenharmony_ci        for (Entity entity : allEntities) {
6228bf80f4bSopenharmony_ci            json::standalone_value componentJson = json::standalone_value::object();
6238bf80f4bSopenharmony_ci            if (WriteComponents(ec, entity, componentJson["components"])) {
6248bf80f4bSopenharmony_ci                json::standalone_value entityRefJson;
6258bf80f4bSopenharmony_ci                if (EntityToJson(ec, entity, entityRefJson)) {
6268bf80f4bSopenharmony_ci                    componentJson["entity"] = move(entityRefJson);
6278bf80f4bSopenharmony_ci                }
6288bf80f4bSopenharmony_ci                entityComponentsJson.array_.emplace_back(move(componentJson));
6298bf80f4bSopenharmony_ci            }
6308bf80f4bSopenharmony_ci        }
6318bf80f4bSopenharmony_ci    }
6328bf80f4bSopenharmony_ci
6338bf80f4bSopenharmony_ci    // NOTE: Always returns true even if parts of the writing failed.
6348bf80f4bSopenharmony_ci    return true;
6358bf80f4bSopenharmony_ci}
6368bf80f4bSopenharmony_ci
6378bf80f4bSopenharmony_cibool EcsSerializer::WriteComponents(const IEntityCollection& ec, Entity entity, json::standalone_value& jsonOut) const
6388bf80f4bSopenharmony_ci{
6398bf80f4bSopenharmony_ci    // Write all entity components to json (Sorting by uid to keep the order more stable).
6408bf80f4bSopenharmony_ci    vector<string> managerUids;
6418bf80f4bSopenharmony_ci    jsonOut = json::standalone_value::object();
6428bf80f4bSopenharmony_ci    auto cms = ec.GetEcs().GetComponentManagers();
6438bf80f4bSopenharmony_ci    for (auto cm : cms) {
6448bf80f4bSopenharmony_ci        const auto componentId = cm->GetComponentId(entity);
6458bf80f4bSopenharmony_ci        if (componentId != IComponentManager::INVALID_COMPONENT_ID) {
6468bf80f4bSopenharmony_ci            auto uidString = to_string(cm->GetUid());
6478bf80f4bSopenharmony_ci            managerUids.emplace_back(string(uidString));
6488bf80f4bSopenharmony_ci        }
6498bf80f4bSopenharmony_ci    }
6508bf80f4bSopenharmony_ci    std::sort(managerUids.begin(), managerUids.end());
6518bf80f4bSopenharmony_ci
6528bf80f4bSopenharmony_ci    for (auto& uidString : managerUids) {
6538bf80f4bSopenharmony_ci        const auto* cm = ec.GetEcs().GetComponentManager(StringToUid(uidString));
6548bf80f4bSopenharmony_ci        const auto componentId = cm->GetComponentId(entity);
6558bf80f4bSopenharmony_ci
6568bf80f4bSopenharmony_ci        json::standalone_value componentJson = json::standalone_value::object();
6578bf80f4bSopenharmony_ci        if (WriteComponent(ec, entity, *cm, componentId, componentJson)) {
6588bf80f4bSopenharmony_ci            jsonOut[uidString] = move(componentJson);
6598bf80f4bSopenharmony_ci        }
6608bf80f4bSopenharmony_ci    }
6618bf80f4bSopenharmony_ci
6628bf80f4bSopenharmony_ci    return (!jsonOut.empty());
6638bf80f4bSopenharmony_ci}
6648bf80f4bSopenharmony_ci
6658bf80f4bSopenharmony_cibool EcsSerializer::WriteComponent(const IEntityCollection& ec, Entity entity, const IComponentManager& cm,
6668bf80f4bSopenharmony_ci    IComponentManager::ComponentId id, json::standalone_value& jsonOut) const
6678bf80f4bSopenharmony_ci{
6688bf80f4bSopenharmony_ci    // Write all properties to json.
6698bf80f4bSopenharmony_ci    json::standalone_value propertiesJson = json::standalone_value::object();
6708bf80f4bSopenharmony_ci    const auto* props = ec.GetSerializedProperties(entity, cm.GetUid());
6718bf80f4bSopenharmony_ci    if (props) {
6728bf80f4bSopenharmony_ci        const auto* propertyHandle = cm.GetData(id);
6738bf80f4bSopenharmony_ci        if (!propertyHandle) {
6748bf80f4bSopenharmony_ci            return false;
6758bf80f4bSopenharmony_ci        }
6768bf80f4bSopenharmony_ci        for (auto& propertyPath : *props) {
6778bf80f4bSopenharmony_ci            const IPropertyHandle* handle = propertyHandle;
6788bf80f4bSopenharmony_ci
6798bf80f4bSopenharmony_ci            PropertyData propertyData;
6808bf80f4bSopenharmony_ci            PropertyData::PropertyOffset propertyOffset;
6818bf80f4bSopenharmony_ci
6828bf80f4bSopenharmony_ci            // Check if this is property container.
6838bf80f4bSopenharmony_ci            string path, name;
6848bf80f4bSopenharmony_ci            auto containerHandle = ResolveContainerProperty(*handle, propertyPath, path, name);
6858bf80f4bSopenharmony_ci            if (containerHandle) {
6868bf80f4bSopenharmony_ci                propertyOffset = propertyData.RLock(*containerHandle, name);
6878bf80f4bSopenharmony_ci            } else {
6888bf80f4bSopenharmony_ci                propertyOffset = propertyData.RLock(*propertyHandle, propertyPath);
6898bf80f4bSopenharmony_ci            }
6908bf80f4bSopenharmony_ci
6918bf80f4bSopenharmony_ci            if (propertyOffset) {
6928bf80f4bSopenharmony_ci                if ((propertyOffset.property->flags & static_cast<uint32_t>(PropertyFlags::NO_SERIALIZE)) != 0) {
6938bf80f4bSopenharmony_ci                    continue;
6948bf80f4bSopenharmony_ci                }
6958bf80f4bSopenharmony_ci
6968bf80f4bSopenharmony_ci                json::standalone_value propertyJson;
6978bf80f4bSopenharmony_ci                if (WriteProperty(ec, *propertyOffset.property, propertyOffset.offset, propertyJson)) {
6988bf80f4bSopenharmony_ci                    if (!propertyJson.is_null()) {
6998bf80f4bSopenharmony_ci                        propertiesJson[propertyPath] = move(propertyJson);
7008bf80f4bSopenharmony_ci                    }
7018bf80f4bSopenharmony_ci                }
7028bf80f4bSopenharmony_ci            }
7038bf80f4bSopenharmony_ci        }
7048bf80f4bSopenharmony_ci
7058bf80f4bSopenharmony_ci        // NOTE: optional name to make reading files easier.
7068bf80f4bSopenharmony_ci        jsonOut["name"] = string(cm.GetName());
7078bf80f4bSopenharmony_ci
7088bf80f4bSopenharmony_ci        if (!propertiesJson.empty()) {
7098bf80f4bSopenharmony_ci            jsonOut["properties"] = move(propertiesJson);
7108bf80f4bSopenharmony_ci        }
7118bf80f4bSopenharmony_ci
7128bf80f4bSopenharmony_ci        // NOTE: Maybe return false if any property write fails?
7138bf80f4bSopenharmony_ci        return true;
7148bf80f4bSopenharmony_ci    }
7158bf80f4bSopenharmony_ci    return false;
7168bf80f4bSopenharmony_ci}
7178bf80f4bSopenharmony_ci
7188bf80f4bSopenharmony_cibool EcsSerializer::WriteProperty(
7198bf80f4bSopenharmony_ci    const IEntityCollection& ec, const Property& property, uintptr_t offset, json::standalone_value& jsonOut) const
7208bf80f4bSopenharmony_ci{
7218bf80f4bSopenharmony_ci    auto serializer = typetoSerializerMap_.find(property.type);
7228bf80f4bSopenharmony_ci    if (serializer != typetoSerializerMap_.end()) {
7238bf80f4bSopenharmony_ci        return serializer->second->ToJson(ec, property, offset, jsonOut);
7248bf80f4bSopenharmony_ci
7258bf80f4bSopenharmony_ci    } else if (!property.metaData.enumMetaData.empty()) {
7268bf80f4bSopenharmony_ci        // Enum type property.
7278bf80f4bSopenharmony_ci        switch (property.size) {
7288bf80f4bSopenharmony_ci            case sizeof(uint8_t):
7298bf80f4bSopenharmony_ci                jsonOut = GetPropertyValue<uint8_t>(offset);
7308bf80f4bSopenharmony_ci                return true;
7318bf80f4bSopenharmony_ci            case sizeof(uint16_t):
7328bf80f4bSopenharmony_ci                jsonOut = GetPropertyValue<uint16_t>(offset);
7338bf80f4bSopenharmony_ci                return true;
7348bf80f4bSopenharmony_ci            case sizeof(uint32_t):
7358bf80f4bSopenharmony_ci                jsonOut = GetPropertyValue<uint32_t>(offset);
7368bf80f4bSopenharmony_ci                return true;
7378bf80f4bSopenharmony_ci            case sizeof(uint64_t):
7388bf80f4bSopenharmony_ci                jsonOut = GetPropertyValue<uint64_t>(offset);
7398bf80f4bSopenharmony_ci                return true;
7408bf80f4bSopenharmony_ci        }
7418bf80f4bSopenharmony_ci
7428bf80f4bSopenharmony_ci    } else if (property.metaData.containerMethods) {
7438bf80f4bSopenharmony_ci        const auto& container = *property.metaData.containerMethods;
7448bf80f4bSopenharmony_ci
7458bf80f4bSopenharmony_ci        // Container type property.
7468bf80f4bSopenharmony_ci        if (property.type.isArray) {
7478bf80f4bSopenharmony_ci            // Special handling for byte arrays. Save as a base64 encoded string.
7488bf80f4bSopenharmony_ci            if (property.type == PropertyType::UINT8_ARRAY_T) {
7498bf80f4bSopenharmony_ci                array_view<const uint8_t> bytes { (const uint8_t*)offset, property.size };
7508bf80f4bSopenharmony_ci                jsonOut = BASE_NS::Base64Encode(bytes);
7518bf80f4bSopenharmony_ci                return true;
7528bf80f4bSopenharmony_ci            }
7538bf80f4bSopenharmony_ci
7548bf80f4bSopenharmony_ci            // C style array.
7558bf80f4bSopenharmony_ci            json::standalone_value array = json::standalone_value::array();
7568bf80f4bSopenharmony_ci            array.array_.reserve(property.count);
7578bf80f4bSopenharmony_ci            for (size_t i = 0; i < property.count; i++) {
7588bf80f4bSopenharmony_ci                uintptr_t ptr = offset + i * container.property.size;
7598bf80f4bSopenharmony_ci                // TODO: return false if any recurseive call fails?
7608bf80f4bSopenharmony_ci                json::standalone_value elementJson;
7618bf80f4bSopenharmony_ci                WriteProperty(ec, container.property, ptr, elementJson);
7628bf80f4bSopenharmony_ci                array.array_.push_back(move(elementJson));
7638bf80f4bSopenharmony_ci            }
7648bf80f4bSopenharmony_ci            jsonOut = move(array);
7658bf80f4bSopenharmony_ci            return true;
7668bf80f4bSopenharmony_ci
7678bf80f4bSopenharmony_ci        } else {
7688bf80f4bSopenharmony_ci            // This is a "non trivial container".
7698bf80f4bSopenharmony_ci            const auto count = container.size(offset);
7708bf80f4bSopenharmony_ci
7718bf80f4bSopenharmony_ci            // Special handling for byte arrays. Save as a base64 encoded string.
7728bf80f4bSopenharmony_ci            // NOTE: Only specifically vector so we can assume that the memory usage is linear.
7738bf80f4bSopenharmony_ci            if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
7748bf80f4bSopenharmony_ci                if (count == 0) {
7758bf80f4bSopenharmony_ci                    jsonOut = "";
7768bf80f4bSopenharmony_ci                } else {
7778bf80f4bSopenharmony_ci                    auto data = container.get(offset, 0);
7788bf80f4bSopenharmony_ci                    array_view<const uint8_t> bytes { reinterpret_cast<const uint8_t*>(data), count };
7798bf80f4bSopenharmony_ci                    jsonOut = BASE_NS::Base64Encode(bytes);
7808bf80f4bSopenharmony_ci                }
7818bf80f4bSopenharmony_ci                return true;
7828bf80f4bSopenharmony_ci            }
7838bf80f4bSopenharmony_ci
7848bf80f4bSopenharmony_ci            json::standalone_value array = json::standalone_value::array();
7858bf80f4bSopenharmony_ci            array.array_.reserve(count);
7868bf80f4bSopenharmony_ci
7878bf80f4bSopenharmony_ci            for (size_t i = 0; i < count; i++) {
7888bf80f4bSopenharmony_ci                uintptr_t ptr = container.get(offset, i);
7898bf80f4bSopenharmony_ci                // TODO: return false if any recurseive call fails?
7908bf80f4bSopenharmony_ci                json::standalone_value elementJson;
7918bf80f4bSopenharmony_ci                WriteProperty(ec, container.property, ptr, elementJson);
7928bf80f4bSopenharmony_ci                array.array_.push_back(move(elementJson));
7938bf80f4bSopenharmony_ci            }
7948bf80f4bSopenharmony_ci            jsonOut = move(array);
7958bf80f4bSopenharmony_ci            return true;
7968bf80f4bSopenharmony_ci        }
7978bf80f4bSopenharmony_ci
7988bf80f4bSopenharmony_ci    } else if (!property.metaData.memberProperties.empty()) {
7998bf80f4bSopenharmony_ci        // Struct type property (ie. has sub properties).
8008bf80f4bSopenharmony_ci        json::standalone_value object = json::standalone_value::object();
8018bf80f4bSopenharmony_ci        for (const auto& subProperty : property.metaData.memberProperties) {
8028bf80f4bSopenharmony_ci            json::standalone_value subPropertyJson;
8038bf80f4bSopenharmony_ci            if (WriteProperty(ec, subProperty, offset + subProperty.offset, subPropertyJson)) {
8048bf80f4bSopenharmony_ci                object[subProperty.name] = move(subPropertyJson);
8058bf80f4bSopenharmony_ci            }
8068bf80f4bSopenharmony_ci        }
8078bf80f4bSopenharmony_ci        // TODO: return false if any recurseive call fails?
8088bf80f4bSopenharmony_ci        jsonOut = move(object);
8098bf80f4bSopenharmony_ci        return true;
8108bf80f4bSopenharmony_ci
8118bf80f4bSopenharmony_ci    } else {
8128bf80f4bSopenharmony_ci        CORE_LOG_V("Ecs serializer not found for '%s'", string(property.type.name).c_str());
8138bf80f4bSopenharmony_ci    }
8148bf80f4bSopenharmony_ci    return false;
8158bf80f4bSopenharmony_ci}
8168bf80f4bSopenharmony_ci
8178bf80f4bSopenharmony_cibool EcsSerializer::GatherExternalCollections(
8188bf80f4bSopenharmony_ci    const json::value& jsonIn, string_view contextUri, vector<ExternalCollection>& externalCollectionsOut) const
8198bf80f4bSopenharmony_ci{
8208bf80f4bSopenharmony_ci    const auto* srcJson = jsonIn.find("src");
8218bf80f4bSopenharmony_ci    string srcUri;
8228bf80f4bSopenharmony_ci    if (srcJson && FromJson(*srcJson, srcUri)) {
8238bf80f4bSopenharmony_ci#ifdef VERBOSE_LOGGING
8248bf80f4bSopenharmony_ci        SCENE_PLUGIN_VERBOSE_LOG(
8258bf80f4bSopenharmony_ci            "External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
8268bf80f4bSopenharmony_ci#endif
8278bf80f4bSopenharmony_ci        externalCollectionsOut.emplace_back(ExternalCollection { srcUri, string { contextUri } });
8288bf80f4bSopenharmony_ci    }
8298bf80f4bSopenharmony_ci
8308bf80f4bSopenharmony_ci    const auto* collectionsJson = jsonIn.find("collections");
8318bf80f4bSopenharmony_ci    if (collectionsJson && collectionsJson->is_array()) {
8328bf80f4bSopenharmony_ci        for (const auto& collectionJson : collectionsJson->array_) {
8338bf80f4bSopenharmony_ci            if (collectionJson.is_object()) {
8348bf80f4bSopenharmony_ci                const auto* collectionSrcJson = collectionJson.find("src");
8358bf80f4bSopenharmony_ci                if (collectionSrcJson && collectionSrcJson->is_string()) {
8368bf80f4bSopenharmony_ci                    string collectionSrcUri;
8378bf80f4bSopenharmony_ci                    FromJson(*collectionSrcJson, collectionSrcUri);
8388bf80f4bSopenharmony_ci                    externalCollectionsOut.emplace_back(ExternalCollection { collectionSrcUri, string { contextUri } });
8398bf80f4bSopenharmony_ci                }
8408bf80f4bSopenharmony_ci            }
8418bf80f4bSopenharmony_ci        }
8428bf80f4bSopenharmony_ci    }
8438bf80f4bSopenharmony_ci    return true;
8448bf80f4bSopenharmony_ci}
8458bf80f4bSopenharmony_ci
8468bf80f4bSopenharmony_ci/*IIoUtil::SerializationResult*/ int EcsSerializer::ReadEntityCollection(
8478bf80f4bSopenharmony_ci    IEntityCollection& ec, const json::value& jsonIn, string_view contextUri) const
8488bf80f4bSopenharmony_ci{
8498bf80f4bSopenharmony_ci    // TODO: Move version check to be separately so it can be done before gathering the dependencies.
8508bf80f4bSopenharmony_ci    // NOTE: Only comparing the major version.
8518bf80f4bSopenharmony_ci    const auto minor = IoUtil::CompatibilityRange::IGNORE_VERSION;
8528bf80f4bSopenharmony_ci    // TODO: Type name was changed to be in line with engine naming. Allow the old type name for a while.
8538bf80f4bSopenharmony_ci    const IoUtil::CompatibilityRange validVersions[] {
8548bf80f4bSopenharmony_ci        { VERSION_MAJOR, VERSION_MAJOR, minor, minor, ec.GetType() },
8558bf80f4bSopenharmony_ci        { VERSION_MAJOR, VERSION_MAJOR, minor, minor, "entity_collection" },
8568bf80f4bSopenharmony_ci    };
8578bf80f4bSopenharmony_ci
8588bf80f4bSopenharmony_ci    auto result = IoUtil::CheckCompatibility(jsonIn, validVersions);
8598bf80f4bSopenharmony_ci    if (!result) {
8608bf80f4bSopenharmony_ci        CORE_LOG_W("Deprecated compatibility info found \"entity_collection\": '%s'", string(contextUri).c_str());
8618bf80f4bSopenharmony_ci    }
8628bf80f4bSopenharmony_ci
8638bf80f4bSopenharmony_ci    const auto* srcJson = jsonIn.find("src");
8648bf80f4bSopenharmony_ci    string srcUri;
8658bf80f4bSopenharmony_ci    if (srcJson && FromJson(*srcJson, srcUri)) {
8668bf80f4bSopenharmony_ci#ifdef VERBOSE_LOGGING
8678bf80f4bSopenharmony_ci        SCENE_PLUGIN_VERBOSE_LOG(
8688bf80f4bSopenharmony_ci            "External Collection: uri='%s' context='%s'", srcUri.c_str(), string(contextUri).c_str());
8698bf80f4bSopenharmony_ci#endif
8708bf80f4bSopenharmony_ci        auto* externalCollection = listener_->GetExternalCollection(ec.GetEcs(), srcUri, contextUri);
8718bf80f4bSopenharmony_ci        if (externalCollection) {
8728bf80f4bSopenharmony_ci            ec.CopyContents(*externalCollection);
8738bf80f4bSopenharmony_ci        }
8748bf80f4bSopenharmony_ci    }
8758bf80f4bSopenharmony_ci
8768bf80f4bSopenharmony_ci    const auto* collectionsJson = jsonIn.find("collections");
8778bf80f4bSopenharmony_ci    if (collectionsJson && collectionsJson->is_array()) {
8788bf80f4bSopenharmony_ci        for (const auto& collectionJson : collectionsJson->array_) {
8798bf80f4bSopenharmony_ci            if (collectionJson.is_object()) {
8808bf80f4bSopenharmony_ci                string id;
8818bf80f4bSopenharmony_ci                const auto* idJson = collectionJson.find("id");
8828bf80f4bSopenharmony_ci                if (idJson) {
8838bf80f4bSopenharmony_ci                    FromJson(*idJson, id);
8848bf80f4bSopenharmony_ci                }
8858bf80f4bSopenharmony_ci
8868bf80f4bSopenharmony_ci                const auto* collectionSrcJson = collectionJson.find("src");
8878bf80f4bSopenharmony_ci                if (collectionSrcJson && collectionSrcJson->is_string()) {
8888bf80f4bSopenharmony_ci                    string collectionSrcUri;
8898bf80f4bSopenharmony_ci                    FromJson(*collectionSrcJson, collectionSrcUri);
8908bf80f4bSopenharmony_ci
8918bf80f4bSopenharmony_ci                    // Instantiate the collection pointed by src.
8928bf80f4bSopenharmony_ci#ifdef VERBOSE_LOGGING
8938bf80f4bSopenharmony_ci                    SCENE_PLUGIN_VERBOSE_LOG("External Collection: uri='%s' context='%s'", collectionSrcUri.c_str(),
8948bf80f4bSopenharmony_ci                        string(contextUri).c_str());
8958bf80f4bSopenharmony_ci#endif
8968bf80f4bSopenharmony_ci                    auto* externalCollection =
8978bf80f4bSopenharmony_ci                        listener_->GetExternalCollection(ec.GetEcs(), collectionSrcUri, contextUri);
8988bf80f4bSopenharmony_ci                    if (externalCollection) {
8998bf80f4bSopenharmony_ci                        ec.AddSubCollectionClone(*externalCollection, id).SetSrc(collectionSrcUri);
9008bf80f4bSopenharmony_ci                        ;
9018bf80f4bSopenharmony_ci                    } else {
9028bf80f4bSopenharmony_ci                        CORE_LOG_E("Loading collection failed: '%s'", collectionSrcUri.c_str());
9038bf80f4bSopenharmony_ci                        // Just adding an empty collection as a placeholder.
9048bf80f4bSopenharmony_ci                        ec.AddSubCollection(id, collectionSrcUri).SetSrc(collectionSrcUri);
9058bf80f4bSopenharmony_ci                    }
9068bf80f4bSopenharmony_ci                } else {
9078bf80f4bSopenharmony_ci                    ec.AddSubCollection(id, {});
9088bf80f4bSopenharmony_ci                }
9098bf80f4bSopenharmony_ci            }
9108bf80f4bSopenharmony_ci        }
9118bf80f4bSopenharmony_ci    }
9128bf80f4bSopenharmony_ci
9138bf80f4bSopenharmony_ci    const auto* entitiesJson = jsonIn.find("entities");
9148bf80f4bSopenharmony_ci    bool idSet { false };
9158bf80f4bSopenharmony_ci    if (entitiesJson && entitiesJson->is_array()) {
9168bf80f4bSopenharmony_ci        auto& em = ec.GetEcs().GetEntityManager();
9178bf80f4bSopenharmony_ci
9188bf80f4bSopenharmony_ci        // First create all entities so they can be referenced by components.
9198bf80f4bSopenharmony_ci        for (const auto& entityJson : entitiesJson->array_) {
9208bf80f4bSopenharmony_ci            // Create a new entity.
9218bf80f4bSopenharmony_ci            EntityReference entity = em.CreateReferenceCounted();
9228bf80f4bSopenharmony_ci            ec.AddEntity(entity);
9238bf80f4bSopenharmony_ci
9248bf80f4bSopenharmony_ci            // Add with an id if one is defined.
9258bf80f4bSopenharmony_ci            const auto* idJson = entityJson.find("id");
9268bf80f4bSopenharmony_ci            string id;
9278bf80f4bSopenharmony_ci            if (idJson && FromJson(*idJson, id)) {
9288bf80f4bSopenharmony_ci                ec.SetId(id, entity);
9298bf80f4bSopenharmony_ci                idSet = true;
9308bf80f4bSopenharmony_ci            }
9318bf80f4bSopenharmony_ci        }
9328bf80f4bSopenharmony_ci    }
9338bf80f4bSopenharmony_ci
9348bf80f4bSopenharmony_ci    const auto* ecJson = jsonIn.find("entity-components");
9358bf80f4bSopenharmony_ci    if (ecJson && ecJson->is_array()) {
9368bf80f4bSopenharmony_ci        // Then load entity contents (i.e. components).
9378bf80f4bSopenharmony_ci        for (size_t i = 0; i < ecJson->array_.size(); ++i) {
9388bf80f4bSopenharmony_ci            ReadComponents(ec, ecJson->array_.at(i), !idSet);
9398bf80f4bSopenharmony_ci        }
9408bf80f4bSopenharmony_ci    }
9418bf80f4bSopenharmony_ci
9428bf80f4bSopenharmony_ci    if (!ec.IsActive()) {
9438bf80f4bSopenharmony_ci        // If the ec is not active also make the newly loaded entities not active.
9448bf80f4bSopenharmony_ci        ec.SetActive(false);
9458bf80f4bSopenharmony_ci    }
9468bf80f4bSopenharmony_ci
9478bf80f4bSopenharmony_ci    // NOTE: Always returns success, even if parts of the load failed.
9488bf80f4bSopenharmony_ci    return 1; // result;
9498bf80f4bSopenharmony_ci}
9508bf80f4bSopenharmony_ci
9518bf80f4bSopenharmony_cibool EcsSerializer::ReadComponents(IEntityCollection& ec, const json::value& jsonIn, bool setId) const
9528bf80f4bSopenharmony_ci{
9538bf80f4bSopenharmony_ci    // Figure out to which entity these components belong to.
9548bf80f4bSopenharmony_ci    const auto* entityJson = jsonIn.find("entity");
9558bf80f4bSopenharmony_ci    if (!entityJson) {
9568bf80f4bSopenharmony_ci        CORE_LOG_W("No entity defined for a component.");
9578bf80f4bSopenharmony_ci        return false;
9588bf80f4bSopenharmony_ci    }
9598bf80f4bSopenharmony_ci
9608bf80f4bSopenharmony_ci    Entity entity {};
9618bf80f4bSopenharmony_ci
9628bf80f4bSopenharmony_ci    if (!EntityFromJson(ec, *entityJson, entity)) {
9638bf80f4bSopenharmony_ci        return false;
9648bf80f4bSopenharmony_ci    }
9658bf80f4bSopenharmony_ci
9668bf80f4bSopenharmony_ci    auto& ecs = ec.GetEcs();
9678bf80f4bSopenharmony_ci    const auto* componentsJson = jsonIn.find("components");
9688bf80f4bSopenharmony_ci    if (componentsJson) {
9698bf80f4bSopenharmony_ci        // Read all entity components from json.
9708bf80f4bSopenharmony_ci        for (auto& component : componentsJson->object_) {
9718bf80f4bSopenharmony_ci            auto& key = component.key;
9728bf80f4bSopenharmony_ci            const auto componentUid = StringToUid(key);
9738bf80f4bSopenharmony_ci
9748bf80f4bSopenharmony_ci            auto& componentJson = component.value;
9758bf80f4bSopenharmony_ci            auto* cm = ecs.GetComponentManager(componentUid);
9768bf80f4bSopenharmony_ci            if (cm) {
9778bf80f4bSopenharmony_ci                ReadComponent(ec, componentJson, entity, *cm);
9788bf80f4bSopenharmony_ci                if (setId && cm->GetName() == "NameComponent") {
9798bf80f4bSopenharmony_ci                    if (auto handle = reinterpret_cast<CORE3D_NS::INameComponentManager*>(cm)->Read(entity)) {
9808bf80f4bSopenharmony_ci                        for (auto& ref : ec.GetEntities()) {
9818bf80f4bSopenharmony_ci                            if (ref == entity) {
9828bf80f4bSopenharmony_ci                                BASE_NS::string compound = handle->name;
9838bf80f4bSopenharmony_ci                                compound.append(":");
9848bf80f4bSopenharmony_ci                                compound.append(BASE_NS::to_hex(entity.id));
9858bf80f4bSopenharmony_ci
9868bf80f4bSopenharmony_ci                                ec.SetUniqueIdentifier(compound, ref);
9878bf80f4bSopenharmony_ci                                break;
9888bf80f4bSopenharmony_ci                            }
9898bf80f4bSopenharmony_ci                        }
9908bf80f4bSopenharmony_ci                    }
9918bf80f4bSopenharmony_ci                }
9928bf80f4bSopenharmony_ci            } else {
9938bf80f4bSopenharmony_ci                // TODO: Maybe we should try to find a matching component by name as a fallback
9948bf80f4bSopenharmony_ci                CORE_LOG_W("Unrecognized component found: '%s'", string(key).c_str());
9958bf80f4bSopenharmony_ci            }
9968bf80f4bSopenharmony_ci        }
9978bf80f4bSopenharmony_ci    }
9988bf80f4bSopenharmony_ci
9998bf80f4bSopenharmony_ci    return true;
10008bf80f4bSopenharmony_ci}
10018bf80f4bSopenharmony_ci
10028bf80f4bSopenharmony_cibool EcsSerializer::ReadComponent(
10038bf80f4bSopenharmony_ci    IEntityCollection& ec, const json::value& jsonIn, Entity entity, IComponentManager& component) const
10048bf80f4bSopenharmony_ci{
10058bf80f4bSopenharmony_ci    // Create the component if it does not exist yet.
10068bf80f4bSopenharmony_ci    auto componentId = component.GetComponentId(entity);
10078bf80f4bSopenharmony_ci    if (componentId == IComponentManager::INVALID_COMPONENT_ID) {
10088bf80f4bSopenharmony_ci        component.Create(entity);
10098bf80f4bSopenharmony_ci        componentId = component.GetComponentId(entity);
10108bf80f4bSopenharmony_ci    }
10118bf80f4bSopenharmony_ci
10128bf80f4bSopenharmony_ci    ec.MarkComponentSerialized(entity, component.GetUid(), true);
10138bf80f4bSopenharmony_ci
10148bf80f4bSopenharmony_ci    const auto* propertiesJson = jsonIn.find("properties");
10158bf80f4bSopenharmony_ci    if (!propertiesJson || propertiesJson->type != json::type::object) {
10168bf80f4bSopenharmony_ci        // No properties.
10178bf80f4bSopenharmony_ci        return true;
10188bf80f4bSopenharmony_ci    }
10198bf80f4bSopenharmony_ci
10208bf80f4bSopenharmony_ci    auto* propertyHandle = component.GetData(componentId);
10218bf80f4bSopenharmony_ci    if (!propertyHandle) {
10228bf80f4bSopenharmony_ci        return false;
10238bf80f4bSopenharmony_ci    }
10248bf80f4bSopenharmony_ci
10258bf80f4bSopenharmony_ci    for (auto& propertyJson : propertiesJson->object_) {
10268bf80f4bSopenharmony_ci        const auto& propertyPath = propertyJson.key;
10278bf80f4bSopenharmony_ci        const auto& propertyValueJson = propertyJson.value;
10288bf80f4bSopenharmony_ci        auto pathView = string_view(propertyPath.data(), propertyPath.size());
10298bf80f4bSopenharmony_ci
10308bf80f4bSopenharmony_ci        // Find the property using the propertyName
10318bf80f4bSopenharmony_ci        {
10328bf80f4bSopenharmony_ci            const IPropertyHandle* handle = propertyHandle;
10338bf80f4bSopenharmony_ci            PropertyData propertyData;
10348bf80f4bSopenharmony_ci            PropertyData::PropertyOffset propertyOffset;
10358bf80f4bSopenharmony_ci
10368bf80f4bSopenharmony_ci            // Check if this is property container.
10378bf80f4bSopenharmony_ci            string path, name;
10388bf80f4bSopenharmony_ci            auto containerHandle = ResolveContainerProperty(*handle, string(pathView), path, name);
10398bf80f4bSopenharmony_ci            if (containerHandle) {
10408bf80f4bSopenharmony_ci                propertyOffset = propertyData.WLock(*containerHandle, name);
10418bf80f4bSopenharmony_ci            } else {
10428bf80f4bSopenharmony_ci                // We can only ask for the property if we first make sure it exists (we may be referencing a dynamic
10438bf80f4bSopenharmony_ci                // array).
10448bf80f4bSopenharmony_ci                EnsureDynamicArraySize(propertyHandle, pathView);
10458bf80f4bSopenharmony_ci
10468bf80f4bSopenharmony_ci                propertyOffset = propertyData.WLock(*propertyHandle, pathView);
10478bf80f4bSopenharmony_ci            }
10488bf80f4bSopenharmony_ci
10498bf80f4bSopenharmony_ci            if (propertyOffset) {
10508bf80f4bSopenharmony_ci                if (ReadProperty(ec, propertyValueJson, *propertyOffset.property, propertyOffset.offset)) {
10518bf80f4bSopenharmony_ci                    // Mark this property value as serialized (instead of being a cloned from a prototype entity).
10528bf80f4bSopenharmony_ci                    ec.MarkPropertySerialized(entity, component.GetUid(), pathView, true);
10538bf80f4bSopenharmony_ci                } else {
10548bf80f4bSopenharmony_ci                    CORE_LOG_W("Unrecognized property: Component: '%s' Property: '%s'", component.GetName().data(),
10558bf80f4bSopenharmony_ci                        string(pathView).c_str());
10568bf80f4bSopenharmony_ci                }
10578bf80f4bSopenharmony_ci            }
10588bf80f4bSopenharmony_ci        }
10598bf80f4bSopenharmony_ci    }
10608bf80f4bSopenharmony_ci    return true;
10618bf80f4bSopenharmony_ci}
10628bf80f4bSopenharmony_ci
10638bf80f4bSopenharmony_cibool EcsSerializer::ReadProperty(
10648bf80f4bSopenharmony_ci    IEntityCollection& ec, const json::value& jsonIn, const Property& property, uintptr_t offset) const
10658bf80f4bSopenharmony_ci{
10668bf80f4bSopenharmony_ci    // See if there is a serializer for this type. Otherwise recurse further.
10678bf80f4bSopenharmony_ci    auto serializer = typetoSerializerMap_.find(property.type);
10688bf80f4bSopenharmony_ci    if (serializer != typetoSerializerMap_.end()) {
10698bf80f4bSopenharmony_ci        return serializer->second->FromJson(ec, jsonIn, property, offset);
10708bf80f4bSopenharmony_ci
10718bf80f4bSopenharmony_ci    } else if (!property.metaData.enumMetaData.empty()) {
10728bf80f4bSopenharmony_ci        // Enum type property.
10738bf80f4bSopenharmony_ci        if (jsonIn.is_unsigned_int()) {
10748bf80f4bSopenharmony_ci            switch (property.size) {
10758bf80f4bSopenharmony_ci                case sizeof(uint8_t):
10768bf80f4bSopenharmony_ci                    GetPropertyValue<uint8_t>(offset) = static_cast<uint8_t>(jsonIn.unsigned_);
10778bf80f4bSopenharmony_ci                    return true;
10788bf80f4bSopenharmony_ci                case sizeof(uint16_t):
10798bf80f4bSopenharmony_ci                    GetPropertyValue<uint16_t>(offset) = static_cast<uint16_t>(jsonIn.unsigned_);
10808bf80f4bSopenharmony_ci                    return true;
10818bf80f4bSopenharmony_ci                case sizeof(uint32_t):
10828bf80f4bSopenharmony_ci                    GetPropertyValue<uint32_t>(offset) = static_cast<uint32_t>(jsonIn.unsigned_);
10838bf80f4bSopenharmony_ci                    return true;
10848bf80f4bSopenharmony_ci                case sizeof(uint64_t):
10858bf80f4bSopenharmony_ci                    GetPropertyValue<uint64_t>(offset) = static_cast<uint64_t>(jsonIn.unsigned_);
10868bf80f4bSopenharmony_ci                    return true;
10878bf80f4bSopenharmony_ci            }
10888bf80f4bSopenharmony_ci        }
10898bf80f4bSopenharmony_ci
10908bf80f4bSopenharmony_ci    } else if (property.metaData.containerMethods) {
10918bf80f4bSopenharmony_ci        // Special handling for byte data encoded as base64.
10928bf80f4bSopenharmony_ci        if (jsonIn.is_string()) {
10938bf80f4bSopenharmony_ci            // A base64 encoded string containing raw array data
10948bf80f4bSopenharmony_ci            auto bytes = BASE_NS::Base64Decode(jsonIn.string_);
10958bf80f4bSopenharmony_ci
10968bf80f4bSopenharmony_ci            // Only valid for byte data.
10978bf80f4bSopenharmony_ci            if (property.type == PropertyType::UINT8_ARRAY_T) {
10988bf80f4bSopenharmony_ci                if (property.size != bytes.size()) {
10998bf80f4bSopenharmony_ci                    CORE_LOG_W("Invalid base64 data size in: %s", string(property.name).c_str());
11008bf80f4bSopenharmony_ci                    return false;
11018bf80f4bSopenharmony_ci                }
11028bf80f4bSopenharmony_ci                CloneData(GetPropertyValue<uint8_t*>(offset), property.size, &bytes[0], bytes.size());
11038bf80f4bSopenharmony_ci                return true;
11048bf80f4bSopenharmony_ci
11058bf80f4bSopenharmony_ci            } else if (property.type == PROPERTYTYPE(vector<uint8_t>)) {
11068bf80f4bSopenharmony_ci                GetPropertyValue<vector<uint8_t>>(offset).swap(bytes);
11078bf80f4bSopenharmony_ci                return true;
11088bf80f4bSopenharmony_ci            }
11098bf80f4bSopenharmony_ci            return false;
11108bf80f4bSopenharmony_ci        }
11118bf80f4bSopenharmony_ci
11128bf80f4bSopenharmony_ci        // Container type property.
11138bf80f4bSopenharmony_ci        if (property.type.isArray) {
11148bf80f4bSopenharmony_ci            // C style array.
11158bf80f4bSopenharmony_ci            if (jsonIn.is_array()) {
11168bf80f4bSopenharmony_ci                if (jsonIn.array_.size() != property.count) {
11178bf80f4bSopenharmony_ci                    CORE_LOG_W("Expecting a json array of size %zu", property.count);
11188bf80f4bSopenharmony_ci                    return false;
11198bf80f4bSopenharmony_ci                }
11208bf80f4bSopenharmony_ci                for (size_t i = 0; i < property.count; i++) {
11218bf80f4bSopenharmony_ci                    uintptr_t ptr = offset + i * property.metaData.containerMethods->property.size;
11228bf80f4bSopenharmony_ci                    // TODO: return false if any recurseive call fails?
11238bf80f4bSopenharmony_ci                    ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
11248bf80f4bSopenharmony_ci                }
11258bf80f4bSopenharmony_ci                return true;
11268bf80f4bSopenharmony_ci
11278bf80f4bSopenharmony_ci            } else if (jsonIn.is_object()) {
11288bf80f4bSopenharmony_ci                // Allow "sparse arrays" by using objects with the array index as the key.
11298bf80f4bSopenharmony_ci                for (auto& element : jsonIn.object_) {
11308bf80f4bSopenharmony_ci                    const auto& key = element.key;
11318bf80f4bSopenharmony_ci                    const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
11328bf80f4bSopenharmony_ci                    if ((index == 0 && key != "0") || index >= property.count) {
11338bf80f4bSopenharmony_ci                        CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
11348bf80f4bSopenharmony_ci                        continue;
11358bf80f4bSopenharmony_ci                    }
11368bf80f4bSopenharmony_ci                    uintptr_t ptr = offset + index * property.metaData.containerMethods->property.size;
11378bf80f4bSopenharmony_ci                    ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
11388bf80f4bSopenharmony_ci                }
11398bf80f4bSopenharmony_ci                return true;
11408bf80f4bSopenharmony_ci            }
11418bf80f4bSopenharmony_ci            return false;
11428bf80f4bSopenharmony_ci
11438bf80f4bSopenharmony_ci        } else {
11448bf80f4bSopenharmony_ci            // This is a "non trivial container".
11458bf80f4bSopenharmony_ci            if (jsonIn.is_array()) {
11468bf80f4bSopenharmony_ci                const auto count = jsonIn.array_.size();
11478bf80f4bSopenharmony_ci                property.metaData.containerMethods->resize(offset, count);
11488bf80f4bSopenharmony_ci                for (size_t i = 0; i < count; i++) {
11498bf80f4bSopenharmony_ci                    uintptr_t ptr = property.metaData.containerMethods->get(offset, i);
11508bf80f4bSopenharmony_ci                    ReadProperty(ec, jsonIn.array_.at(i), property.metaData.containerMethods->property, ptr);
11518bf80f4bSopenharmony_ci                }
11528bf80f4bSopenharmony_ci                return true;
11538bf80f4bSopenharmony_ci
11548bf80f4bSopenharmony_ci            } else if (jsonIn.is_object()) {
11558bf80f4bSopenharmony_ci                // Allow "sparse arrays" by using objects with the array index as the key.
11568bf80f4bSopenharmony_ci                for (auto& element : jsonIn.object_) {
11578bf80f4bSopenharmony_ci                    const auto& key = element.key;
11588bf80f4bSopenharmony_ci                    const auto index = static_cast<size_t>(strtol(string(key).c_str(), nullptr, 10));
11598bf80f4bSopenharmony_ci                    if ((index == 0 && key != "0")) {
11608bf80f4bSopenharmony_ci                        CORE_LOG_W("Invalid array Index: %s", string(key).c_str());
11618bf80f4bSopenharmony_ci                        continue;
11628bf80f4bSopenharmony_ci                    }
11638bf80f4bSopenharmony_ci
11648bf80f4bSopenharmony_ci                    const auto count = property.metaData.containerMethods->size(offset);
11658bf80f4bSopenharmony_ci                    if (count <= index) {
11668bf80f4bSopenharmony_ci                        property.metaData.containerMethods->resize(offset, index + 1);
11678bf80f4bSopenharmony_ci                    }
11688bf80f4bSopenharmony_ci                    uintptr_t ptr = property.metaData.containerMethods->get(offset, index);
11698bf80f4bSopenharmony_ci                    ReadProperty(ec, element.value, property.metaData.containerMethods->property, ptr);
11708bf80f4bSopenharmony_ci                }
11718bf80f4bSopenharmony_ci                return true;
11728bf80f4bSopenharmony_ci            }
11738bf80f4bSopenharmony_ci            return false;
11748bf80f4bSopenharmony_ci        }
11758bf80f4bSopenharmony_ci
11768bf80f4bSopenharmony_ci    } else if (!property.metaData.memberProperties.empty()) {
11778bf80f4bSopenharmony_ci        // Struct type property (ie. has sub properties).
11788bf80f4bSopenharmony_ci        if (jsonIn.is_object()) {
11798bf80f4bSopenharmony_ci            for (const auto& subProperty : property.metaData.memberProperties) {
11808bf80f4bSopenharmony_ci                // TODO: is there a way to not create the string
11818bf80f4bSopenharmony_ci                const string name(subProperty.name);
11828bf80f4bSopenharmony_ci                const auto* subJson = jsonIn.find(name);
11838bf80f4bSopenharmony_ci                if (subJson) {
11848bf80f4bSopenharmony_ci                    ReadProperty(ec, *subJson, subProperty, offset + subProperty.offset);
11858bf80f4bSopenharmony_ci                }
11868bf80f4bSopenharmony_ci            }
11878bf80f4bSopenharmony_ci            // TODO: return false if any recurseive call fails?
11888bf80f4bSopenharmony_ci            return true;
11898bf80f4bSopenharmony_ci        }
11908bf80f4bSopenharmony_ci    }
11918bf80f4bSopenharmony_ci
11928bf80f4bSopenharmony_ci    return false;
11938bf80f4bSopenharmony_ci}
11948bf80f4bSopenharmony_ci
11958bf80f4bSopenharmony_ciRENDER_NS::RenderHandleReference EcsSerializer::LoadImageResource(BASE_NS::string_view uri) const
11968bf80f4bSopenharmony_ci{
11978bf80f4bSopenharmony_ci    // Get rid of a possible query string in the uri.
11988bf80f4bSopenharmony_ci    const auto fileUri = PathUtil::ResolvePath({}, uri, false);
11998bf80f4bSopenharmony_ci
12008bf80f4bSopenharmony_ci    auto params = PathUtil::GetUriParameters(uri);
12018bf80f4bSopenharmony_ci
12028bf80f4bSopenharmony_ci    // Image loading flags can be passed in the uri query string.
12038bf80f4bSopenharmony_ci    uint64_t imageLoaderFlags {};
12048bf80f4bSopenharmony_ci    auto loaderFlags = params.find("loaderFlags");
12058bf80f4bSopenharmony_ci    if (loaderFlags != params.end()) {
12068bf80f4bSopenharmony_ci        const char* start = loaderFlags->second.data();
12078bf80f4bSopenharmony_ci        const char* end = start + loaderFlags->second.size();
12088bf80f4bSopenharmony_ci        std::from_chars(start, end, imageLoaderFlags);
12098bf80f4bSopenharmony_ci    }
12108bf80f4bSopenharmony_ci
12118bf80f4bSopenharmony_ci    auto imageLoadResult = renderContext_.GetEngine().GetImageLoaderManager().LoadImage(fileUri, imageLoaderFlags);
12128bf80f4bSopenharmony_ci
12138bf80f4bSopenharmony_ci    RenderHandleReference imageHandle {};
12148bf80f4bSopenharmony_ci    if (!imageLoadResult.success) {
12158bf80f4bSopenharmony_ci        CORE_LOG_E("Could not load image asset: %s", imageLoadResult.error);
12168bf80f4bSopenharmony_ci
12178bf80f4bSopenharmony_ci    } else {
12188bf80f4bSopenharmony_ci        auto& gpuResourceMgr = renderContext_.GetDevice().GetGpuResourceManager();
12198bf80f4bSopenharmony_ci        GpuImageDesc gpuDesc = gpuResourceMgr.CreateGpuImageDesc(imageLoadResult.image->GetImageDesc());
12208bf80f4bSopenharmony_ci        gpuDesc.usageFlags = CORE_IMAGE_USAGE_SAMPLED_BIT | CORE_IMAGE_USAGE_TRANSFER_DST_BIT;
12218bf80f4bSopenharmony_ci        if (gpuDesc.engineCreationFlags & EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_GENERATE_MIPS) {
12228bf80f4bSopenharmony_ci            gpuDesc.usageFlags |= CORE_IMAGE_USAGE_TRANSFER_SRC_BIT;
12238bf80f4bSopenharmony_ci        }
12248bf80f4bSopenharmony_ci        gpuDesc.memoryPropertyFlags = CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
12258bf80f4bSopenharmony_ci
12268bf80f4bSopenharmony_ci        imageHandle = gpuResourceMgr.Create(uri, gpuDesc, std::move(imageLoadResult.image));
12278bf80f4bSopenharmony_ci    }
12288bf80f4bSopenharmony_ci
12298bf80f4bSopenharmony_ci    return imageHandle;
12308bf80f4bSopenharmony_ci}
12318bf80f4bSopenharmony_ci
12328bf80f4bSopenharmony_civoid EcsSerializer::Destroy()
12338bf80f4bSopenharmony_ci{
12348bf80f4bSopenharmony_ci    delete this;
12358bf80f4bSopenharmony_ci}
12368bf80f4bSopenharmony_ci
12378bf80f4bSopenharmony_ciSCENE_END_NAMESPACE()
1238