// Copyright Epic Games, Inc. All Rights Reserved. #include "StructDeserializer.h" #include "UObject/UnrealType.h" #include "IStructDeserializerBackend.h" #include "UObject/PropertyOptional.h" #include "UObject/PropertyPortFlags.h" /* Internal helpers *****************************************************************************/ namespace StructDeserializer { /** * Structure for the read state stack. */ struct FReadState { /** Holds the property's current array index. */ int32 ArrayIndex = 0; /** Holds a pointer to the property's data. */ void* Data = nullptr; /** Holds the property's meta data. */ FProperty* Property = nullptr; /** Holds a pointer to the UStruct describing the data. */ UStruct* TypeInfo = nullptr; }; /** * Finds the class for the given stack state. * * @param State The stack state to find the class for. * @return The class, if found. */ UStruct* FindClass( const FReadState& State ) { UStruct* Class = nullptr; if (State.Property != nullptr) { FProperty* ParentProperty = State.Property; if (FArrayProperty* ArrayProperty = CastField(ParentProperty)) { ParentProperty = ArrayProperty->Inner; } FStructProperty* StructProperty = CastField(ParentProperty); FObjectPropertyBase* ObjectProperty = CastField(ParentProperty); if (StructProperty != nullptr) { Class = StructProperty->Struct; } else if (ObjectProperty != nullptr) { Class = ObjectProperty->PropertyClass; } } else { UObject* RootObject = static_cast(State.Data); Class = RootObject->GetClass(); } return Class; } /** Finds an element in a Map/Set Container at the given logical index, adding enough entries for that logical index to be valid */ template int32 ExpandForIndex(ScriptContainerHelperType& ScriptContainerHelper, int32 LogicalIndex) { int32 InternalIndex = ScriptContainerHelper.FindInternalIndex(LogicalIndex); if (InternalIndex != INDEX_NONE) { return InternalIndex; } // Container does not have any element at the logical array index, add enough items so that this logical index is valid const int32 ItemsToAddCount = LogicalIndex - ScriptContainerHelper.Num() + 1; // Maps/Set containers don't have a ExpandForIndex / Add(Count). Add default values manually for (int32 ItemsAddedCount = 0; ItemsAddedCount < ItemsToAddCount; ++ItemsAddedCount) { ScriptContainerHelper.AddDefaultValue_Invalid_NeedsRehash(); } InternalIndex = ScriptContainerHelper.FindInternalIndex(LogicalIndex); check(InternalIndex != INDEX_NONE); return InternalIndex; } } /* FStructDeserializer static interface *****************************************************************************/ bool FStructDeserializer::Deserialize( void* OutStruct, UStruct& TypeInfo, IStructDeserializerBackend& Backend, const FStructDeserializerPolicies& Policies ) { using namespace StructDeserializer; check(OutStruct != nullptr); // initialize deserialization FReadState CurrentState; { CurrentState.ArrayIndex = 0; CurrentState.Data = OutStruct; CurrentState.Property = nullptr; CurrentState.TypeInfo = &TypeInfo; } TArray StateStack; EStructDeserializerBackendTokens Token; // process state stack while (Backend.GetNextToken(Token)) { FString PropertyName = Backend.GetCurrentPropertyName(); switch (Token) { case EStructDeserializerBackendTokens::ArrayEnd: { // rehash the set now that we are done with it if (FSetProperty* SetProperty = CastField(CurrentState.Property)) { FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); SetHelper.Rehash(); } if (StateStack.Num() == 0) { UE_LOG(LogSerialization, Verbose, TEXT("Malformed input: Found ArrayEnd without matching ArrayStart")); return false; } CurrentState = StateStack.Pop(EAllowShrinking::No); } break; case EStructDeserializerBackendTokens::ArrayStart: { FReadState NewState; NewState.Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); if (NewState.Property != nullptr) { if (Policies.PropertyFilter && !Policies.PropertyFilter(NewState.Property, CurrentState.Property)) { Backend.SkipArray(); continue; } // handle set property if (FSetProperty* SetProperty = CastField(NewState.Property)) { NewState.Data = SetProperty->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); FScriptSetHelper SetHelper(SetProperty, NewState.Data); SetHelper.EmptyElements(); } // handle array property else if (FArrayProperty* ArrayProperty = CastField(NewState.Property)) { // fast path for byte array if (Backend.ReadPODArray(ArrayProperty, CurrentState.Data)) { // read the entire array, move to the next property continue; } // failed to read as a pod array, read as regular array iterating on each property else { NewState.Data = CurrentState.Data; } } // handle static array else { NewState.Data = CurrentState.Data; } NewState.TypeInfo = FindClass(NewState); StateStack.Push(CurrentState); CurrentState = NewState; } else { // error: array property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("The array property '%s' does not exist"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } Backend.SkipArray(); } } break; case EStructDeserializerBackendTokens::Error: { return false; } case EStructDeserializerBackendTokens::Property: { // Set are serialized as array, so no property name will be set for each entry if (PropertyName.IsEmpty() && (CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FSetProperty::StaticClass())) { // handle set element FSetProperty* SetProperty = CastField(CurrentState.Property); FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); FProperty* Property = SetProperty->ElementProp; const int32 ElementIndex = SetHelper.AddDefaultValue_Invalid_NeedsRehash(); uint8* ElementPtr = SetHelper.GetElementPtr(ElementIndex); if (!Backend.ReadProperty(Property, CurrentState.Property, ElementPtr, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in Set '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } // Otherwise we are dealing with dynamic or static array else if (PropertyName.IsEmpty()) { // handle array element FArrayProperty* ArrayProperty = CastField(CurrentState.Property); FProperty* Property = nullptr; if (ArrayProperty != nullptr) { // dynamic array element Property = ArrayProperty->Inner; } else { // static array element Property = CurrentState.Property; } if (Property == nullptr) { // error: no meta data for array element if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Failed to serialize array element %i"), CurrentState.ArrayIndex); } return false; } else if (!Backend.ReadProperty(Property, CurrentState.Property, CurrentState.Data, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The array element '%s[%i]' could not be read (%s)"), *PropertyName, CurrentState.ArrayIndex, *Backend.GetDebugString()); } ++CurrentState.ArrayIndex; } else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FMapProperty::StaticClass())) { // handle map element FMapProperty* MapProperty = CastField(CurrentState.Property); FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); FProperty* Property = MapProperty->ValueProp; int32 PairIndex = MapHelper.AddDefaultValue_Invalid_NeedsRehash(); uint8* PairPtr = MapHelper.GetPairPtr(PairIndex); MapProperty->KeyProp->ImportText_Direct(*PropertyName, PairPtr, nullptr, PPF_None); if (!Backend.ReadProperty(Property, CurrentState.Property, PairPtr, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in map '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FOptionalProperty::StaticClass())) { FOptionalProperty* OptionalProperty = CastField(CurrentState.Property); FProperty* Property = OptionalProperty->GetValueProperty(); void* ValueData = OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(CurrentState.Data); if (!Backend.ReadProperty(Property, CurrentState.Property, ValueData, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in optional '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } else { // handle scalar property FProperty* Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); if (Property != nullptr) { if (Policies.PropertyFilter && !Policies.PropertyFilter(Property, CurrentState.Property)) { continue; } if (!Backend.ReadProperty(Property, CurrentState.Property, CurrentState.Data, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } else { // error: scalar property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' does not exist"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } } } break; case EStructDeserializerBackendTokens::StructureEnd: { // rehash if value was a map FMapProperty* MapProperty = CastField(CurrentState.Property); if (MapProperty != nullptr) { FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); MapHelper.Rehash(); } // ending of root structure if (StateStack.Num() == 0) { return true; } CurrentState = StateStack.Pop(EAllowShrinking::No); } break; case EStructDeserializerBackendTokens::StructureStart: { FReadState NewState; if (PropertyName.IsEmpty()) { // skip root structure if (CurrentState.Property == nullptr) { check(StateStack.Num() == 0); continue; } // handle struct element inside set if (FSetProperty* SetProperty = CastField(CurrentState.Property)) { FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); const int32 ElementIndex = SetHelper.AddDefaultValue_Invalid_NeedsRehash(); uint8* ElementPtr = SetHelper.GetElementPtr(ElementIndex); NewState.Data = ElementPtr; NewState.Property = SetProperty->ElementProp; } // handle struct element inside array else if (FArrayProperty* ArrayProperty = CastField(CurrentState.Property)) { FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); const int32 ArrayIndex = ArrayHelper.AddValue(); NewState.Property = ArrayProperty->Inner; NewState.Data = ArrayHelper.GetRawPtr(ArrayIndex); } else { UE_LOG(LogSerialization, Verbose, TEXT("Found unnamed value outside of array or set.")); return false; } } // handle map or struct element inside map else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FMapProperty::StaticClass())) { FMapProperty* MapProperty = CastField(CurrentState.Property); FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); int32 PairIndex = MapHelper.AddDefaultValue_Invalid_NeedsRehash(); uint8* PairPtr = MapHelper.GetPairPtr(PairIndex); NewState.Data = PairPtr + MapHelper.MapLayout.ValueOffset; NewState.Property = MapProperty->ValueProp; MapProperty->KeyProp->ImportText_Direct(*PropertyName, PairPtr, nullptr, PPF_None); } // handle map or struct element inside optional else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FOptionalProperty::StaticClass())) { FOptionalProperty* OptionalProperty = CastFieldChecked(CurrentState.Property); NewState.Property = OptionalProperty->GetValueProperty(); NewState.Data = OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(CurrentState.Data); } else { NewState.Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); // unrecognized property if (NewState.Property == nullptr) { // error: map or struct property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Map, Set, or struct property '%s' not found"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } // handle map property start else if (FMapProperty* MapProperty = CastField(NewState.Property)) { NewState.Data = MapProperty->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); FScriptMapHelper MapHelper(MapProperty, NewState.Data); MapHelper.EmptyValues(); } // handle optional property start else if (FOptionalProperty* OptionalProperty = CastField(NewState.Property)) { NewState.Data = OptionalProperty->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); OptionalProperty->MarkUnset(NewState.Data); } // handle struct property else { NewState.Data = NewState.Property->ContainerPtrToValuePtr(CurrentState.Data); } } if (NewState.Property != nullptr) { // skip struct property if property filter is set and rejects it if (Policies.PropertyFilter && !Policies.PropertyFilter(NewState.Property, CurrentState.Property)) { Backend.SkipStructure(); continue; } NewState.ArrayIndex = 0; NewState.TypeInfo = FindClass(NewState); StateStack.Push(CurrentState); CurrentState = NewState; } else { // error: structured property not found Backend.SkipStructure(); if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Structured property '%s' not found"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } } default: continue; } } // root structure not completed return false; } bool FStructDeserializer::DeserializeElement(void* OutAddress, UStruct& OwnerInfo, int32 InElementIndex, IStructDeserializerBackend& Backend, const FStructDeserializerPolicies& Policies) { using namespace StructDeserializer; check(OutAddress != nullptr); // initialize deserialization FReadState CurrentState; { CurrentState.ArrayIndex = InElementIndex == INDEX_NONE ? 0 : InElementIndex; CurrentState.Data = OutAddress; CurrentState.TypeInfo = &OwnerInfo; } TArray StateStack; EStructDeserializerBackendTokens Token; // process state stack while (Backend.GetNextToken(Token)) { const FString PropertyName = Backend.GetCurrentPropertyName(); switch (Token) { case EStructDeserializerBackendTokens::ArrayEnd: { // rehash the set/maps -> we're closing them check(CurrentState.Property); if (FSetProperty* SetProperty = CastField(CurrentState.Property)) { FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); SetHelper.Rehash(); } else if (FMapProperty* MapProperty = CastField(CurrentState.Property)) { FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); MapHelper.Rehash(); } else if (CurrentState.Property->ArrayDim > 1 && CurrentState.ArrayIndex < CurrentState.Property->ArrayDim) //-V522 - Property should never be null { // error: array entry not found in static array if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("The static array '%s' of size %d only had %d entries"), *CurrentState.Property->GetFName().ToString(), CurrentState.Property->ArrayDim, CurrentState.ArrayIndex); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } if (StateStack.Num() == 0) { UE_LOG(LogSerialization, Verbose, TEXT("Malformed input: Found ArrayEnd without matching ArrayStart")); return false; } CurrentState = StateStack.Pop(EAllowShrinking::No); } break; case EStructDeserializerBackendTokens::ArrayStart: { FReadState NewState; NewState.Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); if (NewState.Property != nullptr) { if (Policies.PropertyFilter && !Policies.PropertyFilter(NewState.Property, CurrentState.Property)) { Backend.SkipArray(); continue; } if (FSetProperty* SetProperty = CastField(NewState.Property)) { NewState.Data = SetProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.ArrayIndex = 0; } else if (FArrayProperty* ArrayProperty = CastField(NewState.Property)) { NewState.Data = CurrentState.Data; NewState.ArrayIndex = 0; } else if (FMapProperty* MapProperty = CastField(NewState.Property)) { NewState.Data = MapProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.ArrayIndex = 0; } // static array property else if (NewState.Property != nullptr) { NewState.Data = CurrentState.Data; NewState.ArrayIndex = 0; } NewState.TypeInfo = FindClass(NewState); StateStack.Push(CurrentState); CurrentState = NewState; } else { // error: array property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' does not exist"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } Backend.SkipArray(); } } break; case EStructDeserializerBackendTokens::Error: { return false; } case EStructDeserializerBackendTokens::Property: { // Set are serialized as array, so no property name will be set for each entry if (PropertyName.IsEmpty() && (CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FSetProperty::StaticClass())) { // handle set element FSetProperty* SetProperty = CastField(CurrentState.Property); FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); const int32 InternalIndex = ExpandForIndex(SetHelper, CurrentState.ArrayIndex); uint8* ElementPtr = SetHelper.GetElementPtr(InternalIndex); FProperty* Property = SetProperty->ElementProp; constexpr int32 ReadIndex = 0; //Pointer is offset so reading index is 0 if (!Backend.ReadProperty(Property, CurrentState.Property, ElementPtr, ReadIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in Set '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } ++CurrentState.ArrayIndex; } // Maps can be serialized as array, so no property name will be set for each entry. Each entry will be taken in order if (PropertyName.IsEmpty() && (CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FMapProperty::StaticClass())) { // handle map element FMapProperty* MapProperty = CastField(CurrentState.Property); FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); //When written as array, maps won't include the key, only values const int32 InternalIndex = ExpandForIndex(MapHelper, CurrentState.ArrayIndex); uint8* PairPtr = MapHelper.GetPairPtr(InternalIndex); FProperty* Property = MapProperty->ValueProp; constexpr int32 ReadIndex = 0; //Pointer is offset so reading index is 0 if (!Backend.ReadProperty(Property, CurrentState.Property, PairPtr, ReadIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in Set '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } ++CurrentState.ArrayIndex; } else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FOptionalProperty::StaticClass())) { FOptionalProperty* OptionalProperty = CastField(CurrentState.Property); FProperty* Property = OptionalProperty->GetValueProperty(); void* ValueData = OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(CurrentState.Data); if (!Backend.ReadProperty(Property, CurrentState.Property, ValueData, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("An item in optional '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } // Otherwise we are dealing with dynamic or static array else if (PropertyName.IsEmpty()) { // When reading the property, the deserialize behavior is to add element. We bypass that with the property FArrayProperty* ArrayProperty = CastField(CurrentState.Property); FProperty* Property = nullptr; void* DataAddress = CurrentState.Data; int32 CurrentArrayIndex = CurrentState.ArrayIndex; if (ArrayProperty != nullptr) { // dynamic array element FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); ArrayHelper.ExpandForIndex(CurrentState.ArrayIndex); Property = ArrayProperty->Inner; DataAddress = ArrayHelper.GetRawPtr(CurrentState.ArrayIndex); //Arraydim will be 1 for inner TArray properties. Offset the read data and keep index at 0 CurrentArrayIndex = 0; } else { checkSlow(CurrentState.Property); // static array element if (CurrentState.ArrayIndex >= 0 && CurrentState.ArrayIndex < CurrentState.Property->ArrayDim) { Property = CurrentState.Property; } else { //Too many entries in static Array UE_LOG(LogSerialization, Verbose, TEXT("Static array %s has dimension of %d and trying to read element %d"), *CurrentState.Property->GetFName().ToString(), CurrentState.Property->ArrayDim, CurrentState.ArrayIndex); continue; } } if (Property == nullptr) { // error: no meta data for array element if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Failed to serialize array element %i"), CurrentState.ArrayIndex); } return false; } else if (!Backend.ReadProperty(Property, nullptr, DataAddress, CurrentArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The array element '%s[%i]' could not be read (%s)"), *PropertyName, CurrentState.ArrayIndex, *Backend.GetDebugString()); } ++CurrentState.ArrayIndex; } else { // handle scalar property FProperty* Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); if (Property != nullptr) { if (Policies.PropertyFilter && !Policies.PropertyFilter(Property, CurrentState.Property)) { continue; } //Direct set element if (FSetProperty* SetProperty = CastField(Property)) { FScriptSetHelper SetHelper(SetProperty, SetProperty->ContainerPtrToValuePtr(CurrentState.Data)); const int32 InternalIndex = ExpandForIndex(SetHelper, CurrentState.ArrayIndex); Property = SetProperty->ElementProp; //Offset the pointer directly and give index 0 to be read so no offsetting is done during deserialization CurrentState.Data = SetHelper.GetElementPtr(InternalIndex); CurrentState.ArrayIndex = 0; if (!Backend.ReadProperty(Property, nullptr, CurrentState.Data, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } //An element of a set was written so rehash it SetHelper.Rehash(); continue; } else if (FMapProperty* MapProperty = CastField(Property)) { FScriptMapHelper MapHelper(MapProperty, MapProperty->ContainerPtrToValuePtr(CurrentState.Data)); const int32 InternalIndex = ExpandForIndex(MapHelper, CurrentState.ArrayIndex); Property = MapProperty->ValueProp; //Offset the pointer directly and give index 0 to be read so no offsetting is done during deserialization CurrentState.Data = MapHelper.GetPairPtr(InternalIndex); CurrentState.ArrayIndex = 0; if (!Backend.ReadProperty(Property, nullptr, CurrentState.Data, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } //An element of a map was written so rehash it MapHelper.Rehash(); continue; } else if (FArrayProperty* ArrayProperty = CastField(Property)) { FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); ArrayHelper.ExpandForIndex(CurrentState.ArrayIndex); Property = ArrayProperty->Inner; //Offset the pointer directly and give index 0 to be read so no offsetting is done during deserialization CurrentState.Data = ArrayHelper.GetRawPtr(CurrentState.ArrayIndex); CurrentState.ArrayIndex = 0; } else if (FOptionalProperty* OptionalProperty = CastField(Property)) { void* Data = OptionalProperty->ContainerPtrToValuePtr(CurrentState.Data); if (void* ValueData = OptionalProperty->GetValuePointerForReadOrReplaceIfSet(Data)) { Property = OptionalProperty->GetValueProperty(); //Offset the pointer directly and give index 0 to be read so no offsetting is done during deserialization CurrentState.Data = ValueData; CurrentState.ArrayIndex = 0; } else { // Not set UE_LOG(LogSerialization, Verbose, TEXT("TOptional %s is not set and is trying to be read"), *OptionalProperty->GetFName().ToString()); Backend.SkipStructure(); continue; } } if (!Backend.ReadProperty(Property, nullptr, CurrentState.Data, CurrentState.ArrayIndex)) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' could not be read (%s)"), *PropertyName, *Backend.GetDebugString()); } } else { // error: scalar property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("The property '%s' does not exist"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } } } break; case EStructDeserializerBackendTokens::StructureEnd: { // rehash if value was a map, set if(FMapProperty* MapProperty = CastField(CurrentState.Property)) { FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); MapHelper.Rehash(); } else if (FSetProperty* SetProperty = CastField(CurrentState.Property)) { FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); SetHelper.Rehash(); } // ending of root structure if (StateStack.Num() == 0) { return true; } CurrentState = StateStack.Pop(EAllowShrinking::No); } break; case EStructDeserializerBackendTokens::StructureStart: { FReadState NewState; if (PropertyName.IsEmpty()) { // skip root structure if (CurrentState.Property == nullptr) { check(StateStack.Num() == 0); continue; } // handle struct element inside set if (FSetProperty* SetProperty = CastField(CurrentState.Property)) { FScriptSetHelper SetHelper(SetProperty, CurrentState.Data); const int32 InternalIndex = ExpandForIndex(SetHelper, CurrentState.ArrayIndex); NewState.Property = SetProperty->ElementProp; NewState.Data = SetHelper.GetElementPtr(InternalIndex); NewState.ArrayIndex = 0; ++CurrentState.ArrayIndex; } else if (FMapProperty* MapProperty = CastField(CurrentState.Property)) { FScriptMapHelper MapHelper(MapProperty, CurrentState.Data); const int32 InternalIndex = ExpandForIndex(MapHelper, CurrentState.ArrayIndex); NewState.Property = MapProperty->ValueProp; NewState.Data = MapHelper.GetValuePtr(InternalIndex); NewState.ArrayIndex = 0; ++CurrentState.ArrayIndex; } // handle struct element inside array else if (FArrayProperty* ArrayProperty = CastField(CurrentState.Property)) { FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); ArrayHelper.ExpandForIndex(CurrentState.ArrayIndex); NewState.Property = ArrayProperty->Inner; NewState.Data = ArrayHelper.GetRawPtr(CurrentState.ArrayIndex); NewState.ArrayIndex = 0; ++CurrentState.ArrayIndex; } // handle struct element inside optional else if (FOptionalProperty* OptionalProperty = CastField(CurrentState.Property)) { void* Data = OptionalProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.Property = OptionalProperty->GetValueProperty(); NewState.Data = OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(Data); NewState.ArrayIndex = 0; } else { //Property was found so we might be in a static array of struct if (CurrentState.ArrayIndex >= 0 && CurrentState.ArrayIndex < CurrentState.Property->ArrayDim) { NewState.Property = CurrentState.Property; NewState.Data = NewState.Property->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); NewState.ArrayIndex = 0; ++CurrentState.ArrayIndex; } else { //Too many entries in static Array UE_LOG(LogSerialization, Verbose, TEXT("Static array %s has dimension of %d and trying to read element %d"), *CurrentState.Property->GetFName().ToString(), CurrentState.Property->ArrayDim, CurrentState.ArrayIndex); Backend.SkipStructure(); continue; } } } // handle map or struct element inside optional else if ((CurrentState.Property != nullptr) && (CurrentState.Property->GetClass() == FOptionalProperty::StaticClass())) { FOptionalProperty* OptionalProperty = CastFieldChecked(CurrentState.Property); NewState.Property = OptionalProperty->GetValueProperty(); NewState.Data = OptionalProperty->MarkSetAndGetInitializedValuePointerToReplace(CurrentState.Data); } else { NewState.Property = FindFProperty(CurrentState.TypeInfo, *PropertyName); // unrecognized property if (NewState.Property == nullptr) { // error: map or struct property not found if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Map, Set, or struct property '%s' not found"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } // handle map property start else if (FMapProperty* MapProperty = CastField(NewState.Property)) { if (FStructProperty* SetStructProperty = CastField(MapProperty->ValueProp)) { FScriptMapHelper MapHelper(MapProperty, MapProperty->ContainerPtrToValuePtr(CurrentState.Data)); const int32 InternalIndex = ExpandForIndex(MapHelper, CurrentState.ArrayIndex); // We're skipping a level directly so CurrentState becomes the outer (set) and NewState the inner (Element prop) CurrentState.Property = MapProperty; CurrentState.Data = MapProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.Property = SetStructProperty; NewState.Data = MapHelper.GetValuePtr(InternalIndex); NewState.ArrayIndex = 0; } } //Handle Set property entry else if (FSetProperty* SetProperty = CastField(NewState.Property)) { if (FStructProperty* SetStructProperty = CastField(SetProperty->ElementProp)) { FScriptSetHelper SetHelper(SetProperty, SetProperty->ContainerPtrToValuePtr(CurrentState.Data)); const int32 InternalIndex = ExpandForIndex(SetHelper, CurrentState.ArrayIndex); //We're skipping a level directly so CurrentState becomes the outer (set) and NewState the inner (Element prop) CurrentState.Property = SetProperty; CurrentState.Data = SetProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.Property = SetProperty->ElementProp; NewState.Data = SetHelper.GetElementPtr(InternalIndex); NewState.ArrayIndex = 0; } } //Handle array property entry else if (FArrayProperty* ArrayProperty = CastField(NewState.Property)) { if (FStructProperty* ArrayStructProperty = CastField(ArrayProperty->Inner)) { FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); ArrayHelper.ExpandForIndex(CurrentState.ArrayIndex); //NewState will become the outer when going through the properties. //When reading from Array, we expect to read from one level. When its a struct, it's not. CurrentState.Property = ArrayProperty; CurrentState.Data = ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data); NewState.Property = ArrayProperty->Inner; NewState.Data = ArrayHelper.GetRawPtr(CurrentState.ArrayIndex); NewState.ArrayIndex = 0; } } // handle optional property entry else if (FOptionalProperty* OptionalProperty = CastField(NewState.Property)) { NewState.Data = OptionalProperty->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); OptionalProperty->MarkUnset(NewState.Data); } // handle struct property else { if (CurrentState.ArrayIndex >= 0 && CurrentState.ArrayIndex < NewState.Property->ArrayDim) { NewState.Data = NewState.Property->ContainerPtrToValuePtr(CurrentState.Data, CurrentState.ArrayIndex); NewState.ArrayIndex = 0; } else { //Index out of bound UE_LOG(LogSerialization, Verbose, TEXT("Static array %s has dimension of %d and trying to read element %d"), *NewState.Property->GetFName().ToString(), NewState.Property->ArrayDim, CurrentState.ArrayIndex); Backend.SkipStructure(); continue; } } } if (NewState.Property != nullptr) { // skip struct property if property filter is set and rejects it if (Policies.PropertyFilter && !Policies.PropertyFilter(NewState.Property, CurrentState.Property)) { Backend.SkipStructure(); continue; } NewState.TypeInfo = FindClass(NewState); StateStack.Push(CurrentState); CurrentState = NewState; } else { // error: structured property not found Backend.SkipStructure(); if (Policies.MissingFields != EStructDeserializerErrorPolicies::Ignore) { UE_LOG(LogSerialization, Verbose, TEXT("Structured property '%s' not found"), *PropertyName); } if (Policies.MissingFields == EStructDeserializerErrorPolicies::Error) { return false; } } } default: continue; } } // root structure not completed return false; }