631 lines
17 KiB
C++
631 lines
17 KiB
C++
#include "WingProperty.h"
|
|
#include "WingComponent.h"
|
|
#include "WingUtils.h"
|
|
#include "WingBasics.h"
|
|
#include "WingServer.h"
|
|
#include "WingTypes.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "MaterialGraph/MaterialGraphNode.h"
|
|
#include "Components/Widget.h"
|
|
#include "Components/PanelSlot.h"
|
|
#include "EdGraph/EdGraphPin.h"
|
|
#include "UObject/EnumProperty.h"
|
|
#include "Dom/JsonValue.h"
|
|
|
|
|
|
bool FWingProperty::SetObject(UObject *Obj, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop);
|
|
if (!OProp)
|
|
{
|
|
PrintExpectsReceived(TEXT("object"), Errors);
|
|
return false;
|
|
}
|
|
if (Obj)
|
|
{
|
|
if (!Obj->IsA(OProp->PropertyClass))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' expects %s, but received %s\n"),
|
|
*WingUtils::FormatName(Prop), *OProp->PropertyClass->GetName(), *Obj->GetClass()->GetName());
|
|
return false;
|
|
}
|
|
if (FClassProperty *CProp = CastField<FClassProperty>(Prop))
|
|
{
|
|
UClass *Class = CastChecked<UClass>(Obj);
|
|
if (!Class->IsChildOf(CProp->MetaClass))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' expects a subclass of %s, but received %s\n"),
|
|
*WingUtils::FormatName(Prop), *CProp->MetaClass->GetName(), *Class->GetName());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
OProp->SetObjectPropertyValue_InContainer(Container, Obj);
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::SetDoubleInternal(
|
|
FNumericProperty *NProp, void *Container, double D, WingOut Errors)
|
|
{
|
|
uint8 buffer[16];
|
|
NProp->SetFloatingPointPropertyValue(buffer, D);
|
|
if (!FMath::IsFinite(NProp->GetFloatingPointPropertyValue(buffer)))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lf\n"),
|
|
*WingUtils::FormatName(NProp), *NProp->GetCPPType(), D);
|
|
return false;
|
|
}
|
|
NProp->SetValue_InContainer(Container, buffer);
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::SetInt64Internal(
|
|
FNumericProperty *NProp, void *Container, int64 I, WingOut Errors)
|
|
{
|
|
uint8 buffer[16];
|
|
if ((I < 0) && IsUnsigned(NProp))
|
|
{
|
|
Errors.Printf(TEXT(
|
|
"ERROR: Cannot store signed %lld in unsigned property %s\n"),
|
|
I, *WingUtils::FormatName(NProp));
|
|
return false;
|
|
}
|
|
NProp->SetIntPropertyValue(buffer, I);
|
|
if (NProp->GetSignedIntPropertyValue(buffer) != I)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"),
|
|
*WingUtils::FormatName(NProp), *NProp->GetCPPType(), I);
|
|
return false;
|
|
}
|
|
NProp->SetValue_InContainer(Container, buffer);
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::SetDouble(double D, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
|
|
if (NProp == nullptr)
|
|
{
|
|
PrintExpectsReceived(TEXT("double"), Errors);
|
|
return false;
|
|
}
|
|
else if (NProp->IsFloatingPoint())
|
|
{
|
|
return SetDoubleInternal(NProp, Container, D, Errors);
|
|
}
|
|
else
|
|
{
|
|
int64 I;
|
|
if (!LosslessDoubleToInt64(D, I, Errors)) return false;
|
|
return SetInt64Internal(NProp, Container, I, Errors);
|
|
}
|
|
}
|
|
|
|
bool FWingProperty::SetInt64(int64 I, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
|
|
if (NProp == nullptr)
|
|
{
|
|
PrintExpectsReceived(TEXT("int"), Errors);
|
|
return false;
|
|
}
|
|
else if (NProp->IsFloatingPoint())
|
|
{
|
|
double D;
|
|
if (!LosslessInt64ToDouble(I, D, Errors)) return false;
|
|
return SetDoubleInternal(NProp, Container, D, Errors);
|
|
}
|
|
else
|
|
{
|
|
return SetInt64Internal(NProp, Container, I, Errors);
|
|
}
|
|
}
|
|
|
|
bool FWingProperty::SetBool(bool B, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
|
|
{
|
|
Prop->SetValue_InContainer(Container, &B);
|
|
return true;
|
|
}
|
|
PrintExpectsReceived(TEXT("boolean"), Errors);
|
|
return false;
|
|
}
|
|
|
|
bool FWingProperty::SetText(FString Value, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
|
|
// Pin types get parsed by UWingTypes.
|
|
if (IsPinTypeProperty(Prop))
|
|
{
|
|
FEdGraphPinType PinType;
|
|
UWingTypes::Requirements Req;
|
|
Req.BlueprintType = true;
|
|
Req.Blueprintable = false;
|
|
Req.AllowContainer = true;
|
|
if (!UWingTypes::TextToType(Value, PinType, Req, Errors)) return false;
|
|
Prop->SetValue_InContainer(Container, &PinType);
|
|
return true;
|
|
}
|
|
|
|
// Class properties get parsed by UWingTypes.
|
|
if (FClassProperty *CProp = CastField<FClassProperty>(Prop))
|
|
{
|
|
UWingTypes::Requirements Req;
|
|
Req.IsChildOf = CProp->MetaClass;
|
|
if (CProp->MetaClass == nullptr) Req.IsChildOf = UObject::StaticClass();
|
|
Req.AllowNone = true;
|
|
Req.AllowContainer = false;
|
|
FEdGraphPinType PinType;
|
|
if (!UWingTypes::TextToType(Value, PinType, Req, Errors)) return false;
|
|
CProp->SetObjectPropertyValue_InContainer(Container,
|
|
Cast<UClass>(PinType.PinSubCategoryObject.Get()));
|
|
return true;
|
|
}
|
|
|
|
// If it's an enum type, use our parsing routine which is smarter about
|
|
// prefixes than ImportText. We canonicalize the string, and then send
|
|
// it onward to ImportText.
|
|
UEnum *Enum = nullptr;
|
|
if (FByteProperty* ByteProp = CastField<FByteProperty>(Prop))
|
|
Enum = ByteProp->Enum;
|
|
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
|
|
Enum = EnumProp->GetEnum();
|
|
if (Enum != nullptr)
|
|
{
|
|
int64 EnumValue;
|
|
if (!WingUtils::StringToEnum(Enum, Value, EnumValue, Errors)) return false;
|
|
Value = Enum->GetNameStringByValue(EnumValue);
|
|
}
|
|
|
|
// Now Use ImportText
|
|
const TCHAR* Result = Prop->ImportText_InContainer(*Value, Container, nullptr, PPF_None);
|
|
if ((!Result) || (*Result != 0))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
|
|
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
|
|
return false;
|
|
}
|
|
if (!CheckImportTextResult(Value, Errors)) return false;
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::SetJson(const FJsonValue &JsonValue, WingOut Errors) const
|
|
{
|
|
if (!CheckEditable(Errors)) return false;
|
|
|
|
if (JsonValue.Type == EJson::String)
|
|
{
|
|
return SetText(JsonValue.AsString(), Errors);
|
|
}
|
|
|
|
if (JsonValue.Type == EJson::Number)
|
|
{
|
|
return SetDouble(JsonValue.AsNumber(), Errors);
|
|
}
|
|
|
|
if (JsonValue.Type == EJson::Boolean)
|
|
{
|
|
return SetBool(JsonValue.AsBool(), Errors);
|
|
}
|
|
|
|
if (JsonValue.Type == EJson::Object)
|
|
{
|
|
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
|
if (StructProp && (StructProp->Struct == FWingJsonObject::StaticStruct()))
|
|
{
|
|
FWingJsonObject Val;
|
|
Val.Json = JsonValue.AsObject();
|
|
Prop->SetValue_InContainer(Container, &Val);
|
|
return true;
|
|
}
|
|
PrintExpectsReceived(TEXT("json object"), Errors);
|
|
return false;
|
|
}
|
|
|
|
if (JsonValue.Type == EJson::Array)
|
|
{
|
|
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
|
if (StructProp && (StructProp->Struct == FWingJsonArray::StaticStruct()))
|
|
{
|
|
FWingJsonArray Val;
|
|
Val.Array = JsonValue.AsArray();
|
|
Prop->SetValue_InContainer(Container, &Val);
|
|
return true;
|
|
}
|
|
PrintExpectsReceived(TEXT("json array"), Errors);
|
|
return false;
|
|
}
|
|
|
|
PrintExpectsReceived(TEXT("Unrecognized Json Data"), Errors);
|
|
return false;
|
|
}
|
|
|
|
TOptional<UObject*> FWingProperty::GetObject(WingOut Errors) const
|
|
{
|
|
FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop);
|
|
if (!OProp)
|
|
{
|
|
PrintExpectsReceived(TEXT("object"), Errors);
|
|
return {};
|
|
}
|
|
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
|
|
return OProp->GetObjectPropertyValue(VP);
|
|
}
|
|
|
|
TOptional<double> FWingProperty::GetDouble(WingOut Errors) const
|
|
{
|
|
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
|
|
if (!NProp)
|
|
{
|
|
PrintExpectsReceived(TEXT("double"), Errors);
|
|
return {};
|
|
}
|
|
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
|
|
if (NProp->IsFloatingPoint())
|
|
{
|
|
return NProp->GetFloatingPointPropertyValue(VP);
|
|
}
|
|
else
|
|
{
|
|
int64 I = NProp->GetSignedIntPropertyValue(VP);
|
|
double D = (double)I;
|
|
if ((int64)D != I)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' value %lld cannot be represented losslessly as double\n"),
|
|
*WingUtils::FormatName(Prop), I);
|
|
return {};
|
|
}
|
|
return D;
|
|
}
|
|
}
|
|
|
|
TOptional<int64> FWingProperty::GetInt64(WingOut Errors) const
|
|
{
|
|
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
|
|
if (!NProp)
|
|
{
|
|
PrintExpectsReceived(TEXT("int"), Errors);
|
|
return {};
|
|
}
|
|
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
|
|
if (NProp->IsFloatingPoint())
|
|
{
|
|
double D = NProp->GetFloatingPointPropertyValue(VP);
|
|
if (FMath::Floor(D) != D)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' value %lf is not an integer\n"),
|
|
*WingUtils::FormatName(Prop), D);
|
|
return {};
|
|
}
|
|
if (FMath::Abs(D) > (double)((1LL)<<53))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Property '%s' value %lf is too large to convert to int64 losslessly\n"),
|
|
*WingUtils::FormatName(Prop), D);
|
|
return {};
|
|
}
|
|
return (int64)D;
|
|
}
|
|
else
|
|
{
|
|
return NProp->GetSignedIntPropertyValue(VP);
|
|
}
|
|
}
|
|
|
|
TOptional<bool> FWingProperty::GetBool(WingOut Errors) const
|
|
{
|
|
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
|
|
{
|
|
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
|
|
return BoolProp->GetPropertyValue(VP);
|
|
}
|
|
PrintExpectsReceived(TEXT("boolean"), Errors);
|
|
return {};
|
|
}
|
|
|
|
FString FWingProperty::GetText() const
|
|
{
|
|
if (IsPinTypeProperty(Prop))
|
|
{
|
|
FEdGraphPinType *PinType = Prop->ContainerPtrToValuePtr<FEdGraphPinType>(Container);
|
|
return UWingTypes::TypeToText(*PinType);
|
|
}
|
|
if (FClassProperty *CProp = CastField<FClassProperty>(Prop))
|
|
{
|
|
UObject *Obj = CProp->GetObjectPropertyValue_InContainer(Container);
|
|
if (Obj) return UWingTypes::TypeToText(Obj);
|
|
return TEXT("None");
|
|
}
|
|
FString Result;
|
|
Prop->ExportTextItem_InContainer(Result, Container, nullptr, nullptr, PPF_None);
|
|
return Result;
|
|
}
|
|
|
|
FString FWingProperty::GetTruncatedText(int32 MaxLen) const
|
|
{
|
|
FString Result = GetText();
|
|
for (int i = 0; i < Result.Len(); i++)
|
|
if (Result[i] == '\n') Result[i] = ' ';
|
|
if (Result.Len() > MaxLen)
|
|
Result = Result.Left(MaxLen) + TEXT("...");
|
|
return Result;
|
|
}
|
|
|
|
|
|
void FWingProperty::Print(WingOut Out) const
|
|
{
|
|
bool bEditable = !Prop->HasAnyPropertyFlags(CPF_EditConst);
|
|
Out.Printf(TEXT(" %s %s %s = %s\n"),
|
|
bEditable ? TEXT("editable") : TEXT("readonly"),
|
|
*UWingTypes::TypeToText(Prop),
|
|
*WingUtils::FormatName(Prop),
|
|
*GetTruncatedText(100));
|
|
}
|
|
|
|
FString FWingProperty::GetCategory() const
|
|
{
|
|
FString Result = Prop->GetMetaData(TEXT("Category"));
|
|
if (Result.IsEmpty()) Result = "Unclassified";
|
|
return Result;
|
|
}
|
|
|
|
TArray<FWingProperty> FWingProperty::GetAll(FWingStructAndUStruct Obj)
|
|
{
|
|
TArray<FWingProperty> Result;
|
|
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
|
|
{
|
|
bool Editable = !It->HasAnyPropertyFlags(CPF_EditConst);
|
|
Result.Add(FWingProperty(*It, Obj.StructPtr, Editable));
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TArray<FWingProperty> FWingProperty::GetVisible(FWingStructAndUStruct Obj)
|
|
{
|
|
TArray<FWingProperty> Result;
|
|
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
|
|
{
|
|
if (!It->HasAllPropertyFlags(CPF_Edit)) continue;
|
|
bool Editable = !It->HasAnyPropertyFlags(CPF_EditConst);
|
|
Result.Add(FWingProperty(*It, Obj.StructPtr, Editable));
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TArray<FName> FWingProperty::GetVisibleNames(UStruct *US)
|
|
{
|
|
TArray<FName> Result;
|
|
for (TFieldIterator<FProperty> It(US); It; ++It)
|
|
{
|
|
if (!It->HasAllPropertyFlags(CPF_Edit)) continue;
|
|
Result.Add(It->GetFName());
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void FWingProperty::Remove(TArray<FWingProperty>& Props, FName Name)
|
|
{
|
|
Props.RemoveAll([&](const FWingProperty& P) { return P.Prop->GetName() == Name; });
|
|
}
|
|
|
|
void FWingProperty::Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, FName Name)
|
|
{
|
|
int Dst = 0;
|
|
for (int i = 0; i < In.Num(); i++)
|
|
{
|
|
if (In[i]->GetFName() == Name) Out.Add(In[i]);
|
|
else In[Dst++] = In[i];
|
|
}
|
|
In.SetNum(Dst);
|
|
}
|
|
|
|
TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, bool Mutable)
|
|
{
|
|
if (!Obj) return {};
|
|
|
|
// If it's a UWingStructPointer, return the properties
|
|
// of the struct instead. Propagate editability of the host.
|
|
if (UWingStructPointer *SP = Cast<UWingStructPointer>(Obj))
|
|
{
|
|
TArray<FWingProperty> Result =
|
|
GetVisible(FWingStructAndUStruct(SP->StructBase, SP->StructType));
|
|
if (!Mutable || (!SP->Editable)) StripEditable(Result);
|
|
return Result;
|
|
}
|
|
|
|
// Blueprints don't have editable properties. So
|
|
// instead, we fetch properties from the generated CDO.
|
|
if (UBlueprint *BP = ::Cast<UBlueprint>(Obj))
|
|
{
|
|
if (BP->GeneratedClass == nullptr)
|
|
{
|
|
WingOut::Stdout.Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
|
|
return {};
|
|
}
|
|
Obj = BP->GeneratedClass->GetDefaultObject();
|
|
}
|
|
|
|
// Component references: get the proper template.
|
|
if (UWingComponentReference* Ref = ::Cast<UWingComponentReference>(Obj))
|
|
{
|
|
Obj = Mutable ? UWingComponent::GetMutableTemplate(Ref) : UWingComponent::GetImmutableTemplate(Ref);
|
|
if (!Obj)
|
|
{
|
|
WingOut::Stdout.Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString());
|
|
return {};
|
|
}
|
|
}
|
|
|
|
TArray<FWingProperty> Result = GetVisible(Obj);
|
|
|
|
// If it's a Material Graph node, also collect properties from
|
|
// the associated material expression.
|
|
//
|
|
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Obj))
|
|
{
|
|
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
|
|
{
|
|
Result.Append(GetVisible(Expr));
|
|
}
|
|
}
|
|
|
|
// If it's a Widget, hide the slot property, and add the
|
|
// slot properties.
|
|
if (UWidget* Widget = Cast<UWidget>(Obj))
|
|
{
|
|
FWingProperty::Remove(Result, TEXT("Slot"));
|
|
if (UPanelSlot* Slot = Widget->Slot)
|
|
{
|
|
Result.Append(GetVisible(Slot));
|
|
}
|
|
}
|
|
|
|
if (!Mutable) StripEditable(Result);
|
|
return Result;
|
|
}
|
|
|
|
|
|
bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors)
|
|
{
|
|
bool Ok = true;
|
|
|
|
// Build a set of known property names for the unknown-field check.
|
|
TSet<FName> KnownKeys;
|
|
for (const FWingProperty& P : Props) KnownKeys.Add(P->GetFName());
|
|
|
|
// Check for unknown fields in the JSON
|
|
for (const auto& KV : Json.Values)
|
|
{
|
|
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
|
|
if (!KnownKeys.Contains(Name))
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
|
|
Ok = false;
|
|
}
|
|
}
|
|
|
|
// Populate each property from JSON
|
|
for (FWingProperty& P : Props)
|
|
{
|
|
FString JsonKey = WingUtils::FormatName(P.Prop);
|
|
TSharedPtr<FJsonValue> Value = Json.TryGetField(JsonKey);
|
|
if (!Value)
|
|
{
|
|
bool Optional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
|
|
if (!Optional)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
|
|
Ok = false;
|
|
}
|
|
continue;
|
|
}
|
|
if (!P.SetJson(*Value, Errors)) Ok = false;
|
|
}
|
|
return Ok;
|
|
}
|
|
|
|
bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json, bool AllOptional, WingOut Errors)
|
|
{
|
|
// Make sure they passed in a JSON object.
|
|
TSharedPtr<FJsonObject> Obj = Json.AsObject();
|
|
if (Obj == nullptr)
|
|
{
|
|
Errors.Printf(TEXT("property data should be stored in a json object\n"));
|
|
return false;
|
|
}
|
|
return PopulateFromJson(Props, *Obj, AllOptional, Errors);
|
|
}
|
|
|
|
|
|
bool FWingProperty::IsPinTypeProperty(FProperty* Prop)
|
|
{
|
|
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
|
return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct();
|
|
}
|
|
|
|
|
|
|
|
void FWingProperty::PrintExpectsReceived(const TCHAR *Type, WingOut Errors) const
|
|
{
|
|
Errors.Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"),
|
|
*WingUtils::FormatName(Prop), Type, *Prop->GetCPPType());
|
|
}
|
|
|
|
|
|
bool FWingProperty::CheckImportTextResult(const FString &Value, WingOut Errors) const
|
|
{
|
|
if (FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop))
|
|
{
|
|
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
|
|
UObject *Obj = OProp->GetObjectPropertyValue(VP);
|
|
if (Obj == nullptr && Value.TrimStartAndEnd().Compare(TEXT("None"), ESearchCase::IgnoreCase) != 0)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Failed to parse '%s' for property '%s'\n"),
|
|
*Value, *WingUtils::FormatName(Prop));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void FWingProperty::StripEditable(TArray<FWingProperty> &Props)
|
|
{
|
|
for (FWingProperty &Elt : Props) Elt.Editable = false;
|
|
}
|
|
|
|
bool FWingProperty::CheckEditable(WingOut Errors) const
|
|
{
|
|
if (!Editable)
|
|
{
|
|
Errors.Printf(TEXT("ERROR: Cannot edit property %s, not marked editable"),
|
|
*WingUtils::FormatName(Prop));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::IsUnsigned(FNumericProperty* Prop)
|
|
{
|
|
return
|
|
CastField<FByteProperty>(Prop) ||
|
|
CastField<FUInt16Property>(Prop) ||
|
|
CastField<FUInt32Property>(Prop) ||
|
|
CastField<FUInt64Property>(Prop);
|
|
}
|
|
|
|
bool FWingProperty::LosslessDoubleToInt64(double D, int64 &I, WingOut Errors)
|
|
{
|
|
if (FMath::Floor(D) != D)
|
|
{
|
|
Errors.Printf(TEXT(
|
|
"ERROR: Converting double %.4lf to integer would lose precision\n"), D);
|
|
return false;
|
|
}
|
|
if (FMath::Abs(D) > (double)((1LL)<<53))
|
|
{
|
|
Errors.Printf(TEXT(
|
|
"ERROR: Converting huge double %lf to integer would lose data.\n"), D);
|
|
return false;
|
|
}
|
|
I = (int64)D;
|
|
return true;
|
|
}
|
|
|
|
bool FWingProperty::LosslessInt64ToDouble(int64 I, double &D, WingOut Errors)
|
|
{
|
|
D = (double)I;
|
|
if ((int64)D != I)
|
|
{
|
|
Errors.Printf(TEXT(
|
|
"ERROR: Converting huge integer %lld to floating point would lose data.\n"), I);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|