// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. #include "SerializationPrivatePCH.h" #include "IStructSerializerBackend.h" #include "StructSerializer.h" /* Internal helpers *****************************************************************************/ namespace StructSerializer { // Structure for the write state stack. struct FWriteState { // Holds a pointer to the property's data. const void* Data; // Holds a flag indicating whether the property has been processed. bool HasBeenProcessed; // Holds the property's meta data. UProperty* Property; // Holds a pointer to the UStruct describing the data. UStruct* TypeInfo; }; // Gets the value from the given property. template PropertyType* GetPropertyValue( const FWriteState& State, UProperty* Property ) { PropertyType* ValuePtr = nullptr; UArrayProperty* ArrayProperty = Cast(State.Property); if (ArrayProperty) { check(ArrayProperty->Inner == Property); FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->template ContainerPtrToValuePtr(State.Data)); int32 Index = ArrayHelper.AddValue(); ValuePtr = (PropertyType*)ArrayHelper.GetRawPtr( Index ); } else { UPropertyType* TypedProperty = Cast(Property); check(TypedProperty != nullptr); ValuePtr = TypedProperty->template ContainerPtrToValuePtr(State.Data); } return ValuePtr; } } /* FStructSerializer static interface *****************************************************************************/ void FStructSerializer::Serialize( const void* Struct, UStruct& TypeInfo, IStructSerializerBackend& Backend, const FStructSerializerPolicies& Policies ) { using namespace StructSerializer; check(Struct != nullptr); // initialize serialization TArray StateStack; { FWriteState NewState; NewState.Data = Struct; NewState.Property = nullptr; NewState.TypeInfo = &TypeInfo; NewState.HasBeenProcessed = false; StateStack.Push(NewState); } // process state stack while (StateStack.Num() > 0) { FWriteState CurrentState = StateStack.Pop(); // structures if ((CurrentState.Property == nullptr) || (CurrentState.TypeInfo == UStructProperty::StaticClass())) { if (!CurrentState.HasBeenProcessed) { const void* NewData = CurrentState.Data; // write object start if (CurrentState.Property == nullptr) { Backend.BeginStructure(CurrentState.TypeInfo); } else { UObject* Outer = CurrentState.Property->GetOuter(); if ((Outer == nullptr) || (Outer->GetClass() != UArrayProperty::StaticClass())) { NewData = CurrentState.Property->ContainerPtrToValuePtr(CurrentState.Data); } Backend.BeginStructure(CurrentState.Property); } CurrentState.HasBeenProcessed = true; StateStack.Push(CurrentState); // serialize fields if (CurrentState.Property != nullptr) { UStructProperty* StructProperty = Cast(CurrentState.Property); if (StructProperty != nullptr) { CurrentState.TypeInfo = StructProperty->Struct; } else { UObjectPropertyBase* ObjectProperty = Cast(CurrentState.Property); if (ObjectProperty != nullptr) { CurrentState.TypeInfo = ObjectProperty->PropertyClass; } } } TArray NewStates; for (TFieldIterator It(CurrentState.TypeInfo, EFieldIteratorFlags::IncludeSuper); It; ++It) { FWriteState NewState; NewState.Data = NewData; NewState.Property = *It; NewState.TypeInfo = It->GetClass(); NewState.HasBeenProcessed = false; NewStates.Add(NewState); } // push child properties on stack (in reverse order) for (int Index = NewStates.Num() - 1; Index >= 0; --Index) { StateStack.Push(NewStates[Index]); } } else { Backend.EndStructure(); } } // dynamic arrays else if (CurrentState.TypeInfo == UArrayProperty::StaticClass()) { if (!CurrentState.HasBeenProcessed) { Backend.BeginArray(CurrentState.Property); CurrentState.HasBeenProcessed = true; StateStack.Push(CurrentState); UArrayProperty* ArrayProperty = Cast(CurrentState.Property); FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->ContainerPtrToValuePtr(CurrentState.Data)); UProperty* Inner = ArrayProperty->Inner; // push elements on stack (in reverse order) for (int Index = ArrayHelper.Num() - 1; Index >= 0; --Index) { FWriteState NewState; NewState.Data = ArrayHelper.GetRawPtr(Index); NewState.Property = Inner; NewState.TypeInfo = Inner->GetClass(); NewState.HasBeenProcessed = false; StateStack.Push(NewState); } } else { Backend.EndArray(CurrentState.Property); } } // static arrays else if (CurrentState.Property->ArrayDim > 1) { Backend.BeginArray(CurrentState.Property); for (int32 ArrayIndex = 0; ArrayIndex < CurrentState.Property->ArrayDim; ++ArrayIndex) { Backend.WriteProperty(CurrentState.Property, CurrentState.Data, CurrentState.TypeInfo, ArrayIndex); } Backend.EndArray(CurrentState.Property); } // all other properties else { Backend.WriteProperty(CurrentState.Property, CurrentState.Data, CurrentState.TypeInfo); } } }