|
|
@ -140,8 +140,8 @@ public: |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
FProperty* Prop = Stack.MostRecentProperty; // Gets the most recent property from the stack
|
|
|
FProperty* Prop = Stack.MostRecentProperty; |
|
|
void* ValPtr = Stack.MostRecentPropertyAddress; // Gets the address of the value of the most recent property
|
|
|
void* ValPtr = Stack.MostRecentPropertyAddress; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
if (FStructProperty* StructProperty = ExactCastField<FStructProperty>(Prop)) |
|
|
if (FStructProperty* StructProperty = ExactCastField<FStructProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
@ -170,51 +170,15 @@ public: |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
int64 Raw = Enum->GetValueByNameString(S.TrimStartAndEnd()); |
|
|
int64 Raw; |
|
|
if (Raw == INDEX_NONE) |
|
|
if (!ResolveEnumValueFromString(Enum, S, Raw)) |
|
|
{ |
|
|
|
|
|
// Try display names
|
|
|
|
|
|
const int32 Count = Enum->NumEnums(); |
|
|
|
|
|
const FString Trimmed = S.TrimStartAndEnd(); |
|
|
|
|
|
for (int32 i = 0; i < Count; ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
const FString InternalName = Enum->GetNameStringByIndex(i); |
|
|
|
|
|
if (InternalName.EndsWith(TEXT("_MAX"))) |
|
|
|
|
|
{ |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
const FString Display = Enum->GetDisplayNameTextByIndex(i).ToString(); |
|
|
|
|
|
if (Trimmed.Equals(Display, ESearchCase::IgnoreCase)) |
|
|
|
|
|
{ |
|
|
|
|
|
Raw = Enum->GetValueByIndex(i); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (Raw == INDEX_NONE) |
|
|
|
|
|
{ |
|
|
|
|
|
if (const UUserDefinedEnum* UD = Cast<UUserDefinedEnum>(Enum)) |
|
|
|
|
|
{ |
|
|
|
|
|
const FString Trimmed = S.TrimStartAndEnd(); |
|
|
|
|
|
for (const TTuple<FName, FText>& Pair : UD->DisplayNameMap) |
|
|
|
|
|
{ |
|
|
|
|
|
if (Pair.Value.ToString().Equals(Trimmed, ESearchCase::IgnoreCase)) |
|
|
|
|
|
{ |
|
|
|
|
|
Raw = Enum->GetValueByName(Pair.Key); |
|
|
|
|
|
if (Raw != INDEX_NONE) break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (Raw == INDEX_NONE) |
|
|
|
|
|
{ |
|
|
{ |
|
|
*(bool*)RESULT_PARAM = false; |
|
|
*(bool*)RESULT_PARAM = false; |
|
|
OnError.Broadcast(FString::Printf(TEXT("%s is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
OnError.Broadcast(FString::Printf(TEXT("%s is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
FNumericProperty* Underlying = EnumProperty->GetUnderlyingProperty(); |
|
|
EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(ValPtr, Raw); |
|
|
Underlying->SetIntPropertyValue(ValPtr, Raw); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -245,43 +209,8 @@ public: |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
int64 Raw = Enum->GetValueByNameString(S.TrimStartAndEnd()); |
|
|
int64 Raw; |
|
|
if (Raw == INDEX_NONE) |
|
|
if (!ResolveEnumValueFromString(Enum, S, Raw)) |
|
|
{ |
|
|
|
|
|
// Try display names
|
|
|
|
|
|
const int32 Count = Enum->NumEnums(); |
|
|
|
|
|
const FString Trimmed = S.TrimStartAndEnd(); |
|
|
|
|
|
for (int32 i = 0; i < Count; ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
const FString InternalName = Enum->GetNameStringByIndex(i); |
|
|
|
|
|
if (InternalName.EndsWith(TEXT("_MAX"))) |
|
|
|
|
|
{ |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
const FString Display = Enum->GetDisplayNameTextByIndex(i).ToString(); |
|
|
|
|
|
if (Trimmed.Equals(Display, ESearchCase::IgnoreCase)) |
|
|
|
|
|
{ |
|
|
|
|
|
Raw = Enum->GetValueByIndex(i); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (Raw == INDEX_NONE) |
|
|
|
|
|
{ |
|
|
|
|
|
if (const UUserDefinedEnum* UD = Cast<UUserDefinedEnum>(Enum)) |
|
|
|
|
|
{ |
|
|
|
|
|
const FString Trimmed = S.TrimStartAndEnd(); |
|
|
|
|
|
for (const TTuple<FName, FText>& Pair : UD->DisplayNameMap) |
|
|
|
|
|
{ |
|
|
|
|
|
if (Pair.Value.ToString().Equals(Trimmed, ESearchCase::IgnoreCase)) |
|
|
|
|
|
{ |
|
|
|
|
|
Raw = Enum->GetValueByName(Pair.Key); |
|
|
|
|
|
if (Raw != INDEX_NONE) break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (Raw == INDEX_NONE) |
|
|
|
|
|
{ |
|
|
{ |
|
|
*(bool*)RESULT_PARAM = false; |
|
|
*(bool*)RESULT_PARAM = false; |
|
|
OnError.Broadcast(FString::Printf(TEXT("%s is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
OnError.Broadcast(FString::Printf(TEXT("%s is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
@ -329,7 +258,6 @@ public: |
|
|
} |
|
|
} |
|
|
else if (FByteProperty* ByteProperty = ExactCastField<FByteProperty>(Prop)) |
|
|
else if (FByteProperty* ByteProperty = ExactCastField<FByteProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
// Casting to int8 failed, so we cast to int32. The worst thing that can happen is an implicit cast to int8
|
|
|
|
|
|
if (!SettingsJson->TryGetNumberField(Name, *(int32*)ValPtr)) |
|
|
if (!SettingsJson->TryGetNumberField(Name, *(int32*)ValPtr)) |
|
|
OnError.Broadcast("Byte fail"); |
|
|
OnError.Broadcast("Byte fail"); |
|
|
} |
|
|
} |
|
|
@ -349,7 +277,7 @@ public: |
|
|
const TArray<TSharedPtr<FJsonValue>>* Arr = nullptr; |
|
|
const TArray<TSharedPtr<FJsonValue>>* Arr = nullptr; |
|
|
if (Field->TryGetArray(Arr)) |
|
|
if (Field->TryGetArray(Arr)) |
|
|
{ |
|
|
{ |
|
|
// Handle arrays of user-defined structs
|
|
|
// Struct-Array
|
|
|
if (const FStructProperty* InnerStruct = CastField<FStructProperty>(ArrayProp->Inner)) |
|
|
if (const FStructProperty* InnerStruct = CastField<FStructProperty>(ArrayProp->Inner)) |
|
|
{ |
|
|
{ |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
@ -365,20 +293,16 @@ public: |
|
|
} |
|
|
} |
|
|
else if (Elem.IsValid() && Elem->Type == EJson::String) |
|
|
else if (Elem.IsValid() && Elem->Type == EJson::String) |
|
|
{ |
|
|
{ |
|
|
// Try to parse stringified JSON object
|
|
|
|
|
|
FString Raw = Elem->AsString(); |
|
|
FString Raw = Elem->AsString(); |
|
|
TSharedPtr<FJsonObject> ParsedObj; |
|
|
TSharedPtr<FJsonObject> ParsedObj; |
|
|
{ |
|
|
{ |
|
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Raw); |
|
|
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Raw); |
|
|
TSharedPtr<FJsonObject> TempObj; |
|
|
TSharedPtr<FJsonObject> TempObj; |
|
|
if (FJsonSerializer::Deserialize(Reader, TempObj) && TempObj.IsValid()) |
|
|
if (FJsonSerializer::Deserialize(Reader, TempObj) && TempObj.IsValid()) |
|
|
{ |
|
|
|
|
|
ParsedObj = TempObj; |
|
|
ParsedObj = TempObj; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (!ParsedObj.IsValid()) |
|
|
if (!ParsedObj.IsValid()) |
|
|
{ |
|
|
{ |
|
|
// Normalize common non-JSON inputs: single quotes and Python booleans
|
|
|
|
|
|
FString Normalized = Raw.Replace(TEXT("'"), TEXT("\"")); |
|
|
FString Normalized = Raw.Replace(TEXT("'"), TEXT("\"")); |
|
|
Normalized.ReplaceInline(TEXT(": True"), TEXT(": true")); |
|
|
Normalized.ReplaceInline(TEXT(": True"), TEXT(": true")); |
|
|
Normalized.ReplaceInline(TEXT(":true"), TEXT(": true")); |
|
|
Normalized.ReplaceInline(TEXT(":true"), TEXT(": true")); |
|
|
@ -387,19 +311,13 @@ public: |
|
|
TSharedRef<TJsonReader<>> Reader2 = TJsonReaderFactory<>::Create(Normalized); |
|
|
TSharedRef<TJsonReader<>> Reader2 = TJsonReaderFactory<>::Create(Normalized); |
|
|
TSharedPtr<FJsonObject> TempObj2; |
|
|
TSharedPtr<FJsonObject> TempObj2; |
|
|
if (FJsonSerializer::Deserialize(Reader2, TempObj2) && TempObj2.IsValid()) |
|
|
if (FJsonSerializer::Deserialize(Reader2, TempObj2) && TempObj2.IsValid()) |
|
|
{ |
|
|
|
|
|
ParsedObj = TempObj2; |
|
|
ParsedObj = TempObj2; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (ParsedObj.IsValid()) |
|
|
if (ParsedObj.IsValid()) |
|
|
{ |
|
|
|
|
|
GetNestedStructFromJson(InnerStruct->Struct, ParsedObj.Get(), ElemPtr); |
|
|
GetNestedStructFromJson(InnerStruct->Struct, ParsedObj.Get(), ElemPtr); |
|
|
} |
|
|
|
|
|
else |
|
|
else |
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is a string but not valid JSON object"), *Name, i)); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is a string but not valid JSON object"), *Name, i)); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is not an object (struct)"), *Name, i)); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is not an object (struct)"), *Name, i)); |
|
|
@ -407,7 +325,7 @@ public: |
|
|
} |
|
|
} |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
} |
|
|
} |
|
|
// Handle arrays of strings (existing behavior)
|
|
|
// String-Array
|
|
|
else if (const FStrProperty* InnerStr = CastField<FStrProperty>(ArrayProp->Inner)) |
|
|
else if (const FStrProperty* InnerStr = CastField<FStrProperty>(ArrayProp->Inner)) |
|
|
{ |
|
|
{ |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
@ -427,6 +345,76 @@ public: |
|
|
} |
|
|
} |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
} |
|
|
} |
|
|
|
|
|
// FEnumProperty-Array (UE5 modern enum)
|
|
|
|
|
|
else if (FEnumProperty* InnerEnum = CastField<FEnumProperty>(ArrayProp->Inner)) |
|
|
|
|
|
{ |
|
|
|
|
|
UEnum* Enum = InnerEnum->GetEnum(); |
|
|
|
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
|
|
|
Helper.Resize(Arr->Num()); |
|
|
|
|
|
bool bAllOk = true; |
|
|
|
|
|
for (int32 i = 0; i < Arr->Num(); ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
void* ElemPtr = Helper.GetRawPtr(i); |
|
|
|
|
|
const TSharedPtr<FJsonValue>& Elem = (*Arr)[i]; |
|
|
|
|
|
if (!Elem.IsValid() || Elem->Type != EJson::String) |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is not a string (enum)"), *Name, i)); |
|
|
|
|
|
bAllOk = false; |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
const FString S = Elem->AsString().TrimStartAndEnd(); |
|
|
|
|
|
int64 Raw; |
|
|
|
|
|
if (!ResolveEnumValueFromString(Enum, S, Raw)) |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("'%s' is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
|
|
|
bAllOk = false; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
InnerEnum->GetUnderlyingProperty()->SetIntPropertyValue(ElemPtr, Raw); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
*(bool*)RESULT_PARAM = bAllOk; |
|
|
|
|
|
} |
|
|
|
|
|
// FNumericProperty-Array mit Byte-Enum
|
|
|
|
|
|
else if (FNumericProperty* InnerNum = CastField<FNumericProperty>(ArrayProp->Inner)) |
|
|
|
|
|
{ |
|
|
|
|
|
if (InnerNum->IsEnum()) |
|
|
|
|
|
{ |
|
|
|
|
|
UEnum* Enum = InnerNum->GetIntPropertyEnum(); |
|
|
|
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
|
|
|
Helper.Resize(Arr->Num()); |
|
|
|
|
|
bool bAllOk = true; |
|
|
|
|
|
for (int32 i = 0; i < Arr->Num(); ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
void* ElemPtr = Helper.GetRawPtr(i); |
|
|
|
|
|
const TSharedPtr<FJsonValue>& Elem = (*Arr)[i]; |
|
|
|
|
|
if (!Elem.IsValid() || Elem->Type != EJson::String) |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("Array '%s' element %d is not a string (byte-enum)"), *Name, i)); |
|
|
|
|
|
bAllOk = false; |
|
|
|
|
|
continue; |
|
|
|
|
|
} |
|
|
|
|
|
const FString S = Elem->AsString().TrimStartAndEnd(); |
|
|
|
|
|
int64 Raw; |
|
|
|
|
|
if (!ResolveEnumValueFromString(Enum, S, Raw)) |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("'%s' is not a valid value of enum %s"), *S, *Enum->GetAuthoredName())); |
|
|
|
|
|
bAllOk = false; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
InnerNum->SetIntPropertyValue(ElemPtr, Raw); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
*(bool*)RESULT_PARAM = bAllOk; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
|
|
|
*(bool*)RESULT_PARAM = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
@ -460,8 +448,8 @@ public: |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
FProperty* Prop = Stack.MostRecentProperty; // Gets the most recent property from the stack
|
|
|
FProperty* Prop = Stack.MostRecentProperty; |
|
|
void* ValPtr = Stack.MostRecentPropertyAddress; // Gets the address of the value of the most recent property
|
|
|
void* ValPtr = Stack.MostRecentPropertyAddress; |
|
|
if (Field->Type == EJson::Object) |
|
|
if (Field->Type == EJson::Object) |
|
|
{ |
|
|
{ |
|
|
if (FStructProperty* StructProperty = ExactCastField<FStructProperty>(Prop)) |
|
|
if (FStructProperty* StructProperty = ExactCastField<FStructProperty>(Prop)) |
|
|
@ -483,32 +471,53 @@ public: |
|
|
} |
|
|
} |
|
|
else if (Field->Type == EJson::String) |
|
|
else if (Field->Type == EJson::String) |
|
|
{ |
|
|
{ |
|
|
// check if the corresponding property in the settings struct is an enum
|
|
|
|
|
|
if (UEnum* SettingsEnum = GetEnumByPropertyName(Name)) |
|
|
if (UEnum* SettingsEnum = GetEnumByPropertyName(Name)) |
|
|
{ |
|
|
{ |
|
|
// check if InVal is an enum
|
|
|
|
|
|
if (FEnumProperty* EnumProperty = ExactCastField<FEnumProperty>(Prop)) |
|
|
if (FEnumProperty* EnumProperty = ExactCastField<FEnumProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
if (EnumProperty->GetEnum() == SettingsEnum) |
|
|
if (EnumProperty->GetEnum() == SettingsEnum) |
|
|
{ |
|
|
{ |
|
|
SettingsJson->SetStringField(Name, SettingsEnum->GetNameStringByIndex(*(int8*)ValPtr)); |
|
|
const int64 Raw = EnumProperty->GetUnderlyingProperty()->GetSignedIntPropertyValue(ValPtr); |
|
|
|
|
|
FString OutStr; |
|
|
|
|
|
if (const UUserDefinedEnum* UD = Cast<UUserDefinedEnum>(SettingsEnum)) |
|
|
|
|
|
{ |
|
|
|
|
|
const FName Key = SettingsEnum->GetNameByValue(Raw); |
|
|
|
|
|
if (const FText* P = UD->DisplayNameMap.Find(Key)) OutStr = P->ToString(); |
|
|
|
|
|
} |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = SettingsEnum->GetDisplayNameTextByValue(Raw).ToString(); |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = SettingsEnum->GetNameStringByValue(Raw); |
|
|
|
|
|
SettingsJson->SetStringField(Name, OutStr); |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
OnError.Broadcast(FString::Printf(TEXT("Enum mismatch for property %s"), *Name)); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Enum mismatch for property %s"), *Name)); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// otherwise check if InVal is numerical
|
|
|
|
|
|
else if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Prop)) |
|
|
else if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
// check if InVal is a valid value of the respective enum
|
|
|
if (!NumericProperty->IsFloatingPoint()) |
|
|
if (!NumericProperty->IsFloatingPoint() && 0 <= *(uint8*)ValPtr && *(uint8*)ValPtr < SettingsEnum->GetMaxEnumValue()) |
|
|
|
|
|
{ |
|
|
{ |
|
|
SettingsJson->SetStringField(Name, SettingsEnum->GetNameStringByIndex(*(int8*)ValPtr)); |
|
|
const int64 Raw = NumericProperty->GetSignedIntPropertyValue(ValPtr); |
|
|
|
|
|
if (SettingsEnum->IsValidEnumValue(Raw)) |
|
|
|
|
|
{ |
|
|
|
|
|
FString OutStr; |
|
|
|
|
|
if (const UUserDefinedEnum* UD = Cast<UUserDefinedEnum>(SettingsEnum)) |
|
|
|
|
|
{ |
|
|
|
|
|
const FName Key = SettingsEnum->GetNameByValue(Raw); |
|
|
|
|
|
if (const FText* P = UD->DisplayNameMap.Find(Key)) OutStr = P->ToString(); |
|
|
|
|
|
} |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = SettingsEnum->GetDisplayNameTextByValue(Raw).ToString(); |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = SettingsEnum->GetNameStringByValue(Raw); |
|
|
|
|
|
SettingsJson->SetStringField(Name, OutStr); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("%lld is not a valid value of enum %s"), Raw, *SettingsEnum->GetAuthoredName())); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
OnError.Broadcast(FString::Printf(TEXT("%d is not a valid value of enum %s"), *(int32*)ValPtr, *SettingsEnum->GetAuthoredName())); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Type mismatch for property %s"), *Name)); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
@ -535,7 +544,6 @@ public: |
|
|
} |
|
|
} |
|
|
else if (Field->Type == EJson::Number) |
|
|
else if (Field->Type == EJson::Number) |
|
|
{ |
|
|
{ |
|
|
|
|
|
|
|
|
if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Prop)) |
|
|
if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
if (NumericProperty->IsFloatingPoint()) |
|
|
if (NumericProperty->IsFloatingPoint()) |
|
|
@ -561,10 +569,9 @@ public: |
|
|
} |
|
|
} |
|
|
else if (Field->Type == EJson::Array) |
|
|
else if (Field->Type == EJson::Array) |
|
|
{ |
|
|
{ |
|
|
// Support TArray<FString> and TArray<Struct>
|
|
|
|
|
|
if (FArrayProperty* ArrayProp = ExactCastField<FArrayProperty>(Prop)) |
|
|
if (FArrayProperty* ArrayProp = ExactCastField<FArrayProperty>(Prop)) |
|
|
{ |
|
|
{ |
|
|
// Array of structs -> serialize each element to a JSON object
|
|
|
// Struct-Array
|
|
|
if (const FStructProperty* InnerStruct = CastField<FStructProperty>(ArrayProp->Inner)) |
|
|
if (const FStructProperty* InnerStruct = CastField<FStructProperty>(ArrayProp->Inner)) |
|
|
{ |
|
|
{ |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
@ -580,7 +587,7 @@ public: |
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
} |
|
|
} |
|
|
// Array of strings (existing behavior)
|
|
|
// String-Array
|
|
|
else if (const FStrProperty* InnerStr = CastField<FStrProperty>(ArrayProp->Inner)) |
|
|
else if (const FStrProperty* InnerStr = CastField<FStrProperty>(ArrayProp->Inner)) |
|
|
{ |
|
|
{ |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
@ -595,6 +602,56 @@ public: |
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
*(bool*)RESULT_PARAM = true; |
|
|
} |
|
|
} |
|
|
|
|
|
// FEnumProperty-Array (UE5 modern enum)
|
|
|
|
|
|
else if (FEnumProperty* InnerEnum = CastField<FEnumProperty>(ArrayProp->Inner)) |
|
|
|
|
|
{ |
|
|
|
|
|
UEnum* Enum = InnerEnum->GetEnum(); |
|
|
|
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
|
|
|
TArray<TSharedPtr<FJsonValue>> Out; |
|
|
|
|
|
Out.Reserve(Helper.Num()); |
|
|
|
|
|
for (int32 i = 0; i < Helper.Num(); ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
void* ElemPtr = Helper.GetRawPtr(i); |
|
|
|
|
|
const int64 Raw = InnerEnum->GetUnderlyingProperty()->GetSignedIntPropertyValue(ElemPtr); |
|
|
|
|
|
FString OutStr; |
|
|
|
|
|
if (const UUserDefinedEnum* UD = Cast<UUserDefinedEnum>(Enum)) |
|
|
|
|
|
{ |
|
|
|
|
|
const FName Key = Enum->GetNameByValue(Raw); |
|
|
|
|
|
if (const FText* P = UD->DisplayNameMap.Find(Key)) OutStr = P->ToString(); |
|
|
|
|
|
} |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = Enum->GetDisplayNameTextByValue(Raw).ToString(); |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = Enum->GetNameStringByValue(Raw); |
|
|
|
|
|
Out.Add(MakeShared<FJsonValueString>(OutStr)); |
|
|
|
|
|
} |
|
|
|
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
|
|
|
*(bool*)RESULT_PARAM = true; |
|
|
|
|
|
} |
|
|
|
|
|
// FNumericProperty-Array mit Byte-Enum
|
|
|
|
|
|
else if (FNumericProperty* InnerNum = CastField<FNumericProperty>(ArrayProp->Inner)) |
|
|
|
|
|
{ |
|
|
|
|
|
if (InnerNum->IsEnum()) |
|
|
|
|
|
{ |
|
|
|
|
|
UEnum* Enum = InnerNum->GetIntPropertyEnum(); |
|
|
|
|
|
FScriptArrayHelper Helper(ArrayProp, ValPtr); |
|
|
|
|
|
TArray<TSharedPtr<FJsonValue>> Out; |
|
|
|
|
|
Out.Reserve(Helper.Num()); |
|
|
|
|
|
for (int32 i = 0; i < Helper.Num(); ++i) |
|
|
|
|
|
{ |
|
|
|
|
|
void* ElemPtr = Helper.GetRawPtr(i); |
|
|
|
|
|
const int64 Raw = InnerNum->GetSignedIntPropertyValue(ElemPtr); |
|
|
|
|
|
FString OutStr = Enum->GetDisplayNameTextByValue(Raw).ToString(); |
|
|
|
|
|
if (OutStr.IsEmpty()) OutStr = Enum->GetNameStringByValue(Raw); |
|
|
|
|
|
Out.Add(MakeShared<FJsonValueString>(OutStr)); |
|
|
|
|
|
} |
|
|
|
|
|
SettingsJson->SetArrayField(Name, Out); |
|
|
|
|
|
*(bool*)RESULT_PARAM = true; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
|
|
|
*(bool*)RESULT_PARAM = false; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
OnError.Broadcast(FString::Printf(TEXT("Unsupported array inner type for '%s'"), *Name)); |
|
|
@ -702,8 +759,10 @@ public: |
|
|
virtual void Deinitialize() override; |
|
|
virtual void Deinitialize() override; |
|
|
|
|
|
|
|
|
private: |
|
|
private: |
|
|
|
|
|
static bool ResolveEnumValueFromString(const UEnum* Enum, const FString& InString, int64& OutValue); |
|
|
static FString GetSchemaType(const FProperty* Prop); |
|
|
static FString GetSchemaType(const FProperty* Prop); |
|
|
static void CollectEnumValues(const FProperty* Prop, TArray<FString>& OutValues); |
|
|
static void CollectEnumValues(const FProperty* Prop, TArray<FString>& OutValues); |
|
|
|
|
|
static TSharedPtr<FJsonObject> BuildFieldSchema(const FProperty* Prop); |
|
|
|
|
|
|
|
|
static TSharedPtr<FJsonObject> RotatorToJson(const FRotator& R); |
|
|
static TSharedPtr<FJsonObject> RotatorToJson(const FRotator& R); |
|
|
static bool JsonToRotator(const FJsonObject& Obj, FRotator& Out); |
|
|
static bool JsonToRotator(const FJsonObject& Obj, FRotator& Out); |
|
|
|