From 8f8d1b6f7e994e7a861671ea7b5713fb80d0042e Mon Sep 17 00:00:00 2001 From: LongerWarrior Date: Thu, 4 Jul 2024 11:53:24 +0300 Subject: [PATCH] UPropertis mappings generation --- UAssetAPI/FieldTypes/UField.cs | 45 +++++++++ UAssetAPI/UnrealTypes/FPackageIndex.cs | 12 ++- UAssetAPI/Unversioned/Usmap.cs | 125 +++++++++++++++++++++++-- 3 files changed, 172 insertions(+), 10 deletions(-) diff --git a/UAssetAPI/FieldTypes/UField.cs b/UAssetAPI/FieldTypes/UField.cs index 192c62e7..6863197f 100644 --- a/UAssetAPI/FieldTypes/UField.cs +++ b/UAssetAPI/FieldTypes/UField.cs @@ -1,6 +1,7 @@ using UAssetAPI.UnrealTypes; using UAssetAPI.ExportTypes; using UAssetAPI.CustomVersions; +using UAssetAPI.Unversioned; namespace UAssetAPI.FieldTypes; @@ -89,6 +90,50 @@ public override void Write(AssetBinaryWriter writer) } public UProperty() { } + + public EPropertyType GetUsmapPropertyType() + { + return this switch + { + UEnumProperty => EPropertyType.EnumProperty, + UByteProperty => EPropertyType.ByteProperty, + UBoolProperty => EPropertyType.BoolProperty, + UInt8Property => EPropertyType.Int8Property, + UInt16Property => EPropertyType.Int16Property, + UIntProperty => EPropertyType.IntProperty, + UInt64Property => EPropertyType.Int64Property, + UUInt16Property => EPropertyType.UInt16Property, + UUInt32Property => EPropertyType.UInt32Property, + UUInt64Property => EPropertyType.UInt64Property, + UFloatProperty => EPropertyType.FloatProperty, + UDoubleProperty => EPropertyType.DoubleProperty, + + UAssetClassProperty => EPropertyType.SoftObjectProperty, + USoftClassProperty => EPropertyType.SoftObjectProperty, + UClassProperty => EPropertyType.ObjectProperty, + UAssetObjectProperty => EPropertyType.AssetObjectProperty, + UWeakObjectProperty => EPropertyType.WeakObjectProperty, + ULazyObjectProperty => EPropertyType.LazyObjectProperty, + USoftObjectProperty => EPropertyType.SoftObjectProperty, + UObjectProperty => EPropertyType.ObjectProperty, + + UNameProperty => EPropertyType.NameProperty, + UStrProperty => EPropertyType.StrProperty, + UTextProperty => EPropertyType.TextProperty, + + UInterfaceProperty => EPropertyType.InterfaceProperty, + + UMulticastDelegateProperty => EPropertyType.MulticastDelegateProperty, + UDelegateProperty => EPropertyType.DelegateProperty, + + UMapProperty => EPropertyType.MapProperty, + USetProperty => EPropertyType.SetProperty, + UArrayProperty => EPropertyType.ArrayProperty, + UStructProperty => EPropertyType.StructProperty, + + _ => EPropertyType.Unknown, + }; + } } public class UEnumProperty : UProperty diff --git a/UAssetAPI/UnrealTypes/FPackageIndex.cs b/UAssetAPI/UnrealTypes/FPackageIndex.cs index d7ef744e..4f6875e6 100644 --- a/UAssetAPI/UnrealTypes/FPackageIndex.cs +++ b/UAssetAPI/UnrealTypes/FPackageIndex.cs @@ -122,10 +122,14 @@ public Import ToImport(UnrealPackage asset) /// Thrown when this is not an index into the export map. public Export ToExport(UnrealPackage asset) { - if (!IsExport()) throw new InvalidOperationException("Index = " + Index + "; cannot call ToExport()"); - int newIndex = Index - 1; - if (newIndex < 0 || newIndex >= asset.Exports.Count) return null; - return asset.Exports[newIndex]; + if (!IsExport() || Index > asset.Exports.Count) throw new InvalidOperationException("Index = " + Index + "; cannot call ToExport()"); + return asset.Exports[Index - 1]; + } + + public T ToExport(UnrealPackage asset) where T : Export + { + if (!IsExport() || Index > asset.Exports.Count) throw new InvalidOperationException("Index = " + Index + "; cannot call ToExport()"); + return (T)asset.Exports[Index-1]; } public override bool Equals(object obj) diff --git a/UAssetAPI/Unversioned/Usmap.cs b/UAssetAPI/Unversioned/Usmap.cs index 8c8a7edb..06227d65 100644 --- a/UAssetAPI/Unversioned/Usmap.cs +++ b/UAssetAPI/Unversioned/Usmap.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using UAssetAPI.CustomVersions; using UAssetAPI.ExportTypes; using UAssetAPI.FieldTypes; using UAssetAPI.PropertyTypes.Objects; @@ -424,7 +425,7 @@ private static UsmapPropertyData ConvertFPropertyToUsmapPropertyData(StructExpor var underlyingProp = (entry as FEnumProperty).UnderlyingProp; if (enumIndex.IsExport()) { - var exp2 = enumIndex.ToExport(exp.Asset) as EnumExport; + var exp2 = enumIndex.ToExport(exp.Asset); var allNames = new List(); foreach (var cosa in exp2.Enum.Names) allNames.Add(cosa.Item1.ToString()); converted1 = new UsmapEnumData(exp2.ObjectName.ToString(), allNames) { InnerType = ConvertFPropertyToUsmapPropertyData(exp, underlyingProp) }; @@ -458,7 +459,7 @@ private static UsmapPropertyData ConvertFPropertyToUsmapPropertyData(StructExpor FPackageIndex enumIndex = (entry as FByteProperty).Enum; if (enumIndex.IsExport()) { - var exp2 = enumIndex.ToExport(exp.Asset) as EnumExport; + var exp2 = enumIndex.ToExport(exp.Asset); var allNames = new List(); foreach (var cosa in exp2.Enum.Names) allNames.Add(cosa.Item1.ToString()); converted1 = new UsmapEnumData(exp2.ObjectName.ToString(), allNames) { InnerType = new UsmapPropertyData(EPropertyType.ByteProperty) }; @@ -511,6 +512,103 @@ private static UsmapPropertyData ConvertFPropertyToUsmapPropertyData(StructExpor return converted1; } + private static UsmapPropertyData ConvertUPropertyToUsmapPropertyData(PropertyExport exp) + { + var asset = exp.Asset; + var typ = exp.Property.GetUsmapPropertyType(); + UsmapPropertyData converted; + switch (exp.Property) + { + case UEnumProperty enumprop: + var enumIndex = enumprop.Enum; + var underlyingProp = enumprop.UnderlyingProp; + if (enumIndex.IsExport()) + { + var exp2 = enumIndex.ToExport(exp.Asset); + var allNames = new List(); + foreach (var cosa in exp2.Enum.Names) allNames.Add(cosa.Item1.ToString()); + converted = new UsmapEnumData(exp2.ObjectName.ToString(), allNames) { InnerType = ConvertUPropertyToUsmapPropertyData(underlyingProp.ToExport(asset)) }; + } + else if (enumIndex.IsImport()) + { + string enumName = enumIndex.ToImport(exp.Asset).ObjectName?.Value.Value; + if (enumName == null || !exp.Asset.Mappings.EnumMap.TryGetValue(enumName, out UsmapEnum value)) + { + if (!exp.Asset.HasUnversionedProperties) + { + return new UsmapEnumData(enumName, []) { InnerType = new UsmapPropertyData(EPropertyType.ByteProperty) }; + } + else + { + throw new InvalidOperationException("Attempt to index into non-existent enum " + enumName); + } + } + var allNames = new List(); + foreach (var cosa in value.Values) allNames.Add(cosa.ToString()); + converted = new UsmapEnumData(enumName, allNames) { InnerType = ConvertUPropertyToUsmapPropertyData(underlyingProp.ToExport(asset)) }; + } + else + { + converted = null; + } + break; + case UByteProperty byt: + enumIndex = byt.Enum; + if (enumIndex.IsExport()) + { + var exp2 = enumIndex.ToExport(exp.Asset); + var allNames = new List(); + foreach (var cosa in exp2.Enum.Names) allNames.Add(cosa.Item1.ToString()); + converted = new UsmapEnumData(exp2.ObjectName.ToString(), allNames) { InnerType = new UsmapPropertyData(EPropertyType.ByteProperty) }; + } + else if (enumIndex.IsImport()) + { + string enumName = enumIndex.ToImport(exp.Asset).ObjectName?.Value.Value; + if (enumName == null || !exp.Asset.Mappings.EnumMap.TryGetValue(enumName, out UsmapEnum value)) + { + if (!exp.Asset.HasUnversionedProperties) + { + return new UsmapEnumData(enumName, []) { InnerType = new UsmapPropertyData(EPropertyType.ByteProperty) }; + } + else + { + //should not happen cause it was before 425 + throw new InvalidOperationException("Attempt to index into non-existent enum " + enumName); + } + } + var allNames = new List(); + foreach (var cosa in value.Values) allNames.Add(cosa.ToString()); + converted = new UsmapEnumData(enumName, allNames) { InnerType = new UsmapPropertyData(EPropertyType.ByteProperty) }; + } + else + { + converted = new UsmapPropertyData(EPropertyType.ByteProperty); // this is most likely an InnerType of an EnumProperty + } + break; + case UStructProperty strukt: + var strucstr = Export.GetClassTypeForAncestry(strukt.Struct, asset, out _); + converted = new UsmapStructData(strucstr.ToString()); + break; + case UArrayProperty array: + converted = new UsmapArrayData(EPropertyType.ArrayProperty) { InnerType = ConvertUPropertyToUsmapPropertyData(array.Inner.ToExport(asset)) }; + break; + case USetProperty set: + converted = new UsmapArrayData(EPropertyType.SetProperty) { InnerType = ConvertUPropertyToUsmapPropertyData(set.ElementProp.ToExport(asset)) }; + break; + case UMapProperty map: + converted = new UsmapMapData() + { + InnerType = ConvertUPropertyToUsmapPropertyData(map.KeyProp.ToExport(asset)), + ValueType = ConvertUPropertyToUsmapPropertyData(map.ValueProp.ToExport(asset)) + }; + break; + default: + converted = new UsmapPropertyData(typ); + break; + } + return converted; + } + public static UsmapSchema GetSchemaFromStructExport(string exportName, UnrealPackage asset) { if (asset == null) throw new InvalidOperationException("Cannot evaluate struct export without package reference"); @@ -525,12 +623,27 @@ public static UsmapSchema GetSchemaFromStructExport(StructExport exp, bool isCas { var res = new Dictionary(); int idx = 0; - foreach (FProperty entry in exp.LoadedProperties) + if (exp.Asset.GetCustomVersion() >= FCoreObjectVersion.FProperties) { - UsmapProperty converted = new UsmapProperty(entry.Name.ToString(), (ushort)idx, 0, 1, ConvertFPropertyToUsmapPropertyData(exp, entry)); - res.Add(idx, converted); - idx++; + foreach (FProperty entry in exp.LoadedProperties) + { + UsmapProperty converted = new UsmapProperty(entry.Name.ToString(), (ushort)idx, 0, 1, ConvertFPropertyToUsmapPropertyData(exp, entry)); + res.Add(idx, converted); + idx++; + } } + else + { + foreach (var entry in exp.Children) + { + if (entry.ToExport(exp.Asset) is not PropertyExport field) continue; + + UsmapProperty converted = new UsmapProperty(field.ObjectName.ToString(), (ushort)idx, 0, 1, ConvertUPropertyToUsmapPropertyData(field)); + res.Add(idx, converted); + idx++; + } + } + return new UsmapSchema(exp.ObjectName.ToString(), exp.SuperStruct.IsImport() ? exp.SuperStruct.ToImport(exp.Asset).ObjectName.ToString() : null, (ushort)res.Count, res, isCaseInsensitive, true); }