You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Previously, I had assumed that FProperty::InitializeValue() would initialize a simple member (eg. a float) to the correct value, but this appears not to be the case, rather values are zero-initialized and the CDO stores the correct default values. In the case of structs, InitializeStruct() does in fact initialize correctly. However, this means that we can't use GetMutableDefault() to get an instance of the UObject, since then we're changing what we're diffing against and serialize no values. Changed the usages of this pattern to use singletons instead. #jira UE-141150 #review-19900014 @lauren.barnes, @aditya.ravichandran #preflight 6267fa44a021c91a50f26a65 [CL 19919719 by sebastian nordgren in ue5-main branch]
787 lines
22 KiB
C++
787 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "EditorConfig.h"
|
|
#include "UObject/EnumProperty.h"
|
|
#include "UObject/TextProperty.h"
|
|
#include "UObject/UnrealType.h"
|
|
|
|
FEditorConfig::FEditorConfig()
|
|
{
|
|
JsonConfig = MakeShared<UE::FJsonConfig>();
|
|
}
|
|
|
|
void FEditorConfig::SetParent(TSharedPtr<FEditorConfig> InConfig)
|
|
{
|
|
ParentConfig = InConfig;
|
|
|
|
if (ParentConfig.IsValid())
|
|
{
|
|
JsonConfig->SetParent(ParentConfig->JsonConfig);
|
|
}
|
|
else
|
|
{
|
|
JsonConfig->SetParent(TSharedPtr<UE::FJsonConfig>());
|
|
}
|
|
}
|
|
|
|
bool FEditorConfig::LoadFromFile(FStringView FilePath)
|
|
{
|
|
JsonConfig = MakeShared<UE::FJsonConfig>();
|
|
if (!JsonConfig->LoadFromFile(FilePath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ParentConfig.IsValid())
|
|
{
|
|
JsonConfig->SetParent(ParentConfig->JsonConfig);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FEditorConfig::LoadFromString(FStringView Content)
|
|
{
|
|
JsonConfig = MakeShared<UE::FJsonConfig>();
|
|
if (!JsonConfig->LoadFromString(Content))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ParentConfig.IsValid())
|
|
{
|
|
JsonConfig->SetParent(ParentConfig->JsonConfig);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FEditorConfig::SaveToString(FString& OutResult) const
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return JsonConfig->SaveToString(OutResult);
|
|
}
|
|
|
|
bool FEditorConfig::SaveToFile(FStringView FilePath) const
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return JsonConfig->SaveToFile(FilePath);
|
|
}
|
|
|
|
bool FEditorConfig::HasOverride(FStringView Key) const
|
|
{
|
|
return JsonConfig->HasOverride(UE::FJsonPath(Key));
|
|
}
|
|
|
|
bool FEditorConfig::TryGetRootUObject(const UClass* Class, UObject* OutValue, EPropertyFilter Filter) const
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> UObjectData = JsonConfig->GetRootObject();
|
|
ReadUObject(UObjectData, Class, OutValue, Filter);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FEditorConfig::TryGetRootStruct(const UStruct* Struct, void* OutValue, EPropertyFilter Filter) const
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> StructData = JsonConfig->GetRootObject();
|
|
ReadStruct(StructData, Struct, OutValue, nullptr, Filter);
|
|
|
|
return true;
|
|
}
|
|
|
|
void FEditorConfig::SetRootUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> JsonObject = WriteUObject(Class, Instance, Filter);
|
|
JsonConfig->SetRootObject(JsonObject);
|
|
|
|
SetDirty();
|
|
}
|
|
|
|
void FEditorConfig::SetRootStruct(const UStruct* Struct, const void* Instance, EPropertyFilter Filter)
|
|
{
|
|
if (!IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> JsonObject = WriteStruct(Struct, Instance, nullptr, Filter);
|
|
JsonConfig->SetRootObject(JsonObject);
|
|
|
|
SetDirty();
|
|
}
|
|
|
|
void FEditorConfig::ReadStruct(const TSharedPtr<FJsonObject>& JsonObject, const UStruct* Struct, void* Instance, UObject* Owner, EPropertyFilter Filter)
|
|
{
|
|
FString TypeName;
|
|
JsonObject->TryGetStringField(TEXT("$type"), TypeName);
|
|
|
|
if (!TypeName.IsEmpty() && !ensureAlwaysMsgf(Struct->GetName().Equals(TypeName), TEXT("Type name mismatch in FEditorConfig::ReadUObject. Expected: %s, Actual: %s"), *Struct->GetName(), *TypeName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (TFieldIterator<FProperty> It(Struct); It; ++It)
|
|
{
|
|
const FProperty* Property = *It;
|
|
|
|
if (Filter == EPropertyFilter::MetadataOnly && !Property->HasMetaData("EditorConfig"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
void* DataPtr = Property->ContainerPtrToValuePtr<void>(Instance);
|
|
|
|
TSharedPtr<FJsonValue> Value = JsonObject->TryGetField(Property->GetName());
|
|
if (Value.IsValid())
|
|
{
|
|
ReadValue(Value, Property, DataPtr, Owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FEditorConfig::ReadUObject(const TSharedPtr<FJsonObject>& JsonObject, const UClass* Class, UObject* Instance, EPropertyFilter Filter)
|
|
{
|
|
FString TypeName;
|
|
JsonObject->TryGetStringField(TEXT("$type"), TypeName);
|
|
|
|
if (!TypeName.IsEmpty() && !ensureAlwaysMsgf(Class->GetName().Equals(TypeName), TEXT("Type name mismatch in FEditorConfig::ReadUObject. Expected: %s, Actual: %s"), *Class->GetName(), *TypeName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (TFieldIterator<FProperty> It(Class); It; ++It)
|
|
{
|
|
const FProperty* Property = *It;
|
|
|
|
if (Filter == EPropertyFilter::MetadataOnly && !Property->HasMetaData("EditorConfig"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
void* DataPtr = Property->ContainerPtrToValuePtr<void>(Instance);
|
|
|
|
TSharedPtr<FJsonValue> Value = JsonObject->TryGetField(Property->GetName());
|
|
if (Value.IsValid())
|
|
{
|
|
ReadValue(Value, Property, DataPtr, Instance);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FEditorConfig::ReadValue(const TSharedPtr<FJsonValue>& JsonValue, const FProperty* Property, void* DataPtr, UObject* Owner)
|
|
{
|
|
if (const FStrProperty* StrProperty = CastField<FStrProperty>(Property))
|
|
{
|
|
FString* Value = (FString*) DataPtr;
|
|
JsonValue->TryGetString(*Value);
|
|
return;
|
|
}
|
|
else if (const FNameProperty* NameProperty = CastField<FNameProperty>(Property))
|
|
{
|
|
FString TempValue;
|
|
JsonValue->TryGetString(TempValue);
|
|
|
|
*(FName*) DataPtr = *TempValue;
|
|
return;
|
|
}
|
|
else if (const FTextProperty* TextProperty = CastField<FTextProperty>(Property))
|
|
{
|
|
FString TempValue;
|
|
JsonValue->TryGetString(TempValue);
|
|
|
|
*(FText*) DataPtr = FText::FromString(TempValue);
|
|
return;
|
|
}
|
|
else if (const FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
|
|
{
|
|
bool Value = BoolProperty->GetDefaultPropertyValue();
|
|
if (JsonValue->TryGetBool(Value))
|
|
{
|
|
BoolProperty->SetPropertyValue(DataPtr, Value);
|
|
}
|
|
return;
|
|
}
|
|
else if (const FFloatProperty* FloatProperty = CastField<FFloatProperty>(Property))
|
|
{
|
|
float* Value = (float*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FDoubleProperty* DoubleProperty = CastField<FDoubleProperty>(Property))
|
|
{
|
|
double* Value = (double*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FInt8Property* Int8Property = CastField<FInt8Property>(Property))
|
|
{
|
|
int8* Value = (int8*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FInt16Property* Int16Property = CastField<FInt16Property>(Property))
|
|
{
|
|
int16* Value = (int16*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FIntProperty* Int32Property = CastField<FIntProperty>(Property))
|
|
{
|
|
int32* Value = (int32*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FInt64Property* Int64Property = CastField<FInt64Property>(Property))
|
|
{
|
|
int64* Value = (int64*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FByteProperty* ByteProperty = CastField<FByteProperty>(Property))
|
|
{
|
|
uint8* Value = (uint8*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FUInt16Property* Uint16Property = CastField<FUInt16Property>(Property))
|
|
{
|
|
uint16* Value = (uint16*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FUInt32Property* Uint32Property = CastField<FUInt32Property>(Property))
|
|
{
|
|
uint32* Value = (uint32*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FUInt64Property* Uint64Property = CastField<FUInt64Property>(Property))
|
|
{
|
|
uint64* Value = (uint64*) DataPtr;
|
|
JsonValue->TryGetNumber(*Value);
|
|
return;
|
|
}
|
|
else if (const FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
|
|
{
|
|
int64* Value = (int64*) DataPtr;
|
|
|
|
UEnum* Enum = EnumProperty->GetEnum();
|
|
if (Enum != nullptr)
|
|
{
|
|
FString ValueString;
|
|
if (JsonValue->TryGetString(ValueString))
|
|
{
|
|
int64 Index = Enum->GetIndexByNameString(ValueString);
|
|
if (Index != INDEX_NONE)
|
|
{
|
|
*Value = Enum->GetValueByIndex(Index);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(Property))
|
|
{
|
|
FString PathString;
|
|
if (JsonValue->TryGetString(PathString))
|
|
{
|
|
Property->ImportText_Direct(*PathString, DataPtr, Owner, 0);
|
|
}
|
|
return;
|
|
}
|
|
else if (const FStructProperty* StructProperty = CastField<FStructProperty>(Property))
|
|
{
|
|
const TSharedPtr<FJsonObject>* ObjectJsonValue;
|
|
if (JsonValue->TryGetObject(ObjectJsonValue))
|
|
{
|
|
ReadStruct(*ObjectJsonValue, StructProperty->Struct, DataPtr, Owner, EPropertyFilter::All);
|
|
}
|
|
return;
|
|
}
|
|
else if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
|
|
{
|
|
const TArray<TSharedPtr<FJsonValue>>* ArrayJsonValue;
|
|
if (JsonValue->TryGetArray(ArrayJsonValue))
|
|
{
|
|
FProperty* InnerProperty = ArrayProperty->Inner;
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, DataPtr);
|
|
|
|
ArrayHelper.EmptyAndAddValues(ArrayJsonValue->Num());
|
|
|
|
for (int32 Idx = 0; Idx < ArrayHelper.Num(); ++Idx)
|
|
{
|
|
TSharedPtr<FJsonValue> Value = (*ArrayJsonValue)[Idx];
|
|
ReadValue(Value, InnerProperty, ArrayHelper.GetRawPtr(Idx), Owner);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (const FSetProperty* SetProperty = CastField<FSetProperty>(Property))
|
|
{
|
|
const TArray<TSharedPtr<FJsonValue>>* SetJsonValue;
|
|
if (JsonValue->TryGetArray(SetJsonValue))
|
|
{
|
|
const FProperty* InnerProperty = SetProperty->ElementProp;
|
|
FScriptSetHelper SetHelper(SetProperty, DataPtr);
|
|
SetHelper.EmptyElements(SetJsonValue->Num());
|
|
|
|
// temporary buffer to read elements into
|
|
TArray<uint8> TempBuffer;
|
|
TempBuffer.AddUninitialized(InnerProperty->ElementSize);
|
|
|
|
for (int32 Idx = 0; Idx < SetJsonValue->Num(); ++Idx)
|
|
{
|
|
InnerProperty->InitializeValue(TempBuffer.GetData());
|
|
|
|
TSharedPtr<FJsonValue> Value = (*SetJsonValue)[Idx];
|
|
ReadValue(Value, InnerProperty, TempBuffer.GetData(), Owner);
|
|
|
|
SetHelper.AddElement(TempBuffer.GetData());
|
|
|
|
InnerProperty->DestroyValue(TempBuffer.GetData());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (const FMapProperty* MapProperty = CastField<FMapProperty>(Property))
|
|
{
|
|
const FProperty* KeyProperty = MapProperty->KeyProp;
|
|
const FProperty* ValueProperty = MapProperty->ValueProp;
|
|
|
|
FScriptMapHelper MapHelper(MapProperty, DataPtr);
|
|
|
|
// maps can either be stored as a simple JSON object, or as an array of { $key, $value } pairs
|
|
// first check for object storage - this will cover eg. numbers and strings as keys
|
|
const TSharedPtr<FJsonObject>* JsonObjectValue;
|
|
if (JsonValue->TryGetObject(JsonObjectValue))
|
|
{
|
|
MapHelper.EmptyValues((*JsonObjectValue)->Values.Num());
|
|
|
|
// temporary buffers to read elements into
|
|
TArray<uint8> TempKey;
|
|
TempKey.AddZeroed(KeyProperty->ElementSize);
|
|
|
|
TArray<uint8> TempValue;
|
|
TempValue.AddZeroed(ValueProperty->ElementSize);
|
|
|
|
for (const TPair<FString, TSharedPtr<FJsonValue>>& JsonPair : (*JsonObjectValue)->Values)
|
|
{
|
|
KeyProperty->InitializeValue(TempKey.GetData());
|
|
KeyProperty->ImportText_Direct(*JsonPair.Key, TempKey.GetData(), Owner, 0);
|
|
|
|
ValueProperty->InitializeValue(TempValue.GetData());
|
|
ReadValue(JsonPair.Value, ValueProperty, TempValue.GetData(), Owner);
|
|
|
|
MapHelper.AddPair(TempKey.GetData(), TempValue.GetData());
|
|
|
|
KeyProperty->DestroyValue(TempKey.GetData());
|
|
ValueProperty->DestroyValue(TempValue.GetData());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// then check for array storage, this will cover complex keys eg. custom structs
|
|
const TArray<TSharedPtr<FJsonValue>>* JsonArrayPtr = nullptr;
|
|
if (JsonValue->TryGetArray(JsonArrayPtr))
|
|
{
|
|
MapHelper.EmptyValues(JsonArrayPtr->Num());
|
|
|
|
// temporary buffers to read elements into
|
|
TArray<uint8> TempKey;
|
|
TempKey.AddUninitialized(KeyProperty->ElementSize);
|
|
|
|
TArray<uint8> TempValue;
|
|
TempValue.AddUninitialized(ValueProperty->ElementSize);
|
|
|
|
for (const TSharedPtr<FJsonValue>& JsonElement : *JsonArrayPtr)
|
|
{
|
|
TSharedPtr<FJsonObject>* JsonObject = nullptr;
|
|
if (JsonElement->TryGetObject(JsonObject))
|
|
{
|
|
TSharedPtr<FJsonValue> JsonKeyField = (*JsonObject)->TryGetField(TEXT("$key"));
|
|
TSharedPtr<FJsonValue> JsonValueField = (*JsonObject)->TryGetField(TEXT("$value"));
|
|
|
|
if (JsonKeyField.IsValid() && JsonValueField.IsValid())
|
|
{
|
|
KeyProperty->InitializeValue(TempKey.GetData());
|
|
ReadValue(JsonKeyField, KeyProperty, TempKey.GetData(), Owner);
|
|
|
|
ValueProperty->InitializeValue(TempValue.GetData());
|
|
ReadValue(JsonValueField, ValueProperty, TempValue.GetData(), Owner);
|
|
|
|
MapHelper.AddPair(TempKey.GetData(), TempValue.GetData());
|
|
|
|
KeyProperty->DestroyValue(TempKey.GetData());
|
|
ValueProperty->DestroyValue(TempValue.GetData());
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
ensureAlwaysMsgf(false, TEXT("Property type is unsupported: %s, type: %s"), *Property->GetPathName(), *Property->GetClass()->GetName());
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> FEditorConfig::WriteArray(const FArrayProperty* ArrayProperty, const void* DataPtr)
|
|
{
|
|
FProperty* InnerProperty = ArrayProperty->Inner;
|
|
FScriptArrayHelper ArrayHelper(ArrayProperty, DataPtr);
|
|
|
|
TArray<TSharedPtr<FJsonValue>> JsonValuesArray;
|
|
JsonValuesArray.Reserve(ArrayHelper.Num());
|
|
|
|
for (int32 Idx = 0; Idx < ArrayHelper.Num(); ++Idx)
|
|
{
|
|
if (ArrayHelper.IsValidIndex(Idx))
|
|
{
|
|
TSharedPtr<FJsonValue> ElementValue = WriteValue(InnerProperty, ArrayHelper.GetRawPtr(Idx), nullptr);
|
|
check(ElementValue.IsValid());
|
|
JsonValuesArray.Add(ElementValue);
|
|
}
|
|
}
|
|
|
|
return MakeShared<FJsonValueArray>(JsonValuesArray);
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> FEditorConfig::WriteSet(const FSetProperty* SetProperty, const void* DataPtr)
|
|
{
|
|
FProperty* InnerProperty = SetProperty->ElementProp;
|
|
FScriptSetHelper SetHelper(SetProperty, DataPtr);
|
|
|
|
TArray<TSharedPtr<FJsonValue>> JsonValuesArray;
|
|
JsonValuesArray.Reserve(SetHelper.Num());
|
|
|
|
for (int32 Idx = 0; Idx < SetHelper.Num(); ++Idx)
|
|
{
|
|
if (SetHelper.IsValidIndex(Idx))
|
|
{
|
|
TSharedPtr<FJsonValue> ElementValue = WriteValue(InnerProperty, SetHelper.GetElementPtr(Idx), nullptr);
|
|
check(ElementValue.IsValid());
|
|
JsonValuesArray.Add(ElementValue);
|
|
}
|
|
}
|
|
|
|
return MakeShared<FJsonValueArray>(JsonValuesArray);
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> FEditorConfig::WriteMap(const FMapProperty* MapProperty, const void* DataPtr)
|
|
{
|
|
TSharedPtr<FJsonValue> ResultValue;
|
|
|
|
FProperty* KeyProperty = MapProperty->KeyProp;
|
|
FProperty* ValueProperty = MapProperty->ValueProp;
|
|
|
|
FScriptMapHelper MapHelper(MapProperty, DataPtr);
|
|
|
|
if (MapHelper.Num() == 0)
|
|
{
|
|
ResultValue = MakeShared<FJsonValueObject>(MakeShared<FJsonObject>());
|
|
}
|
|
else
|
|
{
|
|
TArray<TSharedPtr<FJsonValue>> JsonKeysArray;
|
|
JsonKeysArray.Reserve(MapHelper.Num());
|
|
|
|
TArray<TSharedPtr<FJsonValue>> JsonValuesArray;
|
|
JsonValuesArray.Reserve(MapHelper.Num());
|
|
|
|
for (int32 Idx = 0; Idx < MapHelper.Num(); ++Idx)
|
|
{
|
|
if (MapHelper.IsValidIndex(Idx))
|
|
{
|
|
TSharedPtr<FJsonValue> JsonKey = WriteValue(KeyProperty, MapHelper.GetKeyPtr(Idx), nullptr);
|
|
check(JsonKey.IsValid());
|
|
JsonKeysArray.Add(JsonKey);
|
|
|
|
TSharedPtr<FJsonValue> JsonValue = WriteValue(ValueProperty, MapHelper.GetValuePtr(Idx), nullptr);
|
|
check(JsonValue.IsValid());
|
|
JsonValuesArray.Add(JsonValue);
|
|
}
|
|
}
|
|
|
|
// maps can either be stored as $key, $value pairs or, if the keys can be stringified, as a JSON object
|
|
// check Filter we should use based on the first element
|
|
EJson KeyType = JsonKeysArray[0]->Type;
|
|
if (KeyType == EJson::Object)
|
|
{
|
|
TArray<TSharedPtr<FJsonValue>> ResultArray;
|
|
ResultArray.Reserve(MapHelper.Num());
|
|
|
|
for (int32 Idx = 0; Idx < MapHelper.Num(); ++Idx)
|
|
{
|
|
if (MapHelper.IsValidIndex(Idx))
|
|
{
|
|
TSharedPtr<FJsonObject> ElementObject = MakeShared<FJsonObject>();
|
|
ElementObject->SetField(TEXT("$key"), JsonKeysArray[Idx]);
|
|
ElementObject->SetField(TEXT("$value"), JsonValuesArray[Idx]);
|
|
|
|
ResultArray.Add(MakeShared<FJsonValueObject>(ElementObject));
|
|
}
|
|
}
|
|
|
|
ResultValue = MakeShared<FJsonValueArray>(ResultArray);
|
|
}
|
|
else if (KeyType == EJson::Boolean ||
|
|
KeyType == EJson::Number ||
|
|
KeyType == EJson::String)
|
|
{
|
|
TSharedPtr<FJsonObject> ResultObject = MakeShared<FJsonObject>();
|
|
|
|
for (int32 Idx = 0; Idx < MapHelper.Num(); ++Idx)
|
|
{
|
|
if (MapHelper.IsValidIndex(Idx))
|
|
{
|
|
FString KeyString;
|
|
check(JsonKeysArray[Idx]->TryGetString(KeyString));
|
|
|
|
ResultObject->SetField(KeyString, JsonValuesArray[Idx]);
|
|
}
|
|
}
|
|
|
|
ResultValue = MakeShared<FJsonValueObject>(ResultObject);
|
|
}
|
|
|
|
ensureMsgf(ResultValue.IsValid(), TEXT("Map key type is invalid."));
|
|
}
|
|
|
|
return ResultValue;
|
|
}
|
|
|
|
TSharedPtr<FJsonValue> FEditorConfig::WriteValue(const FProperty* Property, const void* DataPtr, const void* DefaultPtr)
|
|
{
|
|
TSharedPtr<FJsonValue> ResultValue;
|
|
if (DefaultPtr != nullptr && Property->Identical(DataPtr, DefaultPtr))
|
|
{
|
|
return ResultValue;
|
|
}
|
|
|
|
if (const FStrProperty* StrProperty = CastField<FStrProperty>(Property))
|
|
{
|
|
FString* Value = (FString*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueString>(*Value);
|
|
}
|
|
else if (const FNameProperty* NameProperty = CastField<FNameProperty>(Property))
|
|
{
|
|
FName* Value = (FName*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueString>(Value->ToString());
|
|
}
|
|
else if (const FTextProperty* TextProperty = CastField<FTextProperty>(Property))
|
|
{
|
|
FText* Value = (FText*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueString>(Value->ToString());
|
|
}
|
|
else if (const FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
|
|
{
|
|
bool Value = BoolProperty->GetPropertyValue(DataPtr);
|
|
ResultValue = MakeShared<FJsonValueBoolean>(Value);
|
|
}
|
|
else if (const FFloatProperty* FloatProperty = CastField<FFloatProperty>(Property))
|
|
{
|
|
float* Value = (float*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FDoubleProperty* DoubleProperty = CastField<FDoubleProperty>(Property))
|
|
{
|
|
double* Value = (double*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FInt8Property* Int8Property = CastField<FInt8Property>(Property))
|
|
{
|
|
int8* Value = (int8*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FInt16Property* Int16Property = CastField<FInt16Property>(Property))
|
|
{
|
|
int16* Value = (int16*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FIntProperty* Int32Property = CastField<FIntProperty>(Property))
|
|
{
|
|
int32* Value = (int32*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FInt64Property* Int64Property = CastField<FInt64Property>(Property))
|
|
{
|
|
int64* Value = (int64*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FByteProperty* ByteProperty = CastField<FByteProperty>(Property))
|
|
{
|
|
uint8* Value = (uint8*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FUInt16Property* Uint16Property = CastField<FUInt16Property>(Property))
|
|
{
|
|
uint16* Value = (uint16*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FUInt32Property* Uint32Property = CastField<FUInt32Property>(Property))
|
|
{
|
|
uint32* Value = (uint32*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FUInt64Property* Uint64Property = CastField<FUInt64Property>(Property))
|
|
{
|
|
uint64* Value = (uint64*) DataPtr;
|
|
ResultValue = MakeShared<FJsonValueNumber>(*Value);
|
|
}
|
|
else if (const FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
|
|
{
|
|
int64* Value = (int64*) DataPtr;
|
|
|
|
UEnum* Enum = EnumProperty->GetEnum();
|
|
FName ValueName = Enum->GetNameByValue(*Value);
|
|
ResultValue = MakeShared<FJsonValueString>(ValueName.ToString());
|
|
}
|
|
else if (const FObjectPropertyBase* ObjectProperty = CastField<FObjectPropertyBase>(Property))
|
|
{
|
|
FString ObjectPath;
|
|
ObjectProperty->ExportTextItem_Direct(ObjectPath, DataPtr, nullptr, nullptr, PPF_None, nullptr);
|
|
ResultValue = MakeShared<FJsonValueString>(ObjectPath);
|
|
}
|
|
else if (const FStructProperty* StructProperty = CastField<FStructProperty>(Property))
|
|
{
|
|
ResultValue = MakeShared<FJsonValueObject>(WriteStruct(StructProperty->Struct, DataPtr, DefaultPtr, EPropertyFilter::All));
|
|
}
|
|
else if (const FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
|
|
{
|
|
ResultValue = WriteArray(ArrayProperty, DataPtr);
|
|
}
|
|
else if (const FSetProperty* SetProperty = CastField<FSetProperty>(Property))
|
|
{
|
|
ResultValue = WriteSet(SetProperty, DataPtr);
|
|
}
|
|
else if (const FMapProperty* MapProperty = CastField<FMapProperty>(Property))
|
|
{
|
|
ResultValue = WriteMap(MapProperty, DataPtr);
|
|
}
|
|
|
|
ensureAlwaysMsgf(ResultValue.IsValid(), TEXT("Property type is unsupported: %s, type: %s"), *Property->GetPathName(), *Property->GetClass()->GetName());
|
|
return ResultValue;
|
|
}
|
|
|
|
TSharedPtr<FJsonObject> FEditorConfig::WriteStruct(const UStruct* Struct, const void* Instance, const void* Defaults, EPropertyFilter Filter)
|
|
{
|
|
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
|
|
JsonObject->SetStringField(TEXT("$type"), Struct->GetName());
|
|
|
|
// Default initialize a struct here if we weren't passed one.
|
|
// This is necessary because structs can contain default initializations, eg.:
|
|
// struct Foo { float Bar = 5.0f; }
|
|
// The Bar FProperty does not store this value itself, only the struct does.
|
|
TArray<uint8> TempDefaults;
|
|
|
|
if (Defaults == nullptr)
|
|
{
|
|
TempDefaults.AddZeroed(Struct->GetStructureSize());
|
|
Struct->InitializeStruct(TempDefaults.GetData());
|
|
|
|
Defaults = TempDefaults.GetData();
|
|
}
|
|
|
|
bool bAnyWritten = false;
|
|
|
|
for (TFieldIterator<FProperty> It(Struct); It; ++It)
|
|
{
|
|
const FProperty* Property = *It;
|
|
|
|
if (Filter == EPropertyFilter::MetadataOnly && !Property->HasMetaData("EditorConfig"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bAnyWritten = true;
|
|
|
|
const void* ValuePtr = Property->ContainerPtrToValuePtr<void>(Instance);
|
|
const void* PropertyDefaultPtr = Property->ContainerPtrToValuePtr<void>(Defaults);
|
|
|
|
TSharedPtr<FJsonValue> PropertyValue = WriteValue(Property, ValuePtr, PropertyDefaultPtr);
|
|
if (PropertyValue.IsValid())
|
|
{
|
|
JsonObject->SetField(Property->GetName(), PropertyValue);
|
|
}
|
|
}
|
|
|
|
ensureAlwaysMsgf(bAnyWritten, TEXT("Struct type has no properties to serialize: %s"), *Struct->GetName());
|
|
|
|
return JsonObject;
|
|
}
|
|
|
|
/**
|
|
* This exists because of sparse class data that can exist for UObjects only, which is handled in ContainerPtrToValuePtr.
|
|
*/
|
|
TSharedPtr<FJsonObject> FEditorConfig::WriteUObject(const UClass* Class, const UObject* Instance, EPropertyFilter Filter)
|
|
{
|
|
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
|
|
JsonObject->SetStringField(TEXT("$type"), Class->GetName());
|
|
|
|
const UObject* Defaults = Class->GetDefaultObject();
|
|
|
|
bool bAnyWritten = false;
|
|
|
|
for (TFieldIterator<FProperty> It(Class); It; ++It)
|
|
{
|
|
const FProperty* Property = *It;
|
|
|
|
if (Filter == EPropertyFilter::MetadataOnly && !Property->HasMetaData("EditorConfig"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bAnyWritten = true;
|
|
|
|
const void* ValuePtr = Property->ContainerPtrToValuePtr<void>(Instance);
|
|
const void* PropertyDefaultPtr = Property->ContainerPtrToValuePtr<void>(Defaults);
|
|
|
|
TSharedPtr<FJsonValue> PropertyValue = WriteValue(Property, ValuePtr, PropertyDefaultPtr);
|
|
if (PropertyValue.IsValid())
|
|
{
|
|
JsonObject->SetField(Property->GetName(), PropertyValue);
|
|
}
|
|
}
|
|
|
|
ensureAlwaysMsgf(bAnyWritten, TEXT("UObject type has no properties to serialize: %s"), *Class->GetName());
|
|
|
|
return JsonObject;
|
|
}
|
|
|
|
void FEditorConfig::SetDirty()
|
|
{
|
|
if (!Dirty)
|
|
{
|
|
Dirty = true;
|
|
EditorConfigDirtiedEvent.Broadcast(*this);
|
|
}
|
|
}
|
|
|
|
void FEditorConfig::OnSaved()
|
|
{
|
|
Dirty = false;
|
|
}
|