Nameless Engine
Loading...
Searching...
No Matches
Serializable.h
1#pragma once
2
3// Standard.
4#include <filesystem>
5#include <memory>
6#include <variant>
7#include <optional>
8#include <set>
9#include <format>
10
11// Custom.
12#include "misc/Error.h"
13#include "io/Logger.h"
14#include "misc/ProjectPaths.h"
15#include "io/ConfigManager.h"
16#include "io/properties/GuidProperty.h"
17#include "io/properties/SerializeProperty.h"
18#include "io/serializers/SerializableObjectFieldSerializer.h"
19#include "io/FieldSerializerManager.h"
20
21// External.
22#include "Refureku/Object.h"
23#include "GcPtr.h"
24
25#include "Serializable.generated.h"
26
27namespace ne RNAMESPACE() {
28 class Serializable;
29
32 public:
34
47 const std::string& sObjectUniqueId,
48 const std::unordered_map<std::string, std::string>& customAttributes = {},
49 Serializable* pOriginalObject = nullptr) {
50 this->pObject = pObject;
51 this->sObjectUniqueId = sObjectUniqueId;
53 this->pOriginalObject = pOriginalObject;
54 }
55
58
65
67 std::string sObjectUniqueId;
68
70 std::unordered_map<std::string, std::string> customAttributes;
71 };
72
74 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
75 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
76 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
78 public:
80
89 SmartPointer pObject,
90 std::string sObjectUniqueId,
91 std::unordered_map<std::string, std::string> customAttributes) {
92 this->pObject = std::move(pObject);
93 this->sObjectUniqueId = sObjectUniqueId;
94 this->customAttributes = customAttributes;
95 }
96
98 SmartPointer pObject;
99
101 std::string sObjectUniqueId;
102
104 std::unordered_map<std::string, std::string> customAttributes;
105 };
106
113 class RCLASS(Guid("f5a59b47-ead8-4da4-892e-cf05abb2f3cc")) Serializable : public rfk::Object {
114 // This field serializer will call `onAfterDeserialized` after deserialization.
116
117 public:
118 Serializable() = default;
119 virtual ~Serializable() override = default;
120
130 static std::variant<std::pair<std::set<std::string>, toml::value>, Error>
131 getIdsFromFile(std::filesystem::path pathToFile);
132
179 [[nodiscard]] std::optional<Error> serialize(
180 std::filesystem::path pathToFile,
181 bool bEnableBackup,
182 const std::unordered_map<std::string, std::string>& customAttributes = {});
183
203 [[nodiscard]] static std::optional<Error> serializeMultiple(
204 std::filesystem::path pathToFile,
205 std::vector<SerializableObjectInformation> vObjects,
206 bool bEnableBackup);
207
228 [[nodiscard]] std::variant<std::string, Error> serialize(
229 toml::value& tomlData,
230 const std::string& sEntityId = "",
231 const std::unordered_map<std::string, std::string>& customAttributes = {},
232 const std::optional<std::filesystem::path>& optionalPathToFile = {},
233 bool bEnableBackup = false);
234
259 [[nodiscard]] std::variant<std::string, Error> serialize(
260 toml::value& tomlData,
261 Serializable* pOriginalObject,
262 std::string sEntityId = "",
263 const std::unordered_map<std::string, std::string>& customAttributes = {},
264 const std::optional<std::filesystem::path>& optionalPathToFile = {},
265 bool bEnableBackup = false);
266
280 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
281 requires std::derived_from<InnerType, Serializable> &&
282 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
283 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
284 static std::variant<SmartPointer, Error> deserialize(const std::filesystem::path& pathToFile);
285
299 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
300 requires std::derived_from<InnerType, Serializable> &&
301 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
302 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
303 static std::variant<SmartPointer, Error> deserialize(
304 const std::filesystem::path& pathToFile,
305 std::unordered_map<std::string, std::string>& customAttributes);
306
322 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
323 requires std::derived_from<InnerType, Serializable> &&
324 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
325 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
326 static std::variant<SmartPointer, Error> deserialize(
327 std::filesystem::path pathToFile,
328 std::unordered_map<std::string, std::string>& customAttributes,
329 const std::string& sEntityId);
330
345 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
346 requires std::derived_from<InnerType, Serializable> &&
347 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
348 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
349 static std::variant<SmartPointer, Error> deserialize(
350 const std::filesystem::path& pathToFile, const std::string& sEntityId);
351
360 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
361 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
362 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
363 static std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>, Error>
364 deserializeMultiple(std::filesystem::path pathToFile);
365
386 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
387 requires std::derived_from<InnerType, Serializable> &&
388 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
389 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
390 static std::variant<SmartPointer, Error> deserialize(
391 const toml::value& tomlData,
392 std::unordered_map<std::string, std::string>& customAttributes,
393 std::string sEntityId = "",
394 const std::optional<std::filesystem::path>& optionalPathToFile = {});
395
411 std::optional<std::pair<std::string, std::string>> getPathDeserializedFromRelativeToRes() const;
412
413 protected:
421 virtual void onAfterDeserialized() {}
422
423 private:
433 [[nodiscard]] static std::optional<Error> resolvePathToToml(std::filesystem::path& pathToFile);
434
442 static const rfk::Class* getClassForGuid(const std::string& sGuid);
443
453 static const rfk::Struct*
454 getClassForGuid(const rfk::Struct* pArchetypeToAnalyze, const std::string& sGuid);
455
473 template <typename SmartPointer, typename InnerType = typename SmartPointer::element_type>
474 requires std::derived_from<InnerType, Serializable> &&
475 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
476 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
477 static std::variant<SmartPointer, Error> deserializeFromSection(
478 const toml::value& tomlData,
479 std::unordered_map<std::string, std::string>& customAttributes,
480 const std::string& sSectionName,
481 const std::string& sTypeGuid,
482 const std::string& sEntityId,
483 const std::optional<std::filesystem::path>& optionalPathToFile);
484
497 std::optional<std::pair<std::string, std::string>> pathDeserializedFromRelativeToRes;
498
500 static inline const auto sSubEntityFieldNameKey = ".field_name";
501
506 static inline const auto sPathRelativeToResKey = ".path_relative_to_res";
507
509 static inline const auto sNothingToSerializeKey = ".none";
510
512 static constexpr std::string_view sCustomAttributePrefix = "..";
513
514 ne_Serializable_GENERATED
515 };
516
517 template <typename SmartPointer, typename InnerType>
518 requires std::derived_from<InnerType, Serializable> &&
519 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
520 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
521 std::variant<SmartPointer, Error> Serializable::deserialize(const std::filesystem::path& pathToFile) {
522 std::unordered_map<std::string, std::string> foundCustomAttributes;
523 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes);
524 }
525
526 template <typename SmartPointer, typename InnerType>
527 requires std::derived_from<InnerType, Serializable> &&
528 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
529 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
530 std::variant<SmartPointer, Error> Serializable::deserialize(
531 const std::filesystem::path& pathToFile,
532 std::unordered_map<std::string, std::string>& customAttributes) {
533 return deserialize<SmartPointer>(pathToFile, customAttributes, "");
534 }
535
536 template <typename SmartPointer, typename InnerType>
537 requires std::derived_from<InnerType, Serializable> &&
538 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
539 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
540 std::variant<SmartPointer, Error> Serializable::deserialize(
541 const std::filesystem::path& pathToFile, const std::string& sEntityId) {
542 std::unordered_map<std::string, std::string> foundCustomAttributes;
543 return deserialize<SmartPointer>(pathToFile, foundCustomAttributes, sEntityId);
544 }
545
546 template <typename SmartPointer, typename InnerType>
547 requires std::same_as<SmartPointer, sgc::GcPtr<Serializable>> ||
548 std::same_as<SmartPointer, std::unique_ptr<Serializable>>
549 std::variant<std::vector<DeserializedObjectInformation<SmartPointer>>, Error>
550 Serializable::deserializeMultiple(std::filesystem::path pathToFile) {
551 // Resolve path.
552 auto optionalError = resolvePathToToml(pathToFile);
553 if (optionalError.has_value()) [[unlikely]] {
554 auto error = std::move(optionalError.value());
555 error.addCurrentLocationToErrorStack();
556 return error;
557 }
558
559 // Parse file.
560 toml::value tomlData;
561 try {
562 tomlData = toml::parse(pathToFile);
563 } catch (std::exception& exception) {
564 return Error(std::format(
565 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
566 }
567
568 // Get TOML as table.
569 const auto fileTable = tomlData.as_table();
570 if (fileTable.empty()) [[unlikely]] {
571 return Error("provided toml value has 0 sections while expected at least 1 section");
572 }
573
574 // Deserialize.
575 std::vector<DeserializedObjectInformation<SmartPointer>> deserializedObjects;
576 for (const auto& [sSectionName, tomlValue] : fileTable) {
577 // Get type GUID.
578 const auto iIdEndDotPos = sSectionName.rfind('.');
579 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
580 return Error("provided toml value does not contain entity ID");
581 }
582 const auto sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
583
584 // Get entity ID chain.
585 const auto sEntityId = sSectionName.substr(0, iIdEndDotPos);
586
587 // Check if this is a sub-entity.
588 if (sEntityId.contains('.')) {
589 // Only deserialize top-level entities because sub-entities (fields)
590 // will be deserialized while we deserialize top-level entities.
591 continue;
592 }
593
594 // Deserialize object from this section.
595 std::unordered_map<std::string, std::string> customAttributes;
596 auto result = deserializeFromSection<SmartPointer>(
597 tomlData, customAttributes, sSectionName, sTypeGuid, sEntityId, pathToFile);
598 if (std::holds_alternative<Error>(result)) [[unlikely]] {
599 auto error = std::get<Error>(std::move(result));
600 error.addCurrentLocationToErrorStack();
601 return error;
602 }
603
604 // Save object info.
606 std::get<SmartPointer>(std::move(result)), sEntityId, customAttributes);
607 deserializedObjects.push_back(std::move(objectInfo));
608 }
609
610 return deserializedObjects;
611 }
612
613 template <typename SmartPointer, typename InnerType>
614 requires std::derived_from<InnerType, Serializable> &&
615 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
616 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
617 std::variant<SmartPointer, Error> Serializable::deserialize(
618 std::filesystem::path pathToFile,
619 std::unordered_map<std::string, std::string>& customAttributes,
620 const std::string& sEntityId) {
621 // Resolve path.
622 auto optionalError = resolvePathToToml(pathToFile);
623 if (optionalError.has_value()) [[unlikely]] {
624 auto error = std::move(optionalError.value());
625 error.addCurrentLocationToErrorStack();
626 return error;
627 }
628
629 // Parse file.
630 toml::value tomlData;
631 try {
632 tomlData = toml::parse(pathToFile);
633 } catch (std::exception& exception) {
634 return Error(std::format(
635 "failed to parse TOML file at \"{}\", error: {}", pathToFile.string(), exception.what()));
636 }
637
638 // Deserialize.
639 return deserialize<SmartPointer>(tomlData, customAttributes, sEntityId, pathToFile);
640 }
641
642 template <typename SmartPointer, typename InnerType>
643 requires std::derived_from<InnerType, Serializable> &&
644 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
645 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
646 std::variant<SmartPointer, Error> Serializable::deserialize(
647 const toml::value& tomlData,
648 std::unordered_map<std::string, std::string>& customAttributes,
649 std::string sEntityId,
650 const std::optional<std::filesystem::path>& optionalPathToFile) {
651 if (sEntityId.empty()) {
652 // Put something as entity ID so it would not look weird.
653 sEntityId = "0";
654 }
655
656 // Get TOML as table.
657 const auto fileTable = tomlData.as_table();
658 if (fileTable.empty()) [[unlikely]] {
659 return Error("provided toml value has 0 sections while expected at least 1 section");
660 }
661
662 // Find a section that starts with the specified entity ID.
663 // Each entity section has the following format: [entityId.GUID]
664 // For sub entities (field with reflected type) format: [parentEntityId.childEntityId.childGUID]
665 std::string sTargetSection;
666 std::string sTypeGuid;
667 for (const auto& [sSectionName, value] : fileTable) {
668 // We can't just use `sSectionName.starts_with(sEntityId)` because we might make a mistake in the
669 // following situation: [100...] with entity ID equal to "10" and even if we add a dot
670 // to `sEntityId` we still might make a mistake in the following situation:
671 // [10.30.GUID] while we look for just [10.GUID].
672
673 // Get ID end position (GUID start position).
674 const auto iIdEndDotPos = sSectionName.rfind('.');
675 if (iIdEndDotPos == std::string::npos) [[unlikely]] {
676 return Error(std::format("section name \"{}\" does not contain entity ID", sSectionName));
677 }
678 if (iIdEndDotPos + 1 == sSectionName.size()) [[unlikely]] {
679 return Error(std::format("section name \"{}\" does not have a GUID", sSectionName));
680 }
681 if (iIdEndDotPos == 0) [[unlikely]] {
682 return Error(std::format("section \"{}\" is not full", sSectionName));
683 }
684
685 // Get ID chain (either entity ID or something like "parentEntityId.childEntityId").
686 if (std::string_view(sSectionName.data(), iIdEndDotPos) != sEntityId) { // compare without copy
687 continue;
688 }
689
690 // Save target section name.
691 sTargetSection = sSectionName;
692
693 // Save this entity's GUID.
694 sTypeGuid = sSectionName.substr(iIdEndDotPos + 1);
695
696 break;
697 }
698
699 // Make sure something was found.
700 if (sTargetSection.empty()) [[unlikely]] {
701 return Error(std::format("could not find entity with ID \"{}\"", sEntityId));
702 }
703
704 return deserializeFromSection<SmartPointer>(
705 tomlData, customAttributes, sTargetSection, sTypeGuid, sEntityId, optionalPathToFile);
706 }
707
708 template <typename SmartPointer, typename InnerType>
709 requires std::derived_from<InnerType, Serializable> &&
710 (std::same_as<SmartPointer, sgc::GcPtr<InnerType>> ||
711 std::same_as<SmartPointer, std::unique_ptr<InnerType>>)
712 std::variant<SmartPointer, Error> Serializable::deserializeFromSection(
713 const toml::value& tomlData,
714 std::unordered_map<std::string, std::string>& customAttributes,
715 const std::string& sSectionName,
716 const std::string& sTypeGuid,
717 const std::string& sEntityId,
718 const std::optional<std::filesystem::path>& optionalPathToFile) {
719 // Get all keys (field names) from this section.
720 const auto& targetSection = tomlData.at(sSectionName);
721 if (!targetSection.is_table()) [[unlikely]] {
722 return Error(std::format("found \"{}\" section is not a section", sSectionName));
723 }
724
725 // Collect keys from target section.
726 const auto& sectionTable = targetSection.as_table();
727 std::unordered_map<std::string_view, const toml::value*> fieldsToDeserialize;
728 SmartPointer pOriginalEntity = nullptr;
729 for (const auto& [sKey, value] : sectionTable) {
730 if (sKey == sNothingToSerializeKey) {
731 continue;
732 } else if (sKey == sPathRelativeToResKey) {
733 // Make sure the value is string.
734 if (!value.is_string()) [[unlikely]] {
735 return Error(
736 std::format("found \"{}\" key's value is not string", sPathRelativeToResKey));
737 }
738
739 // Deserialize original entity.
740 auto deserializeResult = Serializable::deserialize<SmartPointer>(
741 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT) / value.as_string());
742 if (std::holds_alternative<Error>(deserializeResult)) [[unlikely]] {
743 auto error = std::get<Error>(std::move(deserializeResult));
744 error.addCurrentLocationToErrorStack();
745 return error;
746 }
747 pOriginalEntity = std::get<SmartPointer>(std::move(deserializeResult));
748 } else if (sKey.starts_with(sCustomAttributePrefix)) {
749 // Custom attribute.
750 // Make sure it's a string.
751 if (!value.is_string()) [[unlikely]] {
752 return Error(std::format("found custom attribute \"{}\" is not a string", sKey));
753 }
754
755 // Add attribute.
756 customAttributes[sKey.substr(sCustomAttributePrefix.size())] = value.as_string();
757 } else {
758 fieldsToDeserialize[sKey] = &value;
759 }
760 }
761
762 // Get archetype for found GUID.
763 const auto pType = getClassForGuid(sTypeGuid);
764 if (pType == nullptr) [[unlikely]] {
765 if (pOriginalEntity) {
766 return Error(std::format(
767 "GUID \"{}\" was not found in the database, but "
768 "the original object at \"{}\" (ID \"{}\") was deserialized",
769 sTypeGuid,
770 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
771 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
772 } else {
773 return Error(std::format("no type found for GUID \"{}\"", sTypeGuid));
774 }
775 }
776
777 // Make sure the type indeed derives from serializable.
779 if (pOriginalEntity) {
780 return Error(std::format(
781 "deserialized type for \"{}\" does not derive from {}, but "
782 "the original object at \"{}\" (ID \"{}\") was deserialized",
783 sTypeGuid,
784 staticGetArchetype().getName(),
785 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().first,
786 pOriginalEntity->getPathDeserializedFromRelativeToRes().value().second));
787 } else {
788 return Error(std::format(
789 "deserialized type with GUID \"{}\" does not derive from {}",
790 sTypeGuid,
791 staticGetArchetype().getName()));
792 }
793 }
794
795 // Create instance.
796 SmartPointer pSmartPointerInstance = nullptr;
797 if (pOriginalEntity != nullptr) {
798 // Use the original entity instead of creating a new one.
799 pSmartPointerInstance = std::move(pOriginalEntity);
800 }
801 if (pSmartPointerInstance == nullptr) {
802 if constexpr (std::is_same_v<SmartPointer, std::unique_ptr<InnerType>>) {
803 // Create a unique instance.
804 pSmartPointerInstance = pType->makeUniqueInstance<InnerType>();
805 if (pSmartPointerInstance == nullptr) [[unlikely]] {
806 return Error(std::format(
807 "unable to make an object of type \"{}\" using type's default constructor "
808 "(does type \"{}\" has a default constructor?)",
809 pType->getName(),
810 pType->getName()));
811 }
812 }
813 // NOTE: don't allow deserializing into a `shared_ptr` since it may create a false assumption
814 // that this `shared_ptr` will also save/load its referenced entity or keep its reference
815 // (you can easily convert a unique_ptr to a shared_ptr so it should not be an issue).
816 else if (std::is_same_v<SmartPointer, sgc::GcPtr<InnerType>>) {
817 // Create GC instance.
818 // (this part is a temporary solution until we will add a `makeGcFromThisType` method to
819 // `rfk::Struct`)
820 std::unique_ptr<InnerType> pInstance = pType->makeUniqueInstance<InnerType>();
821 if (pInstance == nullptr) [[unlikely]] {
822 return Error(std::format(
823 "unable to make an object of type \"{}\" using type's default constructor "
824 "(does type \"{}\" has a default constructor?)",
825 pType->getName(),
826 pType->getName()));
827 }
828 sgc::GcPtr<rfk::Object> pParentGcInstance = pInstance->makeGcFromThisType();
829 pSmartPointerInstance = dynamic_cast<InnerType*>(pParentGcInstance.get());
830 if (pSmartPointerInstance == nullptr) [[unlikely]] {
831 return Error(std::format(
832 "dynamic cast failed to cast the type \"{}\" to the specified template argument "
833 "(are you trying to deserialize into a wrong type?)",
834 pParentGcInstance->getArchetype().getName()));
835 }
836 } else [[unlikely]] {
837 return Error("unexpected smart pointer type received");
838 }
839 }
840
841 // Get field serializers.
842 const auto vFieldSerializers = FieldSerializerManager::getFieldSerializers();
843 const auto vBinaryFieldSerializers = FieldSerializerManager::getBinaryFieldSerializers();
844
845 // Deserialize fields.
846 for (auto& [sFieldName, pFieldTomlValue] : fieldsToDeserialize) {
847 if (sFieldName == sSubEntityFieldNameKey) {
848 // This field is used as section metadata and tells us what field of parent entity
849 // this section describes.
850 continue;
851 }
852
853 // Get field by name.
854 rfk::Field const* pField =
855 pType->getFieldByName(sFieldName.data(), rfk::EFieldFlags::Default, true);
856 if (pField == nullptr) [[unlikely]] { // rarely happens
857 Logger::get().warn(std::format(
858 "field name \"{}\" exists in the specified toml value but does not exist in the "
859 "actual object (if you removed/renamed this reflected field from your "
860 "class/struct - ignore this warning)",
861 sFieldName));
862 continue;
863 }
864 const auto sFieldCanonicalTypeName = std::string(pField->getCanonicalTypeName());
865
866 // Check if it's serializable.
868 continue;
869 }
870
871 // Check if we need to deserialize from external file.
872 const auto pSerializeProperty = pField->getProperty<Serialize>();
873 if (pSerializeProperty->getSerializationType() == FieldSerializationType::FST_AS_EXTERNAL_FILE ||
874 pSerializeProperty->getSerializationType() ==
875 FieldSerializationType::FST_AS_EXTERNAL_BINARY_FILE) {
876 // Make sure this field derives from `Serializable`.
878 pField->getType().getArchetype())) [[unlikely]] {
879 // Show an error so that the developer will instantly see the mistake.
880 auto error = Error("only fields of type derived from `Serializable` can use "
881 "`SerializeAsExternal` property");
882 error.showError();
883 throw std::runtime_error(error.getFullErrorMessage());
884 }
885
886 // Make sure path to the main file is specified.
887 if (!optionalPathToFile.has_value()) [[unlikely]] {
888 return Error("unable to deserialize field marked as `SerializeAsExternal` "
889 "because path to the main file was not specified");
890 }
891
892 // Prepare path to the external file.
893 if (!pFieldTomlValue->is_string()) [[unlikely]] {
894 return Error(std::format(
895 "expected field \"{}\" to store external filename in file \"{}\"",
896 pField->getName(),
897 optionalPathToFile.value().string()));
898 }
899 const auto sExternalFileName = pFieldTomlValue->as_string();
900 const auto pathToExternalFile = optionalPathToFile.value().parent_path() / sExternalFileName;
901
902 // Get field object.
903 const auto pFieldObject =
904 reinterpret_cast<Serializable*>(pField->getPtrUnsafe(pSmartPointerInstance.get()));
905
906 if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_FILE) {
907 // Deserialize external file.
908 auto result = deserialize<std::unique_ptr<Serializable>>(pathToExternalFile);
909 if (std::holds_alternative<Error>(result)) [[unlikely]] {
910 auto error = std::get<Error>(std::move(result));
911 error.addCurrentLocationToErrorStack();
912 return error;
913 }
914 const auto pDeserializedExternalField =
915 std::get<std::unique_ptr<Serializable>>(std::move(result));
916
917 // Clone deserialized data to field.
919 pDeserializedExternalField.get(), pFieldObject, false);
920 if (optionalError.has_value()) [[unlikely]] {
921 auto error = optionalError.value();
922 error.addCurrentLocationToErrorStack();
923 return error;
924 }
925 } else if (pSerializeProperty->getSerializationType() == FST_AS_EXTERNAL_BINARY_FILE) {
926 // Deserialize binary file.
927 bool bDeserialized = false;
928 for (const auto& pBinarySerializer : vBinaryFieldSerializers) {
929 if (!pBinarySerializer->isFieldTypeSupported(pField)) {
930 continue;
931 }
932
933 // Deserialize as binary.
934 auto optionalError = pBinarySerializer->deserializeField(
935 pathToExternalFile, pSmartPointerInstance.get(), pField);
936 if (optionalError.has_value()) [[unlikely]] {
937 auto error = optionalError.value();
938 error.addCurrentLocationToErrorStack();
939 return error;
940 }
941
942 // Finished.
943 bDeserialized = true;
944 break;
945 }
946
947 // Make sure we deserialized this field.
948 if (!bDeserialized) [[unlikely]] {
949 return Error(std::format(
950 "the field \"{}\" with type \"{}\" (maybe inherited) has "
951 "unsupported for deserialization type",
952 pField->getName(),
953 pField->getCanonicalTypeName()));
954 }
955
956 // Notify about deserialization.
957 pFieldObject->onAfterDeserialized();
958 } else [[unlikely]] {
959 return Error(std::format("unhandled case on field \"{}\"", pField->getName()));
960 }
961
962 continue;
963 }
964
965 // Deserialize field value.
966 bool bFoundSerializer = false;
967 for (const auto& pSerializer : vFieldSerializers) {
968 if (pSerializer->isFieldTypeSupported(pField)) {
969 bFoundSerializer = true;
970 auto optionalError = pSerializer->deserializeField(
971 &tomlData,
972 pFieldTomlValue,
973 pSmartPointerInstance.get(),
974 pField,
975 sSectionName,
976 sEntityId,
977 customAttributes);
978 if (optionalError.has_value()) [[unlikely]] {
979 auto error = optionalError.value();
980 error.addCurrentLocationToErrorStack();
981 if (pOriginalEntity) {
982 Logger::get().error(std::format(
983 "an error occurred while deserializing "
984 "changed field (this field was not deserialized), error: {}",
985 error.getFullErrorMessage()));
986 } else {
987 return error;
988 }
989 }
990 }
991 }
992
993 if (!bFoundSerializer) [[unlikely]] {
995 std::format("unable to find a deserializer that supports field \"{}\"", sFieldName));
996 }
997 }
998
999 if (optionalPathToFile.has_value() &&
1000 optionalPathToFile->string().starts_with(
1001 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT).string())) {
1002 // File is located in the `res` directory, save a relative path to the `res` directory.
1003 auto sRelativePath = std::filesystem::relative(
1004 optionalPathToFile->string(),
1005 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT))
1006 .string();
1007
1008 // Replace all '\' characters with '/' just to be consistent.
1009 std::replace(sRelativePath.begin(), sRelativePath.end(), '\\', '/');
1010
1011 // Remove the forward slash at the beginning (if exists).
1012 if (sRelativePath.starts_with('/')) {
1013 sRelativePath = sRelativePath.substr(1);
1014 }
1015
1016 // Double check that everything is correct.
1017 const auto pathToOriginalFile =
1018 ProjectPaths::getPathToResDirectory(ResourceDirectory::ROOT) / sRelativePath;
1019 if (!std::filesystem::exists(pathToOriginalFile)) [[unlikely]] {
1020 return Error(std::format(
1021 "failed to save the relative path to the `res` directory for the file at \"{}\", "
1022 "reason: constructed path \"{}\" does not exist",
1023 optionalPathToFile->string(),
1024 pathToOriginalFile.string()));
1025 }
1026
1027 // Save deserialization path.
1028 pSmartPointerInstance->pathDeserializedFromRelativeToRes = {sRelativePath, sEntityId};
1029 }
1030
1031 // Notify about deserialization finished.
1032 Serializable* pTarget = dynamic_cast<Serializable*>(pSmartPointerInstance.get());
1033 pTarget->onAfterDeserialized();
1034
1035 return pSmartPointerInstance;
1036 }
1037
1038}; // namespace ne RNAMESPACE()
1039
1040File_Serializable_GENERATED
Definition: Error.h:27
static std::vector< IFieldSerializer * > getFieldSerializers()
Definition: FieldSerializerManager.cpp:58
static std::vector< IBinaryFieldSerializer * > getBinaryFieldSerializers()
Definition: FieldSerializerManager.cpp:69
Definition: GuidProperty.h:30
void error(std::string_view sText, const std::source_location location=std::source_location::current()) const
Definition: Logger.cpp:75
static Logger & get()
Definition: Logger.cpp:41
void warn(std::string_view sText, const std::source_location location=std::source_location::current()) const
Definition: Logger.cpp:62
static std::filesystem::path getPathToResDirectory()
Definition: ProjectPaths.cpp:123
Definition: SerializableObjectFieldSerializer.h:19
static bool isFieldSerializable(rfk::Field const &field)
Definition: SerializableObjectFieldSerializer.cpp:393
static bool isDerivedFromSerializable(rfk::Archetype const *pArchetype)
Definition: SerializableObjectFieldSerializer.cpp:410
static std::optional< Error > cloneSerializableObject(Serializable *pFrom, Serializable *pTo, bool bNotifyAboutDeserialized)
Definition: SerializableObjectFieldSerializer.cpp:52
Definition: Serializable.h:113
virtual void onAfterDeserialized()
Definition: Serializable.h:421
static std::variant< SmartPointer, Error > deserialize(const std::filesystem::path &pathToFile)
Definition: Serializable.h:521
static std::variant< std::vector< DeserializedObjectInformation< SmartPointer > >, Error > deserializeMultiple(std::filesystem::path pathToFile)
Definition: Serializable.h:550
std::optional< std::pair< std::string, std::string > > pathDeserializedFromRelativeToRes
Definition: Serializable.h:497
static std::optional< Error > resolvePathToToml(std::filesystem::path &pathToFile)
Definition: Serializable.cpp:651
static std::variant< SmartPointer, Error > deserializeFromSection(const toml::value &tomlData, std::unordered_map< std::string, std::string > &customAttributes, const std::string &sSectionName, const std::string &sTypeGuid, const std::string &sEntityId, const std::optional< std::filesystem::path > &optionalPathToFile)
Definition: Serializable.h:712
Definition: SerializeProperty.h:42
Definition: Serializable.h:77
SmartPointer pObject
Definition: Serializable.h:98
DeserializedObjectInformation(SmartPointer pObject, std::string sObjectUniqueId, std::unordered_map< std::string, std::string > customAttributes)
Definition: Serializable.h:88
std::unordered_map< std::string, std::string > customAttributes
Definition: Serializable.h:104
std::string sObjectUniqueId
Definition: Serializable.h:101
Definition: Serializable.h:31
Serializable * pObject
Definition: Serializable.h:57
std::string sObjectUniqueId
Definition: Serializable.h:67
Serializable * pOriginalObject
Definition: Serializable.h:64
std::unordered_map< std::string, std::string > customAttributes
Definition: Serializable.h:70
SerializableObjectInformation(Serializable *pObject, const std::string &sObjectUniqueId, const std::unordered_map< std::string, std::string > &customAttributes={}, Serializable *pOriginalObject=nullptr)
Definition: Serializable.h:45