1 /*
2 * Copyright (C) 2023 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 "system_graph_loader.h"
17
18 #include <charconv>
19 #include <cinttypes>
20
21 #include <core/ecs/intf_system.h>
22 #include <core/intf_engine.h>
23 #include <core/io/intf_file.h>
24 #include <core/io/intf_file_manager.h>
25 #include <core/log.h>
26 #include <core/namespace.h>
27 #include <core/plugin/intf_plugin_register.h>
28 #include <core/property/intf_property_api.h>
29 #include <core/property/intf_property_handle.h>
30 #include <core/property/property_types.h>
31
32 #define JSON_IMPL
33 #include "json_util.h"
34
35 CORE_BEGIN_NAMESPACE()
36 using BASE_NS::array_view;
37 using BASE_NS::string;
38 using BASE_NS::string_view;
39 using BASE_NS::Uid;
40 using BASE_NS::Math::Quat;
41 using BASE_NS::Math::Vec2;
42 using BASE_NS::Math::Vec3;
43 using BASE_NS::Math::Vec4;
44
45 namespace {
46 constexpr size_t VERSION_SIZE { 5u };
47 constexpr uint32_t VERSION_MAJOR { 22u };
48
49 template<class TYPEINFO>
FindTypeInfo(const string_view name,const array_view<const ITypeInfo * const> & container)50 const TYPEINFO* FindTypeInfo(const string_view name, const array_view<const ITypeInfo* const>& container)
51 {
52 for (const auto& info : container) {
53 if (static_cast<const TYPEINFO*>(info)->typeName == name) {
54 return static_cast<const TYPEINFO*>(info);
55 }
56 }
57
58 return nullptr;
59 }
60
61 template<class TYPEINFO>
FindTypeInfo(const Uid & uid,const array_view<const ITypeInfo * const> & container)62 const TYPEINFO* FindTypeInfo(const Uid& uid, const array_view<const ITypeInfo* const>& container)
63 {
64 for (const auto& info : container) {
65 if (static_cast<const TYPEINFO* const>(info)->uid == uid) {
66 return static_cast<const TYPEINFO*>(info);
67 }
68 }
69
70 return nullptr;
71 }
72
73 struct PropertyValue {
74 const IPropertyHandle* handle;
75 void* offset;
76 const Property* info;
77 template<typename Type>
Get__anon93e9d0540111::PropertyValue78 Type& Get() const
79 {
80 return *static_cast<Type*>(offset);
81 }
82 };
83
84 // For primitive types..
85 template<typename ElementType>
ReadArrayPropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)86 void ReadArrayPropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& error)
87 {
88 ElementType* output = &propertyData.Get<ElementType>();
89 if (propertyData.info->type.isArray) {
90 // expecting array data.
91 if (auto const array = jsonData.find(propertyData.info->name); array) {
92 auto outputView = array_view<ElementType>(output, propertyData.info->count);
93 from_json(*array, outputView);
94 }
95 } else {
96 // expecting single value
97 ElementType result;
98 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
99 *output = result;
100 }
101 }
102 }
103
104 // Specialization for char types (strings).. (null terminate the arrays)
105 template<>
ReadArrayPropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)106 void ReadArrayPropertyValue<char>(const json::value& jsonData, PropertyValue& propertyData, string& error)
107 {
108 char* output = &propertyData.Get<char>();
109 string_view result;
110 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
111 if (propertyData.info->type.isArray) {
112 const auto end = result.copy(output, propertyData.info->count - 1, 0);
113 output[end] = 0;
114 } else {
115 *output = result[0];
116 }
117 }
118 }
119
120 template<class HandleType>
ReadHandlePropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)121 void ReadHandlePropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& error)
122 {
123 decltype(HandleType::id) result;
124 if (SafeGetJsonValue(jsonData, propertyData.info->name, error, result)) {
125 HandleType& handle = propertyData.Get<HandleType>();
126 handle.id = result;
127 }
128 }
129
130 template<class VecType, size_t n>
ReadVecPropertyValue(const json::value & jsonData,PropertyValue & propertyData,string & error)131 void ReadVecPropertyValue(const json::value& jsonData, PropertyValue& propertyData, string& error)
132 {
133 if (auto const array = jsonData.find(propertyData.info->name); array) {
134 VecType& result = propertyData.Get<VecType>();
135 from_json(*array, result.data);
136 }
137 }
138
ResolveComponentDependencies(IEcs & ecs,array_view<const Uid> dependencies,const array_view<const ITypeInfo * const> componentMetadata)139 bool ResolveComponentDependencies(
140 IEcs& ecs, array_view<const Uid> dependencies, const array_view<const ITypeInfo* const> componentMetadata)
141 {
142 for (const Uid& dependencyUid : dependencies) {
143 // default constructed UID is used as a wildcard for dependecies not known at compile time.
144 if (dependencyUid != Uid {}) {
145 // See if this component type exists in ecs
146 const IComponentManager* componentManager = ecs.GetComponentManager(dependencyUid);
147 if (!componentManager) {
148 // Find component type and create such manager.
149 const auto* componentTypeInfo =
150 FindTypeInfo<ComponentManagerTypeInfo>(dependencyUid, componentMetadata);
151 if (componentTypeInfo) {
152 ecs.CreateComponentManager(*componentTypeInfo);
153 } else {
154 // Could not find this component manager, unable to continue.
155 return false;
156 }
157 }
158 }
159 }
160
161 return true;
162 }
163
GetProperty(IPropertyHandle * handle,size_t i,void * offset)164 PropertyValue GetProperty(IPropertyHandle* handle, size_t i, void* offset)
165 {
166 auto* api = handle->Owner();
167 if (i < api->PropertyCount()) {
168 auto prop = api->MetaData(i);
169 return { handle, static_cast<uint8_t*>(offset) + prop->offset, prop };
170 }
171 return { nullptr, nullptr, nullptr };
172 }
173
ParseProperties(const json::value & jsonData,ISystem & system,string & error)174 void ParseProperties(const json::value& jsonData, ISystem& system, string& error)
175 {
176 if (const json::value* propertiesIt = jsonData.find("properties"); propertiesIt) {
177 if (auto systemPropertyHandle = system.GetProperties(); systemPropertyHandle) {
178 const IPropertyApi* propertyApi = systemPropertyHandle->Owner();
179 if (auto offset = systemPropertyHandle->WLock(); offset) {
180 for (size_t i = 0; i < propertyApi->PropertyCount(); ++i) {
181 PropertyValue property = GetProperty(systemPropertyHandle, i, offset);
182 if (property.info) {
183 switch (property.info->type) {
184 case PropertyType::BOOL_T:
185 case PropertyType::BOOL_ARRAY_T:
186 ReadArrayPropertyValue<bool>(*propertiesIt, property, error);
187 break;
188
189 case PropertyType::CHAR_T:
190 case PropertyType::CHAR_ARRAY_T:
191 ReadArrayPropertyValue<char>(*propertiesIt, property, error);
192 break;
193
194 case PropertyType::INT8_T:
195 case PropertyType::INT8_ARRAY_T:
196 ReadArrayPropertyValue<int8_t>(*propertiesIt, property, error);
197 break;
198
199 case PropertyType::INT16_T:
200 case PropertyType::INT16_ARRAY_T:
201 ReadArrayPropertyValue<int16_t>(*propertiesIt, property, error);
202 break;
203
204 case PropertyType::INT32_T:
205 case PropertyType::INT32_ARRAY_T:
206 ReadArrayPropertyValue<int32_t>(*propertiesIt, property, error);
207 break;
208
209 case PropertyType::INT64_T:
210 case PropertyType::INT64_ARRAY_T:
211 ReadArrayPropertyValue<int64_t>(*propertiesIt, property, error);
212 break;
213
214 case PropertyType::UINT8_T:
215 case PropertyType::UINT8_ARRAY_T:
216 ReadArrayPropertyValue<uint8_t>(*propertiesIt, property, error);
217 break;
218
219 case PropertyType::UINT16_T:
220 case PropertyType::UINT16_ARRAY_T:
221 ReadArrayPropertyValue<uint16_t>(*propertiesIt, property, error);
222 break;
223
224 case PropertyType::UINT32_T:
225 case PropertyType::UINT32_ARRAY_T:
226 ReadArrayPropertyValue<uint32_t>(*propertiesIt, property, error);
227 break;
228
229 case PropertyType::UINT64_T:
230 case PropertyType::UINT64_ARRAY_T:
231 ReadArrayPropertyValue<uint64_t>(*propertiesIt, property, error);
232 break;
233
234 case PropertyType::DOUBLE_T:
235 case PropertyType::DOUBLE_ARRAY_T:
236 ReadArrayPropertyValue<double>(*propertiesIt, property, error);
237 break;
238
239 case PropertyType::ENTITY_T:
240 ReadHandlePropertyValue<Entity>(*propertiesIt, property, error);
241 break;
242
243 case PropertyType::VEC2_T:
244 ReadVecPropertyValue<Vec2, 2>(*propertiesIt, property, error); // 2: Vec2
245 break;
246 case PropertyType::VEC3_T:
247 ReadVecPropertyValue<Vec3, 3>(*propertiesIt, property, error); // 3: Vec3
248 break;
249 case PropertyType::VEC4_T:
250 ReadVecPropertyValue<Vec4, 4>(*propertiesIt, property, error); // 4: Vec4
251 break;
252 case PropertyType::QUAT_T:
253 ReadVecPropertyValue<Quat, 4>(*propertiesIt, property, error); // 4: Vec4
254 break;
255 case PropertyType::MAT4X4_T:
256 // NOTE: Implement.
257 break;
258
259 case PropertyType::FLOAT_T:
260 case PropertyType::FLOAT_ARRAY_T:
261 ReadArrayPropertyValue<float>(*propertiesIt, property, error);
262 break;
263
264 case PropertyType::STRING_T:
265 ReadArrayPropertyValue<string>(*propertiesIt, property, error);
266 break;
267
268 case PropertyType::ENTITY_ARRAY_T:
269 case PropertyType::VEC2_ARRAY_T:
270 case PropertyType::VEC3_ARRAY_T:
271 case PropertyType::VEC4_ARRAY_T:
272 case PropertyType::QUAT_ARRAY_T:
273 case PropertyType::MAT4X4_ARRAY_T:
274 case PropertyType::INVALID:
275 // NOTE: Implement.
276 break;
277 }
278 }
279 }
280 }
281 systemPropertyHandle->WUnlock();
282 system.SetProperties(*systemPropertyHandle); // notify that the properties HAVE changed.
283 }
284 }
285 }
286
ParseSystem(const json::value & jsonData,const array_view<const ITypeInfo * const> & componentMetadata,const array_view<const ITypeInfo * const> & systemMetadata,IEcs & ecs,string & error)287 bool ParseSystem(const json::value& jsonData, const array_view<const ITypeInfo* const>& componentMetadata,
288 const array_view<const ITypeInfo* const>& systemMetadata, IEcs& ecs, string& error)
289 {
290 string typeName;
291 SafeGetJsonValue(jsonData, "typeName", error, typeName);
292
293 bool optional = false;
294 SafeGetJsonValue(jsonData, "optional", error, optional);
295
296 // Look up this system type from metadata.
297 const auto* typeInfo = FindTypeInfo<SystemTypeInfo>(typeName, systemMetadata);
298 if (typeInfo) {
299 // Ensure we have all components required by this system.
300 if (!ResolveComponentDependencies(ecs, typeInfo->componentDependencies, componentMetadata)) {
301 error += "Failed to resolve component dependencies: (" + typeName + ".\n";
302 return false;
303 }
304 if (!ResolveComponentDependencies(ecs, typeInfo->readOnlyComponentDependencies, componentMetadata)) {
305 error += "Failed to resolve read-only component dependencies: (" + typeName + ".\n";
306 return false;
307 }
308
309 // Create system and read properties.
310 ISystem* system = ecs.CreateSystem(*typeInfo);
311 ParseProperties(jsonData, *system, error);
312
313 return true;
314 } else {
315 CORE_LOG_W("Cannot find system: %s (optional: %s)", typeName.c_str(), (optional ? "true" : "false"));
316 if (!optional) {
317 error += "Cannot find system: " + typeName + ".\n";
318 }
319 }
320
321 return optional;
322 }
323 } // namespace
324
Load(const string_view uri,IEcs & ecs)325 SystemGraphLoader::LoadResult SystemGraphLoader::Load(const string_view uri, IEcs& ecs)
326 {
327 IFile::Ptr file = fileManager_.OpenFile(uri);
328 if (!file) {
329 CORE_LOG_D("Error loading '%s'", string(uri).c_str());
330 return LoadResult("Failed to open file.");
331 }
332
333 const uint64_t byteLength = file->GetLength();
334
335 string raw(static_cast<size_t>(byteLength), string::value_type());
336 if (file->Read(raw.data(), byteLength) != byteLength) {
337 CORE_LOG_D("Error loading '%s'", string(uri).c_str());
338 return LoadResult("Failed to read file.");
339 }
340
341 return LoadString(raw, ecs);
342 }
343
LoadString(const string_view jsonString,IEcs & ecs)344 SystemGraphLoader::LoadResult SystemGraphLoader::LoadString(const string_view jsonString, IEcs& ecs)
345 {
346 auto const json = json::parse(jsonString.data());
347 if (json) {
348 LoadResult finalResult;
349 {
350 string ver;
351 string type;
352 uint32_t verMajor { ~0u };
353 uint32_t verMinor { ~0u };
354 if (const json::value* iter = json.find("compatibility_info"); iter) {
355 SafeGetJsonValue(*iter, "type", finalResult.error, type);
356 SafeGetJsonValue(*iter, "version", finalResult.error, ver);
357 if (ver.size() == VERSION_SIZE) {
358 if (const auto delim = ver.find('.'); delim != string::npos) {
359 std::from_chars(ver.data(), ver.data() + delim, verMajor);
360 std::from_chars(ver.data() + delim + 1, ver.data() + ver.size(), verMinor);
361 }
362 }
363 }
364 if ((type != "systemgraph") || (verMajor != VERSION_MAJOR)) {
365 // NOTE: we're still loading the system graph
366 CORE_LOG_W("DEPRECATED SYSTEM GRAPH: invalid system graph type (%s) and / or invalid version (%s).",
367 type.c_str(), ver.c_str());
368 }
369 }
370 auto& pluginRegister = GetPluginRegister();
371 auto componentMetadata = pluginRegister.GetTypeInfos(ComponentManagerTypeInfo::UID);
372 auto systemMetadata = pluginRegister.GetTypeInfos(SystemTypeInfo::UID);
373 const auto& systemsArrayIt = json.find("systems");
374 if (systemsArrayIt && systemsArrayIt->is_array()) {
375 for (const auto& systemJson : systemsArrayIt->array_) {
376 if (!ParseSystem(systemJson, componentMetadata, systemMetadata, ecs, finalResult.error)) {
377 break;
378 }
379 }
380 }
381
382 finalResult.success = finalResult.error.empty();
383
384 return finalResult;
385 } else {
386 return LoadResult("Invalid json file.");
387 }
388 }
389
SystemGraphLoader(IFileManager & fileManager)390 SystemGraphLoader::SystemGraphLoader(IFileManager& fileManager) : fileManager_ { fileManager } {}
391
Destroy()392 void SystemGraphLoader::Destroy()
393 {
394 delete this;
395 }
396
397 // SystemGraphLoader factory
Create(IFileManager & fileManager)398 ISystemGraphLoader::Ptr SystemGraphLoaderFactory::Create(IFileManager& fileManager)
399 {
400 return ISystemGraphLoader::Ptr { new SystemGraphLoader(fileManager) };
401 }
402
GetInterface(const Uid & uid) const403 const IInterface* SystemGraphLoaderFactory::GetInterface(const Uid& uid) const
404 {
405 if (uid == ISystemGraphLoaderFactory::UID) {
406 return this;
407 }
408 return nullptr;
409 }
410
GetInterface(const Uid & uid)411 IInterface* SystemGraphLoaderFactory::GetInterface(const Uid& uid)
412 {
413 if (uid == ISystemGraphLoaderFactory::UID) {
414 return this;
415 }
416 return nullptr;
417 }
418
Ref()419 void SystemGraphLoaderFactory::Ref() {}
Unref()420 void SystemGraphLoaderFactory::Unref() {}
421
422 CORE_END_NAMESPACE()
423