From 7bb64083e5e5b40adb2e1f83b262b8d4f98d1a37 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Sun, 19 Jul 2026 14:19:59 +0800 Subject: [PATCH 01/10] Abstracted FFieldObjectUnion, add methods to create non-parented IFProperty for IFArrayProperty and IFMapProperty --- .../Unreal/Factories/BaseUnrealFactory.cs | 1 + .../Types/Unreal/Factories/IUnrealFactory.cs | 1 + .../Unreal/Factories/Interfaces/IFField.cs | 5 +- .../Factories/Interfaces/IFFieldVariant.cs | 8 + .../Factories/UE4_27_2/UnrealFactory.cs | 31 ++- .../Unreal/Factories/UE5_0_3/UnrealFactory.cs | 5 + .../Unreal/Factories/UE5_1_1/UnrealFactory.cs | 5 + .../Unreal/Factories/UE5_2_1/UnrealFactory.cs | 1 + .../Unreal/Factories/UE5_3_2/UnrealFactory.cs | 5 + .../Unreal/Factories/UE5_4_4/UnrealFactory.cs | 19 +- .../Unreal/Factories/UE5_5_4/UnrealFactory.cs | 5 + .../Unreal/Factories/UE5_6_1/UnrealFactory.cs | 1 + .../Unreal/Factories/UE5_7_4/UnrealFactory.cs | 1 + .../Types/Unreal/UE4_27_2/Unreal.cs | 1 + UE.Toolkit.Core/UE.Toolkit.Core.csproj | 2 +- UE.Toolkit.DumperMod/ModConfig.json | 2 +- UE.Toolkit.Interfaces/IUnrealClasses.cs | 186 +++++++++++++++++- .../UE.Toolkit.Interfaces.csproj | 2 +- UE.Toolkit.Reloaded/ModConfig.json | 2 +- .../Reflection/PropertyFactory.cs | 108 +++++++++- .../Reflection/UE4_27_2/PropertyFactory.cs | 163 ++++++++++++++- .../Reflection/UE5_2_1/PropertyFactory.cs | 156 ++++++++++++++- .../Reflection/UE5_4_4/PropertyFactory.cs | 155 ++++++++++++++- .../Reflection/UE5_6_1/PropertyFactory.cs | 155 ++++++++++++++- .../Reflection/UE5_7_4/PropertyFactory.cs | 155 ++++++++++++++- UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs | 140 ++++++++++++- 26 files changed, 1271 insertions(+), 44 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFFieldVariant.cs diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs index 61192ee..bf96c38 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs @@ -100,6 +100,7 @@ public nint GetAlignment(IFProperty prop) public abstract IFFieldClass CreateFFieldClass(nint ptr); public abstract IFField CreateFField(nint ptr); + public abstract IFFieldVariant CreateFFieldVariant(nint ptr); public abstract IFStructParams CreateFStructParams(nint ptr); public abstract IFPropertyParams CreateFPropertyParams(nint ptr); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs index e9a5d67..ba3ae96 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs @@ -42,6 +42,7 @@ public interface IUnrealFactory IFFieldClass CreateFFieldClass(nint ptr); IFField CreateFField(nint ptr); + IFFieldVariant CreateFFieldVariant(nint ptr); IFStructParams CreateFStructParams(nint ptr); IFPropertyParams CreateFPropertyParams(nint ptr); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFField.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFField.cs index 42bd13b..99f757c 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFField.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFField.cs @@ -6,8 +6,11 @@ public interface IFField : IPtr { nint VTable { get; } IFFieldClass ClassPrivate { get; } - FFieldObjectUnion Owner { get; } + IFFieldVariant Owner { get; } IFField? Next { get; } string NamePrivate { get; } EObjectFlags FlagsPrivate { get; } + + void SetOwnerUObject(IUObject owner); + void SetOwnerFField(IFField owner); } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFFieldVariant.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFFieldVariant.cs new file mode 100644 index 0000000..d039c87 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IFFieldVariant.cs @@ -0,0 +1,8 @@ +namespace UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +public interface IFFieldVariant : IPtr +{ + IFField? Field { get; } + IUObject? Object { get; } + bool IsObject { get; } +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs index a1463ae..7fb39a2 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs @@ -8,7 +8,7 @@ using EPropertyFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EPropertyFlags; using EPropertyGenFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EPropertyGenFlags; using EClassFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EClassFlags; -using FFieldObjectUnion = UE.Toolkit.Core.Types.Unreal.UE5_4_4.FFieldObjectUnion; +// using FFieldObjectUnion = UE.Toolkit.Core.Types.Unreal.UE5_4_4.FFieldObjectUnion; using FUObjectArray_Pack4 = UE.Toolkit.Core.Types.Unreal.UE5_4_4.FUObjectArray_Pack4; using FName = UE.Toolkit.Core.Types.Unreal.UE5_4_4.FName; using EStructFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EStructFlags; @@ -22,6 +22,7 @@ using FEnumProperty = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FEnumProperty; using FField = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FField; using FFieldClass = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FFieldClass; +using FFieldObjectUnion = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FFieldObjectUnion; using FGenericPropertyParams = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FGenericPropertyParams; using FMapProperty = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FMapProperty; using FObjectProperty = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FObjectProperty; @@ -130,6 +131,8 @@ public override nint SizeOf() public override IFField CreateFField(nint ptr) => new FFieldUE4_27_2(ptr, this); + public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE4_27_2(ptr, this); + public override IFStructParams CreateFStructParams(nint ptr) => new FStructParamsUE4_27_2(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParamsUE4_27_2(ptr, this); @@ -232,10 +235,34 @@ public unsafe class FFieldUE4_27_2(nint ptr, IUnrealFactory factory) public nint Ptr => ptr; public nint VTable => _self->_vtable; public IFFieldClass ClassPrivate => _factory.CreateFFieldClass((nint)_self->class_private); - public FFieldObjectUnion Owner => throw new NotSupportedException(); + public IFFieldVariant Owner => factory.CreateFFieldVariant((nint)(&_self->owner)); public IFField? Next => _self->next != null ? _factory.CreateFField((nint)_self->next) : null; public string NamePrivate => _self->name_private.ToString(); public EObjectFlags FlagsPrivate => _self->flags_private; + + public void SetOwnerUObject(IUObject owner) + { + _self->owner.Object = (UObjectBase*)owner.Ptr; + _self->owner.bIsUObject = true; + } + + public void SetOwnerFField(IFField owner) + { + _self->owner.Field = (FField*)owner.Ptr; + _self->owner.bIsUObject = false; + } +} + +public unsafe class FFieldVariantUE4_27_2(nint ptr, IUnrealFactory factory) + : IFFieldVariant +{ + private readonly FFieldObjectUnion* _self = (FFieldObjectUnion*)ptr; + protected readonly IUnrealFactory _factory = factory; + + public nint Ptr => ptr; + public IFField? Field => _self->Field != null ? _factory.CreateFField((nint)_self->Field) : null; + public IUObject? Object => _self->Object != null ? _factory.CreateUObject((nint)_self->Object) : null; + public bool IsObject => _self->bIsUObject; } public unsafe class FPropertyUE4_27_2(nint ptr, IUnrealFactory factory) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs index 9e492fc..e18ad5b 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs @@ -134,6 +134,11 @@ public override IFField CreateFField(IntPtr ptr) throw new NotImplementedException(); } + public override IFFieldVariant CreateFFieldVariant(IntPtr ptr) + { + throw new NotImplementedException(); + } + public override IFStructParams CreateFStructParams(IntPtr ptr) { throw new NotImplementedException(); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_1_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_1_1/UnrealFactory.cs index 38dc2bd..2cea2dd 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_1_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_1_1/UnrealFactory.cs @@ -134,6 +134,11 @@ public override IFField CreateFField(IntPtr ptr) throw new NotImplementedException(); } + public override IFFieldVariant CreateFFieldVariant(IntPtr ptr) + { + throw new NotImplementedException(); + } + public override IFStructParams CreateFStructParams(IntPtr ptr) { throw new NotImplementedException(); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs index 0e6a0ce..5ec6891 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs @@ -83,6 +83,7 @@ public override nint SizeOf() public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); + public override IFFieldVariant CreateFFieldVariant(nint ptr) => new UE4_27_2.FFieldVariantUE4_27_2(ptr, this); public override IFStructParams CreateFStructParams(nint ptr) => new FStructParams_UE5_2_1(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_2_1(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_2_1(ptr, this); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_3_2/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_3_2/UnrealFactory.cs index 315ef45..681b6a5 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_3_2/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_3_2/UnrealFactory.cs @@ -134,6 +134,11 @@ public override IFField CreateFField(IntPtr ptr) throw new NotImplementedException(); } + public override IFFieldVariant CreateFFieldVariant(IntPtr ptr) + { + throw new NotImplementedException(); + } + public override IFStructParams CreateFStructParams(IntPtr ptr) { throw new NotImplementedException(); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs index c517688..8a3331b 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs @@ -68,6 +68,7 @@ public override nint SizeOf() public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); + public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); public override IFStructParams CreateFStructParams(nint ptr) => new FStructParams_UE5_4_4(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); @@ -169,10 +170,26 @@ public unsafe class FField_UE5_4_4(nint ptr, IUnrealFactory factory) public nint Ptr => ptr; public nint VTable => _self->VTable; public IFFieldClass ClassPrivate => _factory.CreateFFieldClass((nint)_self->ClassPrivate); - public FFieldObjectUnion Owner => _self->Owner; + public IFFieldVariant Owner => factory.CreateFFieldVariant((nint)(&_self->Owner)); public IFField? Next => _self->Next != null ? _factory.CreateFField((nint)_self->Next) : null; public string NamePrivate => _self->NamePrivate.ToString(); public EObjectFlags FlagsPrivate => _self->FlagsPrivate; + + public void SetOwnerUObject(IUObject owner) => _self->Owner.Object = (UObjectBase*)(owner.Ptr + 1); + + public void SetOwnerFField(IFField owner) => _self->Owner.Field = (FField*)owner.Ptr; +} + +public unsafe class FFieldVariantUE5_4_4(nint ptr, IUnrealFactory factory) + : IFFieldVariant +{ + private readonly FFieldObjectUnion* _self = (FFieldObjectUnion*)ptr; + protected readonly IUnrealFactory _factory = factory; + + public nint Ptr => ptr; + public IFField? Field => _self->Field != null ? _factory.CreateFField((nint)_self->Field) : null; + public IUObject? Object => _self->Object != null ? _factory.CreateUObject((nint)(_self->Object) + 1) : null; + public bool IsObject => ((nint)_self->Object & 1) != 0; } public unsafe class FProperty_UE5_4_4(nint ptr, IUnrealFactory factory) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_5_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_5_4/UnrealFactory.cs index d468253..74b001b 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_5_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_5_4/UnrealFactory.cs @@ -134,6 +134,11 @@ public override IFField CreateFField(IntPtr ptr) throw new NotImplementedException(); } + public override IFFieldVariant CreateFFieldVariant(IntPtr ptr) + { + throw new NotImplementedException(); + } + public override IFStructParams CreateFStructParams(IntPtr ptr) { throw new NotImplementedException(); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs index fee2b12..847a4cc 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs @@ -64,6 +64,7 @@ public override nint SizeOf() public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); + public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); public override IFStructParams CreateFStructParams(nint ptr) => new FStructParams_UE5_4_4(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs index 266f10f..058e911 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs @@ -66,6 +66,7 @@ public override nint SizeOf() public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_7_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); + public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); public override IFStructParams CreateFStructParams(nint ptr) => new FStructParams_UE5_4_4(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); diff --git a/UE.Toolkit.Core/Types/Unreal/UE4_27_2/Unreal.cs b/UE.Toolkit.Core/Types/Unreal/UE4_27_2/Unreal.cs index 51ed573..0caf212 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE4_27_2/Unreal.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE4_27_2/Unreal.cs @@ -210,6 +210,7 @@ public unsafe struct FFieldObjectUnion { [FieldOffset(0x0)] public FField* Field; [FieldOffset(0x0)] public UObjectBase* Object; + [FieldOffset(0x8)] public bool bIsUObject; } [StructLayout(LayoutKind.Sequential, Size = 0x78)] diff --git a/UE.Toolkit.Core/UE.Toolkit.Core.csproj b/UE.Toolkit.Core/UE.Toolkit.Core.csproj index 0d00bf1..5779283 100644 --- a/UE.Toolkit.Core/UE.Toolkit.Core.csproj +++ b/UE.Toolkit.Core/UE.Toolkit.Core.csproj @@ -6,7 +6,7 @@ enable true https://github.com/RyoTune/UE.Toolkit - 1.9.0 + 1.10.0 diff --git a/UE.Toolkit.DumperMod/ModConfig.json b/UE.Toolkit.DumperMod/ModConfig.json index ab9294e..129b54a 100644 --- a/UE.Toolkit.DumperMod/ModConfig.json +++ b/UE.Toolkit.DumperMod/ModConfig.json @@ -2,7 +2,7 @@ "ModId": "UE.Toolkit.DumperMod", "ModName": "UE Toolkit: Dumper", "ModAuthor": "RyoTune", - "ModVersion": "1.9.0", + "ModVersion": "1.10.0", "ModDescription": "Unreal Engine object dumper to C# types.", "ModDll": "UE.Toolkit.DumperMod.dll", "ModIcon": "Preview.png", diff --git a/UE.Toolkit.Interfaces/IUnrealClasses.cs b/UE.Toolkit.Interfaces/IUnrealClasses.cs index cfcfc49..467fead 100644 --- a/UE.Toolkit.Interfaces/IUnrealClasses.cs +++ b/UE.Toolkit.Interfaces/IUnrealClasses.cs @@ -67,7 +67,7 @@ public interface IUnrealClasses : IUnrealClassesInternal, ITypeReflection #endregion - #region Struct Field Extension Methods + #region Struct Field Extension Methods (parented to object) /// /// Listen for the creation of an object's class, then extend it's allocation size and call a custom constructor @@ -283,7 +283,7 @@ public bool AddClassProperty(string Name, int Offset, out IFCla public bool AddTextProperty(string Name, int Offset, out IFProperty? Out) where TObject : unmanaged; /// - /// Add a Array (TArray) containing elememts of the property defined in Inner to the object's class with the + /// Add a Array (TArray) containing elements of the property defined in Inner to the object's class with the /// specified name and offset. This will make the field exposable to blueprints and Object XML. /// /// Name of the new field. @@ -294,6 +294,188 @@ public bool AddClassProperty(string Name, int Offset, out IFCla public bool AddArrayProperty(string Name, int Offset, IFProperty Inner, out IFArrayProperty? Property) where TObject : unmanaged; + /* + /// + /// Add a map (TMap) with it's key and value types defined in Key and Value, and with the + /// specified name and offset. This will make the field exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// The property used for each key in the map. + /// The property used for each value in the map. + /// Return value. + /// Object type. + public bool AddMapProperty(string Name, int Offset, + IFProperty Key, IFProperty Value, out IFArrayProperty? Property) where TObject : unmanaged; + */ + + #endregion + + #region Struct Field Extension Methods (no parent) + + /// + /// Add an Int8 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddI8Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a Int16 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddI16Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a Int32 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddI32Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a Int64 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddI64Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a UInt8 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddU8Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a UInt16 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddU16Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a UInt32 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddU32Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a UInt64 property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddU64Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a float property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddF32Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a double property to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddF64Property(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a by-value struct to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + /// Field type. + public bool AddStructProperty(string Name, int Offset, out IFStructProperty? Out) + where TField : unmanaged; + + /// + /// Add a by-value struct to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Field type. + /// Offset of the new field. + /// Return value. + public bool AddStructProperty(string Name, string TypeName, int Offset, out IFStructProperty? Out); + + /// + /// Add a by-reference struct to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// This is constructed as an ObjectProperty, but with the object type set to a UScriptStruct instead of a UClass + /// to make the underlying table in a DataTable viewable by the type reflection system (big hack, but needed for + /// data table debugging tools). + /// + /// Name of the new field. + /// Field type. + /// Offset of the new field. + /// Return value. + public bool AddStructProperty_DataTableSpecial(string Name, string TypeName, int Offset, out IFObjectProperty? Out); + + /// + /// Add a FName to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddNameProperty(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a FString to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddStringProperty(string Name, int Offset, out IFProperty? Out) ; + + /// + /// Add a FText to the object's class with the specified name and offset. This will make the field + /// exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// Return value. + public bool AddTextProperty(string Name, int Offset, out IFProperty? Out) ; + + + /// + /// Add a map (TMap) with it's key and value types defined in Key and Value, and with the + /// specified name and offset. This will make the field exposable to blueprints and Object XML. + /// + /// Name of the new field. + /// Offset of the new field. + /// The property used for each key in the map. + /// The property used for each value in the map. + /// Return value. + public bool AddMapProperty(string Name, int Offset, + IFProperty Key, IFProperty Value, out IFMapProperty? Property); #endregion #region New Struct Construction diff --git a/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj b/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj index b1e6b90..9032814 100644 --- a/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj +++ b/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj @@ -6,7 +6,7 @@ enable true https://github.com/RyoTune/UE.Toolkit - 1.9.0 + 1.10.0 diff --git a/UE.Toolkit.Reloaded/ModConfig.json b/UE.Toolkit.Reloaded/ModConfig.json index c7ee7a4..4dedf14 100644 --- a/UE.Toolkit.Reloaded/ModConfig.json +++ b/UE.Toolkit.Reloaded/ModConfig.json @@ -2,7 +2,7 @@ "ModId": "UE.Toolkit.Reloaded", "ModName": "Unreal Toolkit", "ModAuthor": "RyoTune, Rirurin", - "ModVersion": "1.9.1", + "ModVersion": "1.10.0", "ModDescription": "Modding toolkit for Unreal Engine games.\r\nSupports games between UE 4.27 and UE 5.7", "ModDll": "UE.Toolkit.Reloaded.dll", "ModIcon": "Preview.png", diff --git a/UE.Toolkit.Reloaded/Reflection/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/PropertyFactory.cs index af15891..472bcde 100644 --- a/UE.Toolkit.Reloaded/Reflection/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/PropertyFactory.cs @@ -53,7 +53,7 @@ public bool CheckPropertyEquality(string Name, uint OtherValue) return Value.ComparisonIndex.Value == OtherValue; } - private bool GetProperty(string Name, out FieldClassGlobal? FieldClass) + protected bool GetProperty(string Name, out FieldClassGlobal? FieldClass) { FieldClass = null; PropertyNames ??= InitializePropertyNames(); @@ -72,7 +72,9 @@ protected bool TryGetClassAndProperty(string PropertyName, out IUClass? #region INTERNAL INTERFACE - protected abstract void LinkToPropertyList(IFProperty Property, IUClass Reflect); + protected abstract void LinkToPropertyList(IFProperty Property, IUClass? Reflect); + + #region Properties Owned by a Class protected abstract void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, FieldClassGlobal PropertyClass); @@ -126,6 +128,54 @@ protected bool CreateTextPropertyInner(out IFProperty where TProperty : unmanaged => CreatePropertyInner(out NewProperty, Name, Offset, PropertyName, Visibility, SetTextPropertyFields); + + #endregion + + // Used in cases where we're constructing a type parameter for a generic type such as an array or map, + // so we don't want the element linked to a class. Set no parent initially, then let the array/map + // property initializer set the owners for the type params. + #region Unowned Properties + + protected abstract void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass); + + private bool CreatePropertyInner(out IFProperty? NewProperty, + string Name, int Offset, string PropertyName, PropertyVisibility Visibility, + Action Callback) + where TProperty : unmanaged + { + NewProperty = null; + if (!GetProperty(PropertyName, out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + NewProperty = Factory.CreateFProperty(Alloc); + Callback(NewProperty, Offset, Visibility); + LinkToPropertyList(NewProperty, null); + return true; + } + + protected bool CreateCopyPropertyInner(out IFProperty? NewProperty, + string Name, int Offset, string PropertyName, PropertyVisibility Visibility) + where TField : unmanaged + where TProperty : unmanaged + => CreatePropertyInner(out NewProperty, Name, + Offset, PropertyName, Visibility, SetCopyPropertyFields); + + protected bool CreateStringPropertyInner(out IFProperty? NewProperty, + string Name, int Offset, string PropertyName, PropertyVisibility Visibility) + where TField : unmanaged + where TProperty : unmanaged + => CreatePropertyInner(out NewProperty, Name, + Offset, PropertyName, Visibility, SetStringPropertyFields); + + protected bool CreateTextPropertyInner(out IFProperty? NewProperty, + string Name, int Offset, string PropertyName, PropertyVisibility Visibility) + where TField : unmanaged + where TProperty : unmanaged + => CreatePropertyInner(out NewProperty, Name, + Offset, PropertyName, Visibility, SetTextPropertyFields); + + #endregion protected abstract void SetBoolPropertyFields(IFBoolProperty Property, BooleanMask Mask); @@ -197,6 +247,36 @@ public abstract bool CreateF32(out IFProperty? NewProperty, string Name, public abstract bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) where TOwner: unmanaged; + public abstract bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + + public abstract bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility); + public bool CreateCBool(out IFBoolProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) where TOwner: unmanaged => CreateBoolPropertyInner(out NewProperty, Name, Offset, Visibility, new(1, 255)); @@ -223,6 +303,16 @@ public abstract bool CreateStruct(out IFStructProperty? NewPrope where TOwner : unmanaged where TField : unmanaged; + public abstract bool CreateStruct(out IFStructProperty? NewProperty, + string Name, int Offset, PropertyVisibility Visibility) + where TField : unmanaged; + + public abstract bool CreateStruct(out IFStructProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility); + + public abstract bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility); + public abstract bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) where TOwner : unmanaged @@ -248,10 +338,24 @@ public abstract bool CreateString(out IFProperty? NewProperty, string Na public abstract bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) where TOwner: unmanaged; + + public abstract bool CreateName(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) ; + + public abstract bool CreateString(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) ; + + public abstract bool CreateText(out IFProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) ; public abstract bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) where TObject : unmanaged; + // public abstract bool CreateMap(out IFArrayProperty? NewProperty, string Name, int Offset, + // PropertyVisibility Visibility, IFProperty Key, IFProperty Value) where TObject : unmanaged; + + public abstract bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility, IFProperty Key, IFProperty Value); #endregion protected readonly IUnrealFactory Factory = factory; diff --git a/UE.Toolkit.Reloaded/Reflection/UE4_27_2/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/UE4_27_2/PropertyFactory.cs index 5218a63..24d72f7 100644 --- a/UE.Toolkit.Reloaded/Reflection/UE4_27_2/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/UE4_27_2/PropertyFactory.cs @@ -20,14 +20,18 @@ public class PropertyFactory(IUnrealFactory factory, IUnrealMemory memory, : BasePropertyFactory(factory, memory, classes, flags) { - protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass Reflect) + protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass? Reflect) { var pProperty = (FProperty*)Property.Ptr; pProperty->prop_link_next = null; pProperty->next_ref = null; pProperty->dtor_link_next = null; pProperty->post_ct_link_next = null; - + + if (Reflect == null) + { + return; + } var pClass = (UClass*)Reflect.Ptr; if (((UStruct*)pClass)->prop_link == null) { @@ -47,21 +51,31 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R pProperty->prop_link_next = pNextProp; ((FField*)pProperty)->next = (FField*)pNextProp; } - } + } } - - protected override unsafe void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, - FieldClassGlobal PropertyClass) + + protected override unsafe void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass) { var pField = (FField*)Field.Ptr; pField->_vtable = PropertyClass.Vtable; pField->class_private = (FFieldClass*)PropertyClass.Params.Ptr; - pField->owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* pField->next = null; pField->name_private = new FName(Name); pField->flags_private = EObjectFlags.RF_Public | EObjectFlags.RF_MarkAsNative | EObjectFlags.RF_Transient; } + private unsafe void SetPropertySuperFieldsUObject(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) + { + SetPropertySuperFieldsNoOwner(Field, Name, PropertyClass); + var pField = (FField*)Field.Ptr; + pField->owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* + pField->owner.bIsUObject = true; + } + + protected override void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) => SetPropertySuperFieldsUObject(Field, Name, ClassReflection, PropertyClass); + private unsafe void SetPropertyFieldDefaults(FProperty* pProperty, int Offset) { pProperty->rep_index = 0; @@ -80,7 +94,7 @@ private unsafe void SetPropertyFieldsInner(IFProperty Property, int Offset, SetPropertyFieldDefaults(pProperty, Offset); } - protected override unsafe void SetCopyPropertyFields(IFProperty Property, int Offset, + protected override void SetCopyPropertyFields(IFProperty Property, int Offset, PropertyVisibility Visibility) { var PropertyFlags = PropertyBuilderFlags.NoCtor | PropertyBuilderFlags.Copy | PropertyBuilderFlags.NoDtor; @@ -133,6 +147,36 @@ public override bool CreateF32(out IFProperty? NewProperty, string Name, public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateCopyPropertyInner(out NewProperty, Name, Offset, "DoubleProperty", Visibility); + + public override bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int8Property", Visibility); + + public override bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int16Property", Visibility); + + public override bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "IntProperty", Visibility); + + public override bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int64Property", Visibility); + + public override bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt8Property", Visibility); + + public override bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt16Property", Visibility); + + public override bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt32Property", Visibility); + + public override bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt64Property", Visibility); + + public override bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FloatProperty", Visibility); + + public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "DoubleProperty", Visibility); public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -157,6 +201,75 @@ public override bool CreateStruct(out IFStructProperty? NewPrope return true; } + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromType(out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->struct_data = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, string TypeName, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->struct_data = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("ObjectProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var FieldClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFObjectProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = Marshal.SizeOf(); + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FObjectProperty*)NewProperty.Ptr)->prop_class = (UClass*)FieldClass!.Ptr; } // Our "class" + return true; + } + public override bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) { @@ -190,6 +303,15 @@ public override bool CreateString(out IFProperty? NewProperty, string Na public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); + + public override bool CreateName(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "NameProperty", Visibility); + + public override bool CreateString(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateStringPropertyInner(out NewProperty, Name, Offset, "StrProperty", Visibility); + + public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); public override bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) @@ -212,4 +334,29 @@ public override bool CreateArray(out IFArrayProperty? NewProperty, strin unsafe { ((FArrayProperty*)Alloc)->inner = (FProperty*)Inner.Ptr; } return true; } + + public override bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, + IFProperty Key, IFProperty Value) + { + NewProperty = null; + if (!GetProperty("MapProperty", out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFMapProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = 0x50; // sizeof(TMap), usually + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.NoCtor); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FMapProperty*)Alloc)->key_prop = (FProperty*)Key.Ptr; } + unsafe { ((FMapProperty*)Alloc)->value_prop = (FProperty*)Value.Ptr; } + Key.SetOwnerFField(NewProperty); + Value.SetOwnerFField(NewProperty); + return true; + } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Reflection/UE5_2_1/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/UE5_2_1/PropertyFactory.cs index 407c684..9e14234 100644 --- a/UE.Toolkit.Reloaded/Reflection/UE5_2_1/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/UE5_2_1/PropertyFactory.cs @@ -17,13 +17,18 @@ public class PropertyFactory(IUnrealFactory factory, IUnrealMemory memory, : BasePropertyFactory(factory, memory, classes, flags) { - protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass Reflect) + protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass? Reflect) { var pProperty = (FProperty*)Property.Ptr; pProperty->prop_link_next = null; pProperty->next_ref = null; pProperty->dtor_link_next = null; pProperty->post_ct_link_next = null; + + if (Reflect == null) + { + return; + } var pClass = (UClass*)Reflect.Ptr; if (((UStruct*)pClass)->PropertyLink == null) @@ -47,17 +52,27 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R } } - protected override unsafe void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, - FieldClassGlobal PropertyClass) + protected override unsafe void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass) { var pField = (FField*)Field.Ptr; pField->_vtable = PropertyClass.Vtable; pField->class_private = (FFieldClass*)PropertyClass.Params.Ptr; - pField->owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* pField->next = null; pField->name_private = new FName(Name); pField->flags_private = EObjectFlags.RF_Public | EObjectFlags.RF_MarkAsNative | EObjectFlags.RF_Transient; } + + private unsafe void SetPropertySuperFieldsUObject(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) + { + SetPropertySuperFieldsNoOwner(Field, Name, PropertyClass); + var pField = (FField*)Field.Ptr; + pField->owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* + pField->owner.bIsUObject = true; + } + + protected override void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) => SetPropertySuperFieldsUObject(Field, Name, ClassReflection, PropertyClass); private unsafe void SetPropertyFieldDefaults(FProperty* pProperty, int Offset) { @@ -129,6 +144,36 @@ public override bool CreateF32(out IFProperty? NewProperty, string Name, public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); + + public override bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int8Property", Visibility); + + public override bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int16Property", Visibility); + + public override bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "IntProperty", Visibility); + + public override bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int64Property", Visibility); + + public override bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt8Property", Visibility); + + public override bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt16Property", Visibility); + + public override bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt32Property", Visibility); + + public override bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt64Property", Visibility); + + public override bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FFloatProperty", Visibility); + + public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -152,6 +197,75 @@ public override bool CreateStruct(out IFStructProperty? NewPrope unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } return true; } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromType(out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, string TypeName, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("ObjectProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var FieldClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFObjectProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = Marshal.SizeOf(); + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FObjectProperty*)NewProperty.Ptr)->PropertyClass = (UClass*)FieldClass!.Ptr; } // Our "class" + return true; + } public override bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -186,6 +300,15 @@ public override bool CreateString(out IFProperty? NewProperty, string Na public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); + + public override bool CreateName(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "NameProperty", Visibility); + + public override bool CreateString(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateStringPropertyInner(out NewProperty, Name, Offset, "StrProperty", Visibility); + + public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); public override bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) @@ -208,4 +331,29 @@ public override bool CreateArray(out IFArrayProperty? NewProperty, strin unsafe { ((FArrayProperty*)Alloc)->Inner = (UE.Toolkit.Core.Types.Unreal.UE5_4_4.FProperty*)Inner.Ptr; } return true; } + + public override bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, + IFProperty Key, IFProperty Value) + { + NewProperty = null; + if (!GetProperty("MapProperty", out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFMapProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->array_dim = 1; + pProperty->element_size = 0x50; // sizeof(TMap), usually + pProperty->property_flags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.NoCtor); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FMapProperty*)Alloc)->KeyProp = (UE.Toolkit.Core.Types.Unreal.UE5_4_4.FProperty*)Key.Ptr; } + unsafe { ((FMapProperty*)Alloc)->ValueProp = (UE.Toolkit.Core.Types.Unreal.UE5_4_4.FProperty*)Value.Ptr; } + Key.SetOwnerFField(NewProperty); + Value.SetOwnerFField(NewProperty); + return true; + } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Reflection/UE5_4_4/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/UE5_4_4/PropertyFactory.cs index 9b3ee32..e076e6a 100644 --- a/UE.Toolkit.Reloaded/Reflection/UE5_4_4/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/UE5_4_4/PropertyFactory.cs @@ -12,7 +12,7 @@ public class PropertyFactory(IUnrealFactory factory, IUnrealMemory memory, : BasePropertyFactory(factory, memory, classes, flags) { - protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass Reflect) + protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass? Reflect) { var pProperty = (FProperty*)Property.Ptr; pProperty->PropertyLinkNext = null; @@ -20,6 +20,11 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R pProperty->DestructorLinkNext = null; pProperty->PostConstructLinkNext = null; + if (Reflect == null) + { + return; + } + var pClass = (UClass*)Reflect.Ptr; // Is this the only field? if (((UStruct*)pClass)->PropertyLink == null) @@ -43,17 +48,26 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R } } - protected override unsafe void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, - FieldClassGlobal PropertyClass) + protected override unsafe void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass) { var pField = (FField*)Field.Ptr; pField->VTable = PropertyClass.Vtable; pField->ClassPrivate = (FFieldClass*)PropertyClass.Params.Ptr; - pField->Owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* pField->Next = null; pField->NamePrivate = new FName(Name); pField->FlagsPrivate = EObjectFlags.RF_Public | EObjectFlags.RF_MarkAsNative | EObjectFlags.RF_Transient; } + + private unsafe void SetPropertySuperFieldsUObject(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) + { + SetPropertySuperFieldsNoOwner(Field, Name, PropertyClass); + var pField = (FField*)Field.Ptr; + pField->Owner.Object = (UObjectBase*)(ClassReflection.Ptr + 1); // UClass* + } + + protected override void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) => SetPropertySuperFieldsUObject(Field, Name, ClassReflection, PropertyClass); private unsafe void SetPropertyFieldDefaults(FProperty* pProperty, int Offset) { @@ -125,6 +139,36 @@ public override bool CreateF32(out IFProperty? NewProperty, string Name, public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); + + public override bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int8Property", Visibility); + + public override bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int16Property", Visibility); + + public override bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "IntProperty", Visibility); + + public override bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int64Property", Visibility); + + public override bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt8Property", Visibility); + + public override bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt16Property", Visibility); + + public override bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt32Property", Visibility); + + public override bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt64Property", Visibility); + + public override bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FFloatProperty", Visibility); + + public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -148,6 +192,75 @@ public override bool CreateStruct(out IFStructProperty? NewPrope unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } return true; } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromType(out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, string TypeName, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("ObjectProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var FieldClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFObjectProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = Marshal.SizeOf(); + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FObjectProperty*)NewProperty.Ptr)->PropertyClass = (UClass*)FieldClass!.Ptr; } // Our "class" + return true; + } public override bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -182,6 +295,15 @@ public override bool CreateString(out IFProperty? NewProperty, string Na public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); + + public override bool CreateName(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "NameProperty", Visibility); + + public override bool CreateString(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateStringPropertyInner(out NewProperty, Name, Offset, "StrProperty", Visibility); + + public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); public override bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) @@ -204,4 +326,29 @@ public override bool CreateArray(out IFArrayProperty? NewProperty, strin unsafe { ((FArrayProperty*)Alloc)->Inner = (FProperty*)Inner.Ptr; } return true; } + + public override bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, + IFProperty Key, IFProperty Value) + { + NewProperty = null; + if (!GetProperty("MapProperty", out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFMapProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = 0x50; // sizeof(TMap), usually + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.NoCtor); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FMapProperty*)Alloc)->KeyProp = (FProperty*)Key.Ptr; } + unsafe { ((FMapProperty*)Alloc)->ValueProp = (FProperty*)Value.Ptr; } + Key.SetOwnerFField(NewProperty); + Value.SetOwnerFField(NewProperty); + return true; + } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Reflection/UE5_6_1/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/UE5_6_1/PropertyFactory.cs index 0263442..a620da4 100644 --- a/UE.Toolkit.Reloaded/Reflection/UE5_6_1/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/UE5_6_1/PropertyFactory.cs @@ -15,7 +15,7 @@ public class PropertyFactory(IUnrealFactory factory, IUnrealMemory memory, : BasePropertyFactory(factory, memory, classes, flags) { - protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass Reflect) + protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass? Reflect) { var pProperty = (FProperty*)Property.Ptr; pProperty->PropertyLinkNext = null; @@ -23,6 +23,11 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R pProperty->DestructorLinkNext = null; pProperty->PostConstructLinkNext = null; + if (Reflect == null) + { + return; + } + var pClass = (UClass*)Reflect.Ptr; // Is this the only field? if (((UStruct*)pClass)->PropertyLink == null) @@ -46,17 +51,26 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R } } - protected override unsafe void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, - FieldClassGlobal PropertyClass) + protected override unsafe void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass) { var pField = (FField*)Field.Ptr; pField->VTable = PropertyClass.Vtable; pField->ClassPrivate = (FFieldClass*)PropertyClass.Params.Ptr; - pField->Owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* pField->Next = null; pField->NamePrivate = new FName(Name); pField->FlagsPrivate = EObjectFlags.RF_Public | EObjectFlags.RF_MarkAsNative | EObjectFlags.RF_Transient; } + + private unsafe void SetPropertySuperFieldsUObject(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) + { + SetPropertySuperFieldsNoOwner(Field, Name, PropertyClass); + var pField = (FField*)Field.Ptr; + pField->Owner.Object = (UObjectBase*)(ClassReflection.Ptr + 1); // UClass* + } + + protected override void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) => SetPropertySuperFieldsUObject(Field, Name, ClassReflection, PropertyClass); private unsafe void SetPropertyFieldDefaults(FProperty* pProperty, int Offset) { @@ -128,6 +142,36 @@ public override bool CreateF32(out IFProperty? NewProperty, string Name, public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); + + public override bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int8Property", Visibility); + + public override bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int16Property", Visibility); + + public override bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "IntProperty", Visibility); + + public override bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int64Property", Visibility); + + public override bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt8Property", Visibility); + + public override bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt16Property", Visibility); + + public override bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt32Property", Visibility); + + public override bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt64Property", Visibility); + + public override bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FFloatProperty", Visibility); + + public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -151,6 +195,75 @@ public override bool CreateStruct(out IFStructProperty? NewPrope unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } return true; } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromType(out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, string TypeName, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("ObjectProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var FieldClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFObjectProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = Marshal.SizeOf(); + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FObjectProperty*)NewProperty.Ptr)->PropertyClass = (UClass*)FieldClass!.Ptr; } // Our "class" + return true; + } public override bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -185,6 +298,15 @@ public override bool CreateString(out IFProperty? NewProperty, string Na public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); + + public override bool CreateName(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "NameProperty", Visibility); + + public override bool CreateString(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateStringPropertyInner(out NewProperty, Name, Offset, "StrProperty", Visibility); + + public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); public override bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) @@ -207,4 +329,29 @@ public override bool CreateArray(out IFArrayProperty? NewProperty, strin unsafe { ((FArrayProperty*)Alloc)->Inner = (FProperty*)Inner.Ptr; } return true; } + + public override bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, + IFProperty Key, IFProperty Value) + { + NewProperty = null; + if (!GetProperty("MapProperty", out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFMapProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = 0x50; // sizeof(TMap), usually + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.NoCtor); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FMapProperty*)Alloc)->KeyProp = (FProperty*)Key.Ptr; } + unsafe { ((FMapProperty*)Alloc)->ValueProp = (FProperty*)Value.Ptr; } + Key.SetOwnerFField(NewProperty); + Value.SetOwnerFField(NewProperty); + return true; + } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Reflection/UE5_7_4/PropertyFactory.cs b/UE.Toolkit.Reloaded/Reflection/UE5_7_4/PropertyFactory.cs index e24db84..81634c1 100644 --- a/UE.Toolkit.Reloaded/Reflection/UE5_7_4/PropertyFactory.cs +++ b/UE.Toolkit.Reloaded/Reflection/UE5_7_4/PropertyFactory.cs @@ -18,7 +18,7 @@ public class PropertyFactory(IUnrealFactory factory, IUnrealMemory memory, : BasePropertyFactory(factory, memory, classes, flags) { - protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass Reflect) + protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass? Reflect) { var pProperty = (FProperty*)Property.Ptr; pProperty->PropertyLinkNext = null; @@ -26,6 +26,11 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R pProperty->DestructorLinkNext = null; pProperty->PostConstructLinkNext = null; + if (Reflect == null) + { + return; + } + var pClass = (UClass*)Reflect.Ptr; if (((UStruct*)pClass)->PropertyLink == null) { @@ -48,17 +53,26 @@ protected override unsafe void LinkToPropertyList(IFProperty Property, IUClass R } } - protected override unsafe void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, - FieldClassGlobal PropertyClass) + protected override unsafe void SetPropertySuperFieldsNoOwner(IFField Field, string Name, FieldClassGlobal PropertyClass) { var pField = (FField*)Field.Ptr; pField->VTable = PropertyClass.Vtable; pField->ClassPrivate = (FFieldClass*)PropertyClass.Params.Ptr; - pField->Owner.Object = (UObjectBase*)ClassReflection.Ptr; // UClass* pField->Next = null; pField->NamePrivate = new FName(Name); pField->FlagsPrivate = EObjectFlags.RF_Public | EObjectFlags.RF_MarkAsNative | EObjectFlags.RF_Transient; } + + private unsafe void SetPropertySuperFieldsUObject(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) + { + SetPropertySuperFieldsNoOwner(Field, Name, PropertyClass); + var pField = (FField*)Field.Ptr; + pField->Owner.Object = (UObjectBase*)(ClassReflection.Ptr + 1); // UClass* + } + + protected override void SetPropertySuperFields(IFField Field, string Name, IUClass ClassReflection, + FieldClassGlobal PropertyClass) => SetPropertySuperFieldsUObject(Field, Name, ClassReflection, PropertyClass); private unsafe void SetPropertyFieldDefaults(FProperty* pProperty, int Offset) { @@ -130,6 +144,36 @@ public override bool CreateF32(out IFProperty? NewProperty, string Name, public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); + + public override bool CreateI8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int8Property", Visibility); + + public override bool CreateI16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int16Property", Visibility); + + public override bool CreateI32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "IntProperty", Visibility); + + public override bool CreateI64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "Int64Property", Visibility); + + public override bool CreateU8(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt8Property", Visibility); + + public override bool CreateU16(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt16Property", Visibility); + + public override bool CreateU32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt32Property", Visibility); + + public override bool CreateU64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "UInt64Property", Visibility); + + public override bool CreateF32(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FFloatProperty", Visibility); + + public override bool CreateF64(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "FDoubleProperty", Visibility); public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -153,6 +197,75 @@ public override bool CreateStruct(out IFStructProperty? NewPrope unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } return true; } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromType(out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStruct(out IFStructProperty? NewProperty, string Name, string TypeName, int Offset, + PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("StructProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var ScriptStruct)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFStructProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = ScriptStruct!.PropertiesSize; // FExampleStruct mExampleField; + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FStructProperty*)NewProperty.Ptr)->Struct = (UScriptStruct*)ScriptStruct.Ptr; } + return true; + } + + public override bool CreateStructDTSpecial(out IFObjectProperty? NewProperty, + string Name, string TypeName, int Offset, PropertyVisibility Visibility) + { + NewProperty = null; + if (!GetProperty("ObjectProperty", out var PropertyClass) + || !Classes.GetScriptStructInfoFromName($"F{TypeName}", out var FieldClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFObjectProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = Marshal.SizeOf(); + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.None); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FObjectProperty*)NewProperty.Ptr)->PropertyClass = (UClass*)FieldClass!.Ptr; } // Our "class" + return true; + } public override bool CreateObject(out IFObjectProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) @@ -187,6 +300,15 @@ public override bool CreateString(out IFProperty? NewProperty, string Na public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); + + public override bool CreateName(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateCopyPropertyInner(out NewProperty, Name, Offset, "NameProperty", Visibility); + + public override bool CreateString(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateStringPropertyInner(out NewProperty, Name, Offset, "StrProperty", Visibility); + + public override bool CreateText(out IFProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility) + => CreateTextPropertyInner(out NewProperty, Name, Offset, "TextProperty", Visibility); public override bool CreateArray(out IFArrayProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, IFProperty Inner) @@ -209,4 +331,29 @@ public override bool CreateArray(out IFArrayProperty? NewProperty, strin unsafe { ((FArrayProperty*)Alloc)->Inner = (FProperty*)Inner.Ptr; } return true; } + + public override bool CreateMap(out IFMapProperty? NewProperty, string Name, int Offset, PropertyVisibility Visibility, + IFProperty Key, IFProperty Value) + { + NewProperty = null; + if (!GetProperty("MapProperty", out var PropertyClass)) + return false; + var Alloc = Memory.Malloc(Marshal.SizeOf(), FIELD_ALIGNMENT); + NewProperty = Factory.CreateFMapProperty(Alloc); + SetPropertySuperFieldsNoOwner(Factory.CreateFField(Alloc), Name, PropertyClass!); + unsafe + { + var pProperty = (FProperty*)Alloc; + pProperty->ArrayDim = 1; + pProperty->ElementSize = 0x50; // sizeof(TMap), usually + pProperty->PropertyFlags = Flags.CreatePropertyFlags(Visibility, PropertyBuilderFlags.NoCtor); + SetPropertyFieldDefaults(pProperty, Offset); + } + LinkToPropertyList(NewProperty, null); + unsafe { ((FMapProperty*)Alloc)->KeyProp = (FProperty*)Key.Ptr; } + unsafe { ((FMapProperty*)Alloc)->ValueProp = (FProperty*)Value.Ptr; } + Key.SetOwnerFField(NewProperty); + Value.SetOwnerFField(NewProperty); + return true; + } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs b/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs index a14486e..f3deaae 100644 --- a/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs +++ b/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs @@ -301,56 +301,124 @@ public bool AddI32Property(string Name, int Offset, out IFProperty? Pro where TObject : unmanaged { Property = null; - return PropertyFactory.CreateI32(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateI32(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddI64Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateI64(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateI64(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddU8Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateU8(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateU8(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddU16Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateU16(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateU16(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddU32Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateU32(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateU32(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddU64Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateU64(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateU64(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddF32Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateF32(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateF32(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddF64Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { Property = null; - return PropertyFactory.CreateF64(out Property, Name, Offset, PropertyVisibility.Public); + return PropertyFactory.CreateF64(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddI8Property(string Name, int Offset, out IFProperty? Property) + { + Property = null; + return PropertyFactory.CreateI8(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddI16Property(string Name, int Offset, out IFProperty? Property) + { + Property = null; + return PropertyFactory.CreateI16(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddI32Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateI32(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddI64Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateI64(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddU8Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateU8(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddU16Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateU16(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddU32Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateU32(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddU64Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateU64(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddF32Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateF32(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddF64Property(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateF64(out Property, Name, Offset, PropertyVisibility.Public); } public bool AddCBoolProperty(string Name, int Offset, out IFBoolProperty? Property) @@ -420,6 +488,15 @@ public bool AddArrayProperty(string Name, int Offset, IFProperty Inner, return PropertyFactory.CreateArray(out Property, Name, Offset, PropertyVisibility.Public, Inner); } + /* + public bool AddMapProperty(string Name, int Offset, + IFProperty Key, IFProperty Value, out IFMapProperty? Property) where TObject : unmanaged + { + Property = null; + return PropertyFactory.CreateMap(out Property, Name, Offset, PropertyVisibility.Public, Key, Value); + } + */ + public IFGenericPropertyParams? CreateI8Param(string Name, int Offset) => TypeFactory.CreateI8Param(Name, Offset, out var Out) ? Out : null; @@ -449,6 +526,53 @@ public bool AddArrayProperty(string Name, int Offset, IFProperty Inner, public IFGenericPropertyParams? CreateF64Param(string Name, int Offset) => TypeFactory.CreateF64Param(Name, Offset, out var Out) ? Out : null; + + public bool AddStructProperty(string Name, int Offset, out IFStructProperty? Property) + where TField : unmanaged + { + Property = null; + return PropertyFactory.CreateStruct(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddStructProperty(string Name, string TypeName, int Offset, out IFStructProperty? Property) + { + Property = null; + return PropertyFactory.CreateStruct(out Property, Name, TypeName, Offset, PropertyVisibility.Public); + } + + public bool AddStructProperty_DataTableSpecial(string Name, string TypeName, int Offset, out IFObjectProperty? Property) + { + Property = null; + return PropertyFactory.CreateStructDTSpecial(out Property, Name, TypeName, Offset, PropertyVisibility.Public); + } + + public bool AddNameProperty(string Name, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateName(out Property, Name, Offset, PropertyVisibility.Public); + } + + public bool AddStringProperty(string String, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateString(out Property, String, Offset, PropertyVisibility.Public); + } + + public bool AddTextProperty(string Text, int Offset, out IFProperty? Property) + + { + Property = null; + return PropertyFactory.CreateText(out Property, Text, Offset, PropertyVisibility.Public); + } + + public bool AddMapProperty(string Name, int Offset, + IFProperty Key, IFProperty Value, out IFMapProperty? Property) + { + Property = null; + return PropertyFactory.CreateMap(out Property, Name, Offset, PropertyVisibility.Public, Key, Value); + } public bool CreateScriptStruct(string Name, int Size, List Fields, out IUScriptStruct? Out) { From 8b8c8d168b0221d239b5f183a9a42d9cf75e4e26 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Mon, 20 Jul 2026 19:11:41 +0800 Subject: [PATCH 02/10] Add ProcessEvent for IUObject as successor to IUnrealMethods --- .../Types/Interfaces/IFunctionParam.cs | 57 +++++++++++ .../Common/FunctionParam/DoubleParam.cs | 6 ++ .../Unreal/Common/FunctionParam/FloatParam.cs | 6 ++ .../Unreal/Common/FunctionParam/Int16Param.cs | 6 ++ .../Unreal/Common/FunctionParam/Int32Param.cs | 6 ++ .../Unreal/Common/FunctionParam/Int64Param.cs | 6 ++ .../Unreal/Common/FunctionParam/Int8Param.cs | 6 ++ .../Unreal/Common/FunctionParam/IntParam.cs | 6 ++ .../Unreal/Common/FunctionParam/NameParam.cs | 7 ++ .../Common/FunctionParam/ObjectParam.cs | 6 ++ .../Common/FunctionParam/UInt16Param.cs | 6 ++ .../Common/FunctionParam/UInt32Param.cs | 6 ++ .../Common/FunctionParam/UInt64Param.cs | 6 ++ .../Unreal/Factories/BaseUnrealFactory.cs | 2 + .../Types/Unreal/Factories/IUnrealFactory.cs | 2 + .../Unreal/Factories/Interfaces/IUClass.cs | 2 + .../Unreal/Factories/Interfaces/IUObject.cs | 78 +++++++++++++++ .../Factories/UE4_27_2/UnrealFactory.cs | 96 ++++++++++--------- .../Unreal/Factories/UE5_2_1/UnrealFactory.cs | 42 ++++---- .../Unreal/Factories/UE5_4_4/UnrealFactory.cs | 96 ++++++++++--------- .../Unreal/Factories/UE5_6_1/UnrealFactory.cs | 42 ++++---- .../Unreal/Factories/UE5_7_4/UnrealFactory.cs | 24 ++--- .../Common/DynamicMap/String.cs | 4 +- UE.Toolkit.Reloaded/Mod.cs | 2 + .../ObjectWriters/Nodes/NodeFactory.cs | 2 +- .../ObjectWriters/Nodes/StrNode.cs | 5 +- .../chronos-win64-shipping/unreal.ini | 2 +- .../iostoretest_50-win64-shipping/unreal.ini | 4 +- .../iostoretest_51-win64-shipping/unreal.ini | 2 +- .../iostoretest_52-win64-shipping/unreal.ini | 2 +- .../iostoretest_55-win64-shipping/unreal.ini | 4 +- .../iostoretest_56-win64-shipping/unreal.ini | 4 +- .../iostoretest_57-win64-shipping/unreal.ini | 4 +- UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs | 39 ++++++-- 34 files changed, 433 insertions(+), 155 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DoubleParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/FloatParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int16Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int32Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int64Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int8Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/IntParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/NameParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ObjectParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt16Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt32Param.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt64Param.cs diff --git a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs new file mode 100644 index 0000000..ff40405 --- /dev/null +++ b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs @@ -0,0 +1,57 @@ +namespace UE.Toolkit.Core.Types.Interfaces; + +public interface IFunctionParam +{ + string PropertyType { get; } + void Write(nint Destination); + void Read(nint Destination); +} + +public abstract class FunctionParamCopyable(Ptr rvalue, string propertyType, IUnrealMemoryInternal? memory = null) + : IFunctionParam, IDisposable where T : unmanaged +{ + public unsafe T Value => *RValue.Value; + + protected Ptr RValue => rvalue; + + protected IUnrealMemoryInternal? Memory => memory; + + public string PropertyType => propertyType; + + private static unsafe void Copy(T* source, T* destination) => *destination = *source; + + public unsafe void Write(nint Destination) => Copy(RValue.Value, (T*)Destination); + + public unsafe void Read(nint Destination) => Copy((T*)Destination, RValue.Value); + + private bool IsDisposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + + if (Memory != null) + { + unsafe { Memory.Free((nint)RValue.Value); } + } + IsDisposed = true; + } + } + + ~FunctionParamCopyable() => Dispose(false); +} + +public enum ProcessEventResult +{ + Success, + CouldNotFindFunction, + ParameterTypeMismatch, +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DoubleParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DoubleParam.cs new file mode 100644 index 0000000..985cc54 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DoubleParam.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class DoubleParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "DoubleProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/FloatParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/FloatParam.cs new file mode 100644 index 0000000..618457b --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/FloatParam.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class FloatParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "FloatProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int16Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int16Param.cs new file mode 100644 index 0000000..b308535 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int16Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class Int16Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "Int16Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int32Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int32Param.cs new file mode 100644 index 0000000..7709b13 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int32Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class Int32Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "Int32Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int64Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int64Param.cs new file mode 100644 index 0000000..421e8f2 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int64Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class Int64Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "Int64Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int8Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int8Param.cs new file mode 100644 index 0000000..4713fb9 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Int8Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class Int8Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "Int8Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/IntParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/IntParam.cs new file mode 100644 index 0000000..24c74d4 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/IntParam.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class IntParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "IntProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/NameParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/NameParam.cs new file mode 100644 index 0000000..4ea956b --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/NameParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class NameParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "NameProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ObjectParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ObjectParam.cs new file mode 100644 index 0000000..646fdfb --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ObjectParam.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class ObjectParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "ObjectProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt16Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt16Param.cs new file mode 100644 index 0000000..198d2b1 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt16Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class UInt16Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "UInt16Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt32Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt32Param.cs new file mode 100644 index 0000000..54c048a --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt32Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class UInt32Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "UInt32Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt64Param.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt64Param.cs new file mode 100644 index 0000000..a80b94e --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/UInt64Param.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class UInt64Param(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "UInt64Property", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs index bf96c38..035d819 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs @@ -7,6 +7,8 @@ public abstract class BaseUnrealFactory : IUnrealFactory { public IUnrealMemoryInternal? Memory { get; set; } + public Action? ProcessEvent { get; set; } + public Func? CreateReturnParam { get; set; } public T Cast(IPtr obj) { diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs index ba3ae96..50ba330 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs @@ -13,6 +13,8 @@ public interface IUnrealFactory nint GetAlignment(IFProperty prop); IUnrealMemoryInternal? Memory { get; set; } + Action? ProcessEvent { get; set; } + Func? CreateReturnParam { get; set; } IFProperty CreateFProperty(nint ptr); IFBoolProperty CreateFBoolProperty(nint ptr); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs index e9a1136..b9932ff 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs @@ -8,6 +8,8 @@ public interface IUClass : IUStruct IUFunction? GetFunction(string Name); + IEnumerable GetFunctions(); + IUObject? ClassDefaultObject { get; } nint Constructor { get; } diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs index 08f7a2a..e71e956 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs @@ -1,3 +1,5 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; namespace UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; @@ -31,4 +33,80 @@ public interface IUObject : IPtr string GetPathName(); // IUObject GetWorld(); + + ProcessEventResult ProcessEvent(string Name, List Params, out IFunctionParam? Return); +} + +public abstract unsafe class BaseUObject(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : IUObject where TUObjectBase : unmanaged +{ + protected readonly TUObjectBase* _self = (TUObjectBase*)ptr; + protected readonly IUnrealFactory _factory = factory; + protected readonly IUnrealMemoryInternal _memory = memory; + + public nint Ptr => (nint)_self; + + public abstract nint VTable { get; } + public abstract EObjectFlags ObjectFlags { get; } + public abstract int InternalIndex { get; } + public abstract IUClass ClassPrivate { get; } + public abstract FName NamePrivate { get; } + public abstract IUObject? OuterPrivate { get; } + + public abstract bool IsChildOf(string type); + + public abstract IUObject GetOutermost(); + + public abstract string GetNativeName(); + + public abstract string GetPathName(); + + public ProcessEventResult ProcessEvent(string Name, List Params, out IFunctionParam? Return) + { + Return = null; + var Function = ClassPrivate.GetFunction(Name); + if (Function == null) + { + return ProcessEventResult.CouldNotFindFunction; + } + var Alloc = Function.GetTotalParameterSize() switch + { + 0 => nint.Zero, var SizeOf => _memory.Malloc(SizeOf) + }; + foreach (var (Index, Field) in Function.ChildProperties.Select((x, i) => (i, x))) + { + var Property = _factory.CreateFProperty(Field.Ptr); + if (Property.PropertyFlags.HasFlag(EPropertyFlags.CPF_ReturnParm)) + { + NativeMemory.Clear((void*)(Alloc + Property.Offset_Internal), (nuint)Property.ElementSize); + break; + } + var Parameter = Params[Index]; + if (Property.ClassPrivate.Name != Parameter.PropertyType) + { + return ProcessEventResult.ParameterTypeMismatch; + } + Parameter.Write(Alloc + Property.Offset_Internal); + } + _factory.ProcessEvent!(Ptr, Function, Alloc); + foreach (var (Index, Field) in Function.ChildProperties.Select((x, i) => (i, x))) + { + var Property = _factory.CreateFProperty(Field.Ptr); + if (Property.PropertyFlags.HasFlag(EPropertyFlags.CPF_ReturnParm)) + { + Return = _factory.CreateReturnParam!(Property); + Return.Read(Alloc + Property.Offset_Internal); + break; + } + if (Property.PropertyFlags.HasFlag(EPropertyFlags.CPF_OutParm)) + { + Params[Index].Read(Alloc + Property.Offset_Internal); + } + } + if (Alloc != nint.Zero) + { + _memory.Free(Alloc); + } + return ProcessEventResult.Success; + } } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs index 7fb39a2..b705b15 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Common; +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; using EFunctionFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EFunctionFlags; @@ -111,21 +112,21 @@ public override nint SizeOf() public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArrayUE4_27_2(ptr, this); - public override IUObject CreateUObject(nint ptr) => new UObjectUE4_27_2(ptr, this); + public override IUObject CreateUObject(nint ptr) => new UObjectUE4_27_2(ptr, this, Memory); - public override IUClass CreateUClass(nint ptr) => new UClassUE4_27_2(ptr, this); + public override IUClass CreateUClass(nint ptr) => new UClassUE4_27_2(ptr, this, Memory); - public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStructUE4_27_2(ptr, this); + public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStructUE4_27_2(ptr, this, Memory); - public override IUEnum CreateUEnum(nint ptr) => new UEnumUE4_27_2(ptr, this); + public override IUEnum CreateUEnum(nint ptr) => new UEnumUE4_27_2(ptr, this, Memory); - public override IUField CreateUField(nint ptr) => new UFieldUE4_27_2(ptr, this); + public override IUField CreateUField(nint ptr) => new UFieldUE4_27_2(ptr, this, Memory); - public override IUStruct CreateUStruct(nint ptr) => new UStructUE4_27_2(ptr, this); + public override IUStruct CreateUStruct(nint ptr) => new UStructUE4_27_2(ptr, this, Memory); - public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnumUE4_27_2(ptr, this); + public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnumUE4_27_2(ptr, this, Memory); - public override IUFunction CreateUFunction(nint ptr) => new UFunctionUE4_27_2(ptr, this); + public override IUFunction CreateUFunction(nint ptr) => new UFunctionUE4_27_2(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClassUE4_27_2(ptr, this); @@ -141,9 +142,9 @@ public override nint SizeOf() public override IFWorldContext CreateFWorldContext(nint ptr) => new FWorldContextUE4_27_2(ptr, this); - public override IUEngine CreateUEngine(nint ptr) => new UEngineUE4_27_2(ptr, this); + public override IUEngine CreateUEngine(nint ptr) => new UEngineUE4_27_2(ptr, this, Memory); - public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstanceUE4_27_2(ptr, this); + public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstanceUE4_27_2(ptr, this, Memory); public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() => new FStaticConstructObjectParametersUE4_27_2(this); @@ -355,14 +356,14 @@ public unsafe class FBytePropertyUE4_27_2(nint ptr, IUnrealFactory factory) public IUEnum? Enum => ((FByteProperty*)Ptr)->enum_data != null ? _factory.CreateUEnum((nint)((FByteProperty*)Ptr)->enum_data) : null; } -public unsafe class UUserDefinedEnumUE4_27_2(nint ptr, IUnrealFactory factory) - : UEnumUE4_27_2(ptr, factory), IUUserDefinedEnum +public unsafe class UUserDefinedEnumUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UEnumUE4_27_2(ptr, factory, memory), IUUserDefinedEnum { public TMap DisplayNameMap => *(TMap*)(&((UUserDefinedEnum*)ptr)->DisplayNameMap); } -public unsafe class UEnumUE4_27_2(nint ptr, IUnrealFactory factory) - : UFieldUE4_27_2(ptr, factory), IUEnum +public unsafe class UEnumUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UFieldUE4_27_2(ptr, factory, memory), IUEnum { private readonly UEnum* _self = (UEnum*)ptr; public string CppType => _self->cpp_type.ToString(); @@ -392,8 +393,8 @@ public bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? } } -public unsafe class UScriptStructUE4_27_2(nint ptr, IUnrealFactory factory) - : UStructUE4_27_2(ptr, factory), IUScriptStruct +public unsafe class UScriptStructUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStructUE4_27_2(ptr, factory, memory), IUScriptStruct { private readonly UScriptStruct* _self = (UScriptStruct*)ptr; public EStructFlags StructFlags => (EStructFlags)_self->flags; @@ -401,8 +402,8 @@ public unsafe class UScriptStructUE4_27_2(nint ptr, IUnrealFactory factory) public nint CppStructOps => _self->cpp_struct_ops; } -public unsafe class UStructUE4_27_2(nint ptr, IUnrealFactory factory) - : UFieldUE4_27_2(ptr, factory), IUStruct +public unsafe class UStructUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UFieldUE4_27_2(ptr, factory, memory), IUStruct { private readonly UStruct* _self = (UStruct*)ptr; @@ -493,8 +494,8 @@ public void Dispose() { } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } -public unsafe class UFieldUE4_27_2(nint ptr, IUnrealFactory factory) - : UObjectUE4_27_2(ptr, factory), IUField +public unsafe class UFieldUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObjectUE4_27_2(ptr, factory, memory), IUField { private readonly UField* _self = (UField*)ptr; @@ -502,36 +503,32 @@ public IUField? Next => _self->next != null ? _factory.CreateUField((nint)_self->next) : null; } -public unsafe class UObjectUE4_27_2(nint ptr, IUnrealFactory factory) : IUObject +public unsafe class UObjectUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : BaseUObject(ptr, factory, memory) { - private readonly UObjectBase* _self = (UObjectBase*)ptr; - protected readonly IUnrealFactory _factory = factory; + public override nint VTable => _self->_vtable; - public nint Ptr => (nint)_self; + public override EObjectFlags ObjectFlags => _self->ObjectFlags; - public nint VTable => _self->_vtable; + public override int InternalIndex => (int)_self->InternalIndex; - public EObjectFlags ObjectFlags => _self->ObjectFlags; + public override IUClass ClassPrivate => _factory.CreateUClass((nint)_self->ClassPrivate); - public int InternalIndex => (int)_self->InternalIndex; + public override FName NamePrivate => _self->NamePrivate; - public IUClass ClassPrivate => _factory.CreateUClass((nint)_self->ClassPrivate); + public override IUObject? OuterPrivate => _self->OuterPrivate != null ? _factory.CreateUObject((nint)_self->OuterPrivate) : null; - public FName NamePrivate => _self->NamePrivate; + public override bool IsChildOf(string type) => _self->IsChildOf(type); - public IUObject? OuterPrivate => _self->OuterPrivate != null ? _factory.CreateUObject((nint)_self->OuterPrivate) : null; + public override IUObject GetOutermost() => _factory.CreateUObject((nint)_self->GetOutermost()); - public bool IsChildOf(string type) => _self->IsChildOf(type); + public override string GetNativeName() => ToolkitUtils.GetNativeName(this); - public IUObject GetOutermost() => _factory.CreateUObject((nint)_self->GetOutermost()); - - public string GetNativeName() => ToolkitUtils.GetNativeName(this); - - public string GetPathName() => ToolkitUtils.GetPathName(this); + public override string GetPathName() => ToolkitUtils.GetPathName(this); } -public unsafe class UClassUE4_27_2(nint ptr, IUnrealFactory factory) - : UStructUE4_27_2(ptr, factory), IUClass +public unsafe class UClassUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStructUE4_27_2(ptr, factory, memory), IUClass { private readonly UClass* _self = (UClass*)ptr; @@ -549,6 +546,15 @@ public unsafe class UClassUE4_27_2(nint ptr, IUnrealFactory factory) : null; } + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->func_map), + _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + public IUObject? ClassDefaultObject => _self->class_default_obj != null ? factory.CreateUObject((nint)_self->class_default_obj) : null; @@ -557,8 +563,8 @@ public IUObject? ClassDefaultObject public EClassFlags ClassFlags => _self->class_flags; } -public unsafe class UFunctionUE4_27_2(nint ptr, IUnrealFactory factory) - : UStructUE4_27_2(ptr, factory), IUFunction +public unsafe class UFunctionUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStructUE4_27_2(ptr, factory, memory), IUFunction { private readonly UFunction* _self = (UFunction*)ptr; @@ -570,7 +576,7 @@ public unsafe class UFunctionUE4_27_2(nint ptr, IUnrealFactory factory) public int GetTotalParameterSize() { var LastProperty = ChildProperties.Any() ? _factory.CreateFProperty(ChildProperties.Last().Ptr) : null; - return LastProperty != null ? LastProperty!.Offset_Internal + LastProperty!.ElementSize : 0; + return LastProperty != null ? LastProperty.Offset_Internal + LastProperty.ElementSize : 0; } public nint FunctionPtr => _self->exec_func_ptr; @@ -720,8 +726,8 @@ public void Dispose() {} #endregion } -public unsafe class UEngineUE4_27_2(nint ptr, IUnrealFactory factory) - : UObjectUE4_27_2(ptr, factory), IUEngine +public unsafe class UEngineUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObjectUE4_27_2(ptr, factory, memory), IUEngine { private readonly UEngine* _self = (UEngine*)ptr; @@ -810,8 +816,8 @@ protected virtual void Disposing() #endregion } -public unsafe class UGameInstanceUE4_27_2(nint ptr, IUnrealFactory factory) - : UObjectUE4_27_2(ptr, factory), IUGameInstance +public unsafe class UGameInstanceUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObjectUE4_27_2(ptr, factory, memory), IUGameInstance { private readonly UGameInstance* _self = (UGameInstance*)ptr; diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs index 5ec6891..eab2d64 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs @@ -1,4 +1,5 @@ using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.UE5_4_4; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -73,14 +74,14 @@ public override nint SizeOf() public override IFOptionalProperty CreateFOptionalProperty(nint ptr) => throw new NotSupportedException(); public override IFDelegateProperty CreateFDelegateProperty(nint ptr) => new UE4_27_2.FDelegatePropertyUE4_27_2(ptr, this); public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArray_UE5_4_4(ptr, this); - public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this); - public override IUClass CreateUClass(nint ptr) => new UClass_UE5_2_1(ptr, this); - public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_2_1(ptr, this); - public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this); - public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this); - public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_2_1(ptr, this); - public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); + public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this, Memory); + public override IUClass CreateUClass(nint ptr) => new UClass_UE5_2_1(ptr, this, Memory); + public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_2_1(ptr, this, Memory); + public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this, Memory); + public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); + public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_2_1(ptr, this, Memory); + public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new UE4_27_2.FFieldVariantUE4_27_2(ptr, this); @@ -88,16 +89,16 @@ public override nint SizeOf() public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_2_1(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_2_1(ptr, this); public override IFWorldContext CreateFWorldContext(nint ptr) => new FWorldContext_UE5_4_4(ptr, this); - public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this); - public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this); + public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this, Memory); + public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this, Memory); public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() => new FStaticConstructObjectParameters_UE5_4_4(this); public override IFActorSpawnParameters CreateFActorSpawnParameters() => new FActorSpawnParameters_UE5_4_4(this); } -public unsafe class UScriptStruct_UE5_2_1(nint ptr, IUnrealFactory factory) - : UStruct_UE5_2_1(ptr, factory), IUScriptStruct +public unsafe class UScriptStruct_UE5_2_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_2_1(ptr, factory, memory), IUScriptStruct { private readonly UScriptStruct* _self = (UScriptStruct*)ptr; public EStructFlags StructFlags => _self->StructFlags; @@ -105,8 +106,8 @@ public unsafe class UScriptStruct_UE5_2_1(nint ptr, IUnrealFactory factory) public nint CppStructOps => _self->CppStructOps; } -public unsafe class UStruct_UE5_2_1(nint ptr, IUnrealFactory factory) - : UField_UE5_4_4(ptr, factory), IUStruct +public unsafe class UStruct_UE5_2_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UField_UE5_4_4(ptr, factory, memory), IUStruct { private readonly UStruct* _self = (UStruct*)ptr; @@ -135,8 +136,8 @@ public IEnumerable PostConstructLink _factory); } -public unsafe class UClass_UE5_2_1(nint ptr, IUnrealFactory factory) - : UStruct_UE5_2_1(ptr, factory), IUClass +public unsafe class UClass_UE5_2_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_2_1(ptr, factory, memory), IUClass { private readonly UClass* _self = (UClass*)ptr; @@ -153,6 +154,15 @@ public unsafe class UClass_UE5_2_1(nint ptr, IUnrealFactory factory) : null; } + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), + _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + public IUObject? ClassDefaultObject => _self->ClassDefaultObject != null ? factory.CreateUObject((nint)_self->ClassDefaultObject ) : null; diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs index 8a3331b..5e9509a 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Common; +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -58,14 +59,14 @@ public override nint SizeOf() public override IFOptionalProperty CreateFOptionalProperty(nint ptr) => new FOptionalProperty_UE5_4_4(ptr, this); public override IFDelegateProperty CreateFDelegateProperty(nint ptr) => new FDelegateProperty_UE5_4_4(ptr, this); public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArray_UE5_4_4(ptr, this); - public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this); - public override IUClass CreateUClass(nint ptr) => new UClass_UE5_4_4(ptr, this); - public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_4_4(ptr, this); - public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this); - public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this); - public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_4_4(ptr, this); - public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); + public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this, Memory); + public override IUClass CreateUClass(nint ptr) => new UClass_UE5_4_4(ptr, this, Memory); + public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_4_4(ptr, this, Memory); + public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this, Memory); + public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); + public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_4_4(ptr, this, Memory); + public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); @@ -73,8 +74,8 @@ public override nint SizeOf() public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); public override IFWorldContext CreateFWorldContext(nint ptr) => new FWorldContext_UE5_4_4(ptr, this); - public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this); - public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this); + public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this, Memory); + public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this, Memory); public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() => new FStaticConstructObjectParameters_UE5_4_4(this); public override IFActorSpawnParameters CreateFActorSpawnParameters() @@ -282,14 +283,14 @@ public unsafe class FByteProperty_UE5_4_4(nint ptr, IUnrealFactory factory) public IUEnum? Enum => ((FByteProperty*)Ptr)->Enum != null ? _factory.CreateUEnum((nint)((FByteProperty*)Ptr)->Enum) : null; } -public unsafe class UUserDefinedEnum_UE5_4_4(nint ptr, IUnrealFactory factory) - : UEnum_UE5_4_4(ptr, factory), IUUserDefinedEnum +public unsafe class UUserDefinedEnum_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UEnum_UE5_4_4(ptr, factory, memory), IUUserDefinedEnum { public TMap DisplayNameMap => ((UUserDefinedEnum*)Ptr)->DisplayNameMap; } -public unsafe class UEnum_UE5_4_4(nint ptr, IUnrealFactory factory) - : UField_UE5_4_4(ptr, factory), IUEnum +public unsafe class UEnum_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UField_UE5_4_4(ptr, factory, memory), IUEnum { private readonly UEnum* _self = (UEnum*)ptr; public string CppType => _self->CppType.ToString(); @@ -319,8 +320,8 @@ public bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? } } -public unsafe class UScriptStruct_UE5_4_4(nint ptr, IUnrealFactory factory) - : UStruct_UE5_4_4(ptr, factory), IUScriptStruct +public unsafe class UScriptStruct_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_4_4(ptr, factory, memory), IUScriptStruct { private readonly UScriptStruct* _self = (UScriptStruct*)ptr; public EStructFlags StructFlags => _self->StructFlags; @@ -328,8 +329,8 @@ public unsafe class UScriptStruct_UE5_4_4(nint ptr, IUnrealFactory factory) public nint CppStructOps => _self->CppStructOps; } -public unsafe class UStruct_UE5_4_4(nint ptr, IUnrealFactory factory) - : UField_UE5_4_4(ptr, factory), IUStruct +public unsafe class UStruct_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UField_UE5_4_4(ptr, factory, memory), IUStruct { private readonly UStruct* _self = (UStruct*)ptr; @@ -422,8 +423,8 @@ public void Dispose() { } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } -public unsafe class UField_UE5_4_4(nint ptr, IUnrealFactory factory) - : UObject_UE5_4_4(ptr, factory), IUField +public unsafe class UField_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObject_UE5_4_4(ptr, factory, memory), IUField { private readonly UField* _self = (UField*)ptr; @@ -431,36 +432,32 @@ public IUField? Next => _self->Next != null ? _factory.CreateUField((nint)_self->Next) : null; } -public unsafe class UObject_UE5_4_4(nint ptr, IUnrealFactory factory) : IUObject +public unsafe class UObject_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : BaseUObject(ptr, factory, memory) { - private readonly UObjectBase* _self = (UObjectBase*)ptr; - protected readonly IUnrealFactory _factory = factory; - - public nint Ptr => (nint)_self; - - public nint VTable => _self->VTable; + public override nint VTable => _self->VTable; - public EObjectFlags ObjectFlags => _self->ObjectFlags; + public override EObjectFlags ObjectFlags => _self->ObjectFlags; - public int InternalIndex => _self->InternalIndex; + public override int InternalIndex => _self->InternalIndex; - public IUClass ClassPrivate => _factory.CreateUClass((nint)_self->ClassPrivate); + public override IUClass ClassPrivate => _factory.CreateUClass((nint)_self->ClassPrivate); - public FName NamePrivate => _self->NamePrivate; + public override FName NamePrivate => _self->NamePrivate; - public IUObject? OuterPrivate => _self->OuterPrivate != null ? _factory.CreateUObject((nint)_self->OuterPrivate) : null; + public override IUObject? OuterPrivate => _self->OuterPrivate != null ? _factory.CreateUObject((nint)_self->OuterPrivate) : null; - public bool IsChildOf(string type) => _self->IsChildOf(type); + public override bool IsChildOf(string type) => _self->IsChildOf(type); - public IUObject GetOutermost() => _factory.CreateUObject((nint)_self->GetOutermost()); + public override IUObject GetOutermost() => _factory.CreateUObject((nint)_self->GetOutermost()); - public string GetNativeName() => ToolkitUtils.GetNativeName(this); + public override string GetNativeName() => ToolkitUtils.GetNativeName(this); - public string GetPathName() => ToolkitUtils.GetPathName(this); + public override string GetPathName() => ToolkitUtils.GetPathName(this); } -public unsafe class UClass_UE5_4_4(nint ptr, IUnrealFactory factory) - : UStruct_UE5_4_4(ptr, factory), IUClass +public unsafe class UClass_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_4_4(ptr, factory, memory), IUClass { private readonly UClass* _self = (UClass*)ptr; @@ -477,6 +474,15 @@ public unsafe class UClass_UE5_4_4(nint ptr, IUnrealFactory factory) : null; } + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), + _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + public IUObject? ClassDefaultObject => _self->ClassDefaultObject != null ? factory.CreateUObject((nint)_self->ClassDefaultObject ) : null; @@ -484,8 +490,8 @@ public IUObject? ClassDefaultObject public EClassFlags ClassFlags => _self->ClassFlags; } -public unsafe class UFunction_UE5_4_4(nint ptr, IUnrealFactory factory) - : UStruct_UE5_4_4(ptr, factory), IUFunction +public unsafe class UFunction_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_4_4(ptr, factory, memory), IUFunction { private readonly UFunction* _self = (UFunction*)ptr; @@ -497,7 +503,7 @@ public unsafe class UFunction_UE5_4_4(nint ptr, IUnrealFactory factory) public int GetTotalParameterSize() { var LastProperty = ChildProperties.Any() ? _factory.CreateFProperty(ChildProperties.Last().Ptr) : null; - return LastProperty != null ? LastProperty!.Offset_Internal + LastProperty!.ElementSize : 0; + return LastProperty != null ? LastProperty.Offset_Internal + LastProperty.ElementSize : 0; } public nint FunctionPtr => (nint)_self->Func; @@ -646,8 +652,8 @@ public void Dispose() {} #endregion } -public unsafe class UEngine_UE5_4_4(nint ptr, IUnrealFactory factory) - : UObject_UE5_4_4(ptr, factory), IUEngine +public unsafe class UEngine_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObject_UE5_4_4(ptr, factory, memory), IUEngine { private readonly UEngine* _self = (UEngine*)ptr; @@ -736,8 +742,8 @@ protected virtual void Disposing() #endregion } -public unsafe class UGameInstance_UE5_4_4(nint ptr, IUnrealFactory factory) - : UObject_UE5_4_4(ptr, factory), IUGameInstance +public unsafe class UGameInstance_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UObject_UE5_4_4(ptr, factory, memory), IUGameInstance { private readonly UGameInstance* _self = (UGameInstance*)ptr; diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs index 847a4cc..cd69976 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs @@ -1,3 +1,4 @@ +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.UE5_4_4; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -54,14 +55,14 @@ public override nint SizeOf() public override IFOptionalProperty CreateFOptionalProperty(nint ptr) => new FOptionalProperty_UE5_4_4(ptr, this); public override IFDelegateProperty CreateFDelegateProperty(nint ptr) => new FDelegateProperty_UE5_4_4(ptr, this); public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArray_UE5_4_4(ptr, this); - public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this); - public override IUClass CreateUClass(nint ptr) => new UClass_UE5_6_1(ptr, this); - public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_6_1(ptr, this); - public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this); - public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this); - public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_6_1(ptr, this); - public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); + public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this, Memory); + public override IUClass CreateUClass(nint ptr) => new UClass_UE5_6_1(ptr, this, Memory); + public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UScriptStruct_UE5_6_1(ptr, this, Memory); + public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_4_4(ptr, this, Memory); + public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); + public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_6_1(ptr, this, Memory); + public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); @@ -69,16 +70,16 @@ public override nint SizeOf() public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); public override IFWorldContext CreateFWorldContext(nint ptr) => new FWorldContext_UE5_4_4(ptr, this); - public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this); - public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this); + public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this, Memory); + public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this, Memory); public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() => new FStaticConstructObjectParameters_UE5_4_4(this); public override IFActorSpawnParameters CreateFActorSpawnParameters() => new FActorSpawnParameters_UE5_4_4(this); } -public unsafe class UStruct_UE5_6_1(nint ptr, IUnrealFactory factory) - : UField_UE5_4_4(ptr, factory), IUStruct +public unsafe class UStruct_UE5_6_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UField_UE5_4_4(ptr, factory, memory), IUStruct { private readonly UStruct* _self = (UStruct*)ptr; @@ -107,8 +108,8 @@ public IEnumerable PostConstructLink _factory); } -public unsafe class UScriptStruct_UE5_6_1(nint ptr, IUnrealFactory factory) - : UStruct_UE5_6_1(ptr, factory), IUScriptStruct +public unsafe class UScriptStruct_UE5_6_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_6_1(ptr, factory, memory), IUScriptStruct { private readonly UScriptStruct* _self = (UScriptStruct*)ptr; public EStructFlags StructFlags => _self->StructFlags; @@ -116,8 +117,8 @@ public unsafe class UScriptStruct_UE5_6_1(nint ptr, IUnrealFactory factory) public nint CppStructOps => _self->CppStructOps; } -public unsafe class UClass_UE5_6_1(nint ptr, IUnrealFactory factory) - : UStruct_UE5_6_1(ptr, factory), IUClass +public unsafe class UClass_UE5_6_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_6_1(ptr, factory, memory), IUClass { private readonly UClass* _self = (UClass*)ptr; @@ -134,6 +135,15 @@ public unsafe class UClass_UE5_6_1(nint ptr, IUnrealFactory factory) : null; } + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), + _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + public IUObject? ClassDefaultObject => _self->ClassDefaultObject != null ? factory.CreateUObject((nint)_self->ClassDefaultObject ) : null; diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs index 058e911..ad0f10b 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs @@ -56,14 +56,14 @@ public override nint SizeOf() public override IFOptionalProperty CreateFOptionalProperty(nint ptr) => new FOptionalProperty_UE5_4_4(ptr, this); public override IFDelegateProperty CreateFDelegateProperty(nint ptr) => new FDelegateProperty_UE5_4_4(ptr, this); public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArray_UE5_7_4(ptr, this); - public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this); - public override IUClass CreateUClass(nint ptr) => new UE5_6_1.UClass_UE5_6_1(ptr, this); - public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UE5_6_1.UScriptStruct_UE5_6_1(ptr, this); - public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_7_4(ptr, this); - public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this); - public override IUStruct CreateUStruct(nint ptr) => new UE5_6_1.UStruct_UE5_6_1(ptr, this); - public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this); + public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this, Memory); + public override IUClass CreateUClass(nint ptr) => new UE5_6_1.UClass_UE5_6_1(ptr, this, Memory); + public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UE5_6_1.UScriptStruct_UE5_6_1(ptr, this, Memory); + public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_7_4(ptr, this, Memory); + public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); + public override IUStruct CreateUStruct(nint ptr) => new UE5_6_1.UStruct_UE5_6_1(ptr, this, Memory); + public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_7_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); @@ -71,8 +71,8 @@ public override nint SizeOf() public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_4_4(ptr, this); public override IFGenericPropertyParams CreateFGenericPropertyParams(nint ptr) => new FGenericPropertyParams_UE5_4_4(ptr, this); public override IFWorldContext CreateFWorldContext(nint ptr) => new FWorldContext_UE5_4_4(ptr, this); - public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this); - public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this); + public override IUEngine CreateUEngine(nint ptr) => new UEngine_UE5_4_4(ptr, this, Memory); + public override IUGameInstance CreateUGameInstance(nint ptr) => new UGameInstance_UE5_4_4(ptr, this, Memory); public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() => new FStaticConstructObjectParameters_UE5_4_4(this); public override IFActorSpawnParameters CreateFActorSpawnParameters() @@ -127,8 +127,8 @@ public void RemoveFromRootSet(int idx) } } -public unsafe class UEnum_UE5_7_4(nint ptr, IUnrealFactory factory) - : UField_UE5_4_4(ptr, factory), IUEnum, IDisposable +public unsafe class UEnum_UE5_7_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UField_UE5_4_4(ptr, factory, memory), IUEnum, IDisposable { private readonly Unreal.UE5_7_4.UEnum* _self = (Unreal.UE5_7_4.UEnum*)ptr; public string CppType => _self->CppType.ToString(); diff --git a/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs b/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs index 48351db..774d59c 100644 --- a/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs +++ b/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs @@ -17,7 +17,9 @@ public class StringDynamicMapKeyType(IFMapProperty property, IUnrealFactory fact public override unsafe bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) { - key = new StringDynamicMapKey(*Objects.CreateFString(text), Memory); + var pString = Objects.CreateFString(text); + key = new StringDynamicMapKey(*pString, Memory); + Memory.Free((nint)pString); return true; } diff --git a/UE.Toolkit.Reloaded/Mod.cs b/UE.Toolkit.Reloaded/Mod.cs index d57ecda..4a285c2 100644 --- a/UE.Toolkit.Reloaded/Mod.cs +++ b/UE.Toolkit.Reloaded/Mod.cs @@ -87,6 +87,8 @@ public Mod(ModContext context) _address = new(); _classes = new(_factory, _memory, _objects, _hooks, _address); _methods = new(_factory, _memory, _classes, _objects, _hooks); + _factory.ProcessEvent = _methods.CallProcessEvent; + _factory.CreateReturnParam = _methods.CreateReturnParam; _state = new(_factory, _classes); _spawning = new(_classes, _factory, _state); _writer = new(_objects, _tables, _memory, _classes, _factory); diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs index 3b3ff5b..50a0796 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs @@ -44,7 +44,7 @@ public unsafe IFieldNode Create(IFProperty property, nint fieldPtr) "FloatProperty" => new FloatNode(property, new((float*)fieldPtr)), "DoubleProperty" => new DoubleNode(property, new((double*)fieldPtr)), "NameProperty" => new NameNode(property, new((FName*)fieldPtr)), - "StrProperty" => new StrNode(property, new((FString*)fieldPtr), Objects), + "StrProperty" => new StrNode(property, new((FString*)fieldPtr), Objects, Memory), "TextProperty" => new TextNode(property, new((FText*)fieldPtr), Objects), "ObjectProperty" => new ObjectNode(Factory.CreateFObjectProperty(property.Ptr), fieldPtr, this), "SoftObjectProperty" => new SoftObjectNode(property, new((FSoftObjectPtr*)fieldPtr), Classes), diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs index 8153dd7..541bad5 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs @@ -1,20 +1,23 @@ using System.Runtime.InteropServices; using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; using UE.Toolkit.Interfaces; namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; -public class StrNode(IFProperty property, Ptr value, IUnrealObjects unrealObjects) +public class StrNode(IFProperty property, Ptr value, IUnrealObjects unrealObjects, IUnrealMemoryInternal unrealMemory) : TextPrimitiveNode(property, value) { private IUnrealObjects UnrealObjects => unrealObjects; + private IUnrealMemoryInternal UnrealMemory => unrealMemory; protected override unsafe void SetField(string text) { var fstring = UnrealObjects.CreateFString(text); *Value.Value = *fstring; + UnrealMemory.Free((nint)fstring); } protected override unsafe void SetInitialValue() diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/unreal.ini index 99fd744..9e43853 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/unreal.ini @@ -11,7 +11,7 @@ QuantizeSize=0x48 ProcessEvent=0x268 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x380 [UPackage] GamePackage=/Script/Chronos diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/unreal.ini index 071aafa..42abbbd 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/unreal.ini @@ -8,10 +8,10 @@ Realloc=0x20 QuantizeSize=0x38 [UObject] -ProcessEvent=0x268 +ProcessEvent=0x258 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x370 [UPackage] GamePackage=/Script/IOStoreTest_50 diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/unreal.ini index dd33289..d6e2a24 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/unreal.ini @@ -8,7 +8,7 @@ Realloc=0x20 QuantizeSize=0x38 [UObject] -ProcessEvent=0x268 +ProcessEvent=0x260 [UScriptStruct] GetCustomGuid=0x378 diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/unreal.ini index 1a1ed15..7f4b396 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/unreal.ini @@ -11,7 +11,7 @@ QuantizeSize=0x48 ProcessEvent=0x268 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x380 [UPackage] GamePackage=/Script/IOStoreTest_52 diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/unreal.ini index f0a1ad4..c1646cd 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/unreal.ini @@ -8,10 +8,10 @@ Realloc=0x38 QuantizeSize=0x60 [UObject] -ProcessEvent=0x268 +ProcessEvent=0x278 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x398 [UPackage] GamePackage=/Script/IOStoreTest_55 diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/unreal.ini index 15a830b..735ae61 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/unreal.ini @@ -8,10 +8,10 @@ Realloc=0x38 QuantizeSize=0x60 [UObject] -ProcessEvent=0x268 +ProcessEvent=0x260 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x380 [UPackage] GamePackage=/Script/IOStoreTest_56 diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/unreal.ini index 887d641..14ff3fc 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/unreal.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/unreal.ini @@ -8,10 +8,10 @@ Realloc=0x38 QuantizeSize=0x60 [UObject] -ProcessEvent=0x268 +ProcessEvent=0x260 [UScriptStruct] -GetCustomGuid=0x378 +GetCustomGuid=0x380 [UPackage] GamePackage=/Script/IOStoreTest_57 diff --git a/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs b/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs index 70f33c0..099dc2e 100644 --- a/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs +++ b/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs @@ -2,6 +2,7 @@ using Reloaded.Hooks.Definitions; using UE.Toolkit.Core.Types; using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using EPropertyFlags = UE.Toolkit.Core.Types.Unreal.UE5_4_4.EPropertyFlags; @@ -142,7 +143,7 @@ public unsafe void FromAlloc(nint pAlloc) public class UnrealMethods : IUnrealMethods { - #region Function Invocation Parameters + #region Function Invocation Parameters (OLD) public IInvocationParameter CreateI8Param(sbyte Value = 0) => new I8InvocationParameter(Value); public IInvocationParameter CreateI16Param(short Value = 0) => new I16InvocationParameter(Value); @@ -160,7 +161,7 @@ public class UnrealMethods : IUnrealMethods #endregion - #region Function Invocation Execute + #region Function Invocation Execute (OLD) private delegate void UObject_ProcessEvent(nint Object, nint TargetFunction, nint Params); private uint UObject_ProcessEvent_Offset; @@ -177,6 +178,7 @@ private bool ProcessEventInner(ToolkitUObject Object, string N ref List Parameters, out IUFunction? Function, out nint Alloc, ExecutionFlags Flags) where TObject : unmanaged { + Log.Warning("IUnrealMethods::ProcessEvent is deprecated! Use IUObject::ProcessEvent instead!\n"); // Get type reflection for object type Function = null; Alloc = nint.Zero; @@ -223,11 +225,8 @@ private bool ProcessEventInner(ToolkitUObject Object, string N } Parameter.ToAlloc(Alloc + Property.Offset_Internal); } - unsafe - { - var ProcessEventWrapper = Hooks.CreateWrapper(*(nint*)(Function.VTable + UObject_ProcessEvent_Offset), out _); - ProcessEventWrapper((nint)Object.Self, Function.Ptr, Alloc); - } + + unsafe { CallProcessEvent((nint)Object.Self, Function, Alloc); } return true; } @@ -332,6 +331,32 @@ public TReturnType ProcessEvent(ToolkitUObject Ob } #endregion + + internal unsafe void CallProcessEvent(nint Object, IUFunction Function, nint Alloc) + { + var ProcessEventWrapper = Hooks.CreateWrapper(*(nint*)(Function.VTable + UObject_ProcessEvent_Offset), out _); + ProcessEventWrapper(Object, Function.Ptr, Alloc); + } + + internal unsafe IFunctionParam CreateReturnParam(IFProperty property) + { + return property.ClassPrivate.Name switch + { + "Int8Property" => new Int8Param(new((byte*)Memory.Malloc(sizeof(byte))), Memory), + "Int16Property" => new Int16Param(new((short*)Memory.Malloc(sizeof(short))), Memory), + "Int32Property" => new Int32Param(new((int*)Memory.Malloc(sizeof(int))), Memory), + "IntProperty" => new IntParam(new((int*)Memory.Malloc(sizeof(int))), Memory), + "Int64Property" => new Int64Param(new((long*)Memory.Malloc(sizeof(long))), Memory), + "UInt16Property" => new UInt16Param(new((ushort*)Memory.Malloc(sizeof(ushort))), Memory), + "UInt32Property" => new UInt32Param(new((uint*)Memory.Malloc(sizeof(uint))), Memory), + "UInt64Property" => new UInt64Param(new((ulong*)Memory.Malloc(sizeof(ulong))), Memory), + "FloatProperty" => new FloatParam(new((float*)Memory.Malloc(sizeof(float))), Memory), + "DoubleProperty" => new DoubleParam(new((double*)Memory.Malloc(sizeof(double))), Memory), + "NameProperty" => new NameParam(new((FName*)Memory.Malloc(sizeof(FName))), Memory), + "ObjectProperty" => new ObjectParam(new((nint*)Memory.Malloc(sizeof(nint))), Memory), + _ => throw new NotSupportedException($"CreateReturnParam with property {property.ClassPrivate.Name}") + }; + } #region Unreal Toolkit API References From 3f3085d1ba426ebf64b6e5114ddff8b4ad050d6c Mon Sep 17 00:00:00 2001 From: Rirurin Date: Tue, 21 Jul 2026 19:26:18 +0800 Subject: [PATCH 03/10] Rework type dumper, generate UObjects as classes --- .../Unreal/Factories/Interfaces/IUClass.cs | 2 + .../Unreal/Factories/Interfaces/IUObject.cs | 4 + .../Factories/UE4_27_2/UnrealFactory.cs | 2 + .../Unreal/Factories/UE5_2_1/UnrealFactory.cs | 1 + .../Unreal/Factories/UE5_4_4/UnrealFactory.cs | 1 + .../Unreal/Factories/UE5_6_1/UnrealFactory.cs | 1 + UE.Toolkit.DumperMod/Context.cs | 32 ++ UE.Toolkit.DumperMod/Definitions/Base.cs | 18 + UE.Toolkit.DumperMod/Definitions/Builtins.cs | 103 ++++++ UE.Toolkit.DumperMod/Definitions/Class.cs | 97 +++++ UE.Toolkit.DumperMod/Definitions/Enum.cs | 103 ++++++ UE.Toolkit.DumperMod/Definitions/Function.cs | 1 + UE.Toolkit.DumperMod/Definitions/Property.cs | 333 ++++++++++++++++++ UE.Toolkit.DumperMod/Definitions/Registry.cs | 138 ++++++++ UE.Toolkit.DumperMod/Definitions/Struct.cs | 97 +++++ UE.Toolkit.DumperMod/Dumper.cs | 6 +- UE.Toolkit.DumperMod/DumperEx.cs | 83 +++++ UE.Toolkit.DumperMod/Mod.cs | 11 +- UE.Toolkit.DumperMod/ModConfig.json | 14 +- .../UE.Toolkit.DumperMod.csproj | 5 +- 20 files changed, 1045 insertions(+), 7 deletions(-) create mode 100644 UE.Toolkit.DumperMod/Context.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Base.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Builtins.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Class.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Enum.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Function.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Property.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Registry.cs create mode 100644 UE.Toolkit.DumperMod/Definitions/Struct.cs create mode 100644 UE.Toolkit.DumperMod/DumperEx.cs diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs index b9932ff..f14f2db 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUClass.cs @@ -15,4 +15,6 @@ public interface IUClass : IUStruct nint Constructor { get; } EClassFlags ClassFlags { get; } + + EClassCastFlags ClassCastFlags { get; } } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs index e71e956..a6a08b2 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs @@ -35,6 +35,8 @@ public interface IUObject : IPtr // IUObject GetWorld(); ProcessEventResult ProcessEvent(string Name, List Params, out IFunctionParam? Return); + + IUnrealFactory GetFactory(); } public abstract unsafe class BaseUObject(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) @@ -43,6 +45,8 @@ public abstract unsafe class BaseUObject(nint ptr, IUnrealFactory protected readonly TUObjectBase* _self = (TUObjectBase*)ptr; protected readonly IUnrealFactory _factory = factory; protected readonly IUnrealMemoryInternal _memory = memory; + + public IUnrealFactory GetFactory() => _factory; public nint Ptr => (nint)_self; diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs index b705b15..595d218 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs @@ -561,6 +561,8 @@ public IUObject? ClassDefaultObject public nint Constructor => _self->class_ctor; public EClassFlags ClassFlags => _self->class_flags; + + public EClassCastFlags ClassCastFlags => _self->class_cast_flags; } public unsafe class UFunctionUE4_27_2(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs index eab2d64..2322f16 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs @@ -168,6 +168,7 @@ public IUObject? ClassDefaultObject public nint Constructor => _self->ClassConstructor; public EClassFlags ClassFlags => _self->ClassFlags; + public EClassCastFlags ClassCastFlags => _self->ClassCastFlags; } public unsafe class FStructParams_UE5_2_1(nint ptr, IUnrealFactory factory) : IFStructParams diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs index 5e9509a..c863481 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs @@ -488,6 +488,7 @@ public IUObject? ClassDefaultObject public nint Constructor => _self->ClassConstructor; public EClassFlags ClassFlags => _self->ClassFlags; + public EClassCastFlags ClassCastFlags => _self->ClassCastFlags; } public unsafe class UFunction_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs index cd69976..f0ca8b0 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs @@ -149,4 +149,5 @@ public IUObject? ClassDefaultObject public nint Constructor => _self->ClassConstructor; public EClassFlags ClassFlags => _self->ClassFlags; + public EClassCastFlags ClassCastFlags => _self->ClassCastFlags; } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Context.cs b/UE.Toolkit.DumperMod/Context.cs new file mode 100644 index 0000000..304eece --- /dev/null +++ b/UE.Toolkit.DumperMod/Context.cs @@ -0,0 +1,32 @@ +using UE.Toolkit.Core.Types.Unreal.Factories; +using UE.Toolkit.DumperMod.Definitions; +using UE.Toolkit.Interfaces; +using UnrealEssentials.Interfaces; + +namespace UE.Toolkit.DumperMod; + +public class Context +{ + public Context(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, IUnrealClasses classes, + string dumpDir, IUnrealEssentials essentials) + { + DumpDirectory = dumpDir; + Objects = uobjs; + Strings = strs; + Factory = factory; + Classes = classes; + Essentials = essentials; + Builtins = new(essentials); + Registry = new(this); + } + + public string DumpDirectory { get; } + + public IUnrealObjects Objects { get; } + public IUnrealStrings Strings { get; } + public IUnrealFactory Factory { get; } + public IUnrealClasses Classes { get; } + public IUnrealEssentials Essentials { get; } + public Builtins Builtins { get; } + public Registry Registry { get; } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Base.cs b/UE.Toolkit.DumperMod/Definitions/Base.cs new file mode 100644 index 0000000..bd94de3 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Base.cs @@ -0,0 +1,18 @@ +namespace UE.Toolkit.DumperMod.Definitions; + +public interface IObjectFactory +{ + void Register(); +} + +public interface ISerializable +{ + string Serialize(Context context); +} + +public abstract class BaseObjectFactory(Context context) : IObjectFactory +{ + protected Context Context = context; + + public abstract void Register(); +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs new file mode 100644 index 0000000..63a43ff --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -0,0 +1,103 @@ +using System.Text; +using UnrealEssentials.Interfaces; + +namespace UE.Toolkit.DumperMod.Definitions; + +public class Builtins(IUnrealEssentials essentials) +{ + private IUnrealEssentials Essentials = essentials; + + private List GetDefaultUsings() + { + List Usings = [ + "System.Runtime.InteropServices", + "UE.Toolkit.Core.Types", + "UE.Toolkit.Core.Types.Unreal.UE5_4_4", + ]; + var VerParts = Essentials.GetEngineVersion().Split("-")[^1].Split("."); + // FText definition is different for versions below UE 5.4 + if (int.Parse(VerParts[0]) < 5 || int.Parse(VerParts[1]) < 4) + Usings.Add("FText = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FText"); + return Usings; + } + + public void AddHeader(StringBuilder sb) + { + sb.AppendLine(""" +/* Generated with UE Toolkit: Dumper (1.10.0) */ +/* GitHub: https://github.com/RyoTune/UE.Toolkit */ +/* Author: RyoTune and Rirurin */ +/* Special thanks to UE4SS team whose code was */ +/* used for reference. */ + +"""); + + foreach (var use in GetDefaultUsings()) + sb.AppendLine($"using {use};"); + + if (!string.IsNullOrEmpty(Mod.Config.FileUsings)) + { + var usings = Mod.Config.FileUsings.Split(';', + StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Replace("using ", string.Empty)); + foreach (var use in usings) sb.AppendLine($"using {use};"); + } + + sb.AppendLine(); + if (!string.IsNullOrEmpty(Mod.Config.FileNamespace)) + { + sb.AppendLine($"namespace {Mod.Config.FileNamespace.TrimEnd(';').Replace("namespace ", string.Empty)};"); + sb.AppendLine(); + } + } + + public void AddBaseObjectDefinition(StringBuilder sb) + { +sb.AppendLine(""" +public interface ITypeRepr where TRepr: unmanaged +{ + unsafe TRepr* Repr { get; } +} + +public abstract class ObjectImpl +{ + public IUObject Inner { get; } + protected static Dictionary? FieldOffsets; + + protected ObjectImpl(IUObject inner) { + Inner = inner; + FieldOffsets ??= Inner.ClassPrivate.PropertyLink + .Select(x => (x.NamePrivate, x.Offset_Internal)).ToDictionary(); + } +} + +"""); + } + + public static string SanitizeName(string name) + { + var parts = name.Split('_'); + if (parts.Last().Length == 32 && parts.Length > 2) + { + name = string.Join(string.Empty, parts[..^2]); + } + + name = name.Replace(' ', '_'); + name = name.Replace('-', '_'); + name = name.Replace('/', '_'); + name = name.Replace("?", string.Empty); + name = name.Replace("&", string.Empty); + name = name.Replace('(', '_'); + name = name.Replace(')', '_'); + name = name.Replace('[', '_'); + name = name.Replace(']', '_'); + if (name == "object") name = "_object"; + + if (char.IsDigit(name[0])) + { + name = '_' + name; + } + + return name; + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Class.cs b/UE.Toolkit.DumperMod/Definitions/Class.cs new file mode 100644 index 0000000..31cbd98 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Class.cs @@ -0,0 +1,97 @@ +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.DumperMod.Definitions; + +public class ClassFactory(Context context, IUClass uclass) : BaseObjectFactory(context) +{ + public override void Register() { + var className = uclass.NamePrivate.ToString(); + var classNativeName = uclass.GetNativeName(); + var size = uclass.PropertiesSize; + var alignment = uclass.MinAlignment; + + // TODO: Flag stuff? + + var super = uclass.GetSuperClass(); + var superSize = super?.PropertiesSize ?? 0; + var superName = super?.NamePrivate.ToString(); + //var superNativeName = super != null ? GetNativeClassName(super) : "UObjectBaseUtility"; + + // TODO: Add super header data. + + // TODO: Generate delegates. + + var propClass = new PropertyClassFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); + var propStruct = new PropertyStructReprFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); + + // TODO: Generate functions. + + Context.Registry.Structs[className] = + new ClassDefinition(className, classNativeName, size, alignment, propClass, propStruct, superName); + + Log.Debug($"UObject: {classNativeName}"); + } +} + +public class ClassDefinition( + string internalName, + string displayName, + int size, + int alignment, + List propClass, + List propStruct, + string? superInternalName) + : StructDefinition(internalName, displayName, size, alignment, propClass, superInternalName) +{ + private List PropStruct => propStruct; + + public override string Serialize(Context context) + { + var sb = new StringBuilder(); + if (Mod.Config.Mode == DumpFileMode.FilePerType) context.Builtins.AddHeader(sb); + var DisplayNameCS = Builtins.SanitizeName(DisplayName); + var DisplayNameRepr = $"{DisplayNameCS}_Repr"; + StructDefinition? SuperDef = null; + if (SuperInternalName != null) context.Registry.Structs.TryGetValue(SuperInternalName, out SuperDef); + var SuperDefName = SuperDef != null ? Builtins.SanitizeName(SuperDef.DisplayName) : "ObjectImpl"; + sb.AppendLine($"public class {DisplayNameCS}(IUObject inner) : {SuperDefName}(inner), ITypeRepr<{DisplayNameRepr}>\n{{"); + var ReprNewModifier = DisplayNameCS != "UObject" ? "new " : string.Empty; + sb.AppendLine($"\tpublic {ReprNewModifier}unsafe {DisplayNameRepr}* Repr => ({DisplayNameRepr}*)Inner.Ptr;\n"); + foreach (var prop in Properties) + sb.AppendLine(prop.Serialize(context)); + sb.AppendLine("}\n"); + var reprDef = new StructDefinitionRepr(InternalName, DisplayName, Size, Alignment, PropStruct, SuperInternalName); + sb.AppendLine(reprDef.Serialize(context)); + return sb.ToString(); + } + + public override ObjectType Type => ObjectType.Class; +} + +public class StructDefinitionRepr( + string internalName, + string displayName, + int size, + int alignment, + List properties, + string? superInternalName) + : StructDefinition(internalName, displayName, size, alignment, properties, superInternalName) +{ + public override string Serialize(Context context) + { + var sb = new StringBuilder(); + + if (Mod.Config.Mode == DumpFileMode.FilePerType) context.Builtins.AddHeader(sb); + + var DisplayNameCS = Builtins.SanitizeName(DisplayName); + var DisplayNameRepr = $"{DisplayNameCS}_Repr"; + var (superName, superSize) = GetSuperInfo(context); + var superNameFmt = superName != null ? $"{superName}_Repr" : null; + WriteStructBase(sb, DisplayNameRepr, superNameFmt, superSize, context); + sb.AppendLine($"\n\tpublic {DisplayNameCS} ToManaged(IUnrealFactory factory)\n\t{{"); + sb.AppendLine($"\t\tfixed ({DisplayNameRepr}* self = &this) return new(factory.CreateUObject((nint)self));"); + sb.AppendLine("\t}\n}"); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Enum.cs b/UE.Toolkit.DumperMod/Definitions/Enum.cs new file mode 100644 index 0000000..2a7bff5 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Enum.cs @@ -0,0 +1,103 @@ +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.DumperMod.Definitions; + +public class EnumFactory(Context context, IUEnum uenum, string? knownType = null) : BaseObjectFactory(context) +{ + public override void Register() + { + var name = uenum.NamePrivate.ToString(); + if (Context.Registry.Enums.ContainsKey(name) && knownType == null) return; + + var entries = new Dictionary(); + + var dispNames = new Dictionary(); + if (uenum.IsChildOf()) + { + var userEnum = Context.Factory.Cast(uenum); + for (var i = 0; i < uenum.Names.ArrayNum; i++) + { + var dispName = Context.Strings.UEnumGetDisplayNameTextByIndex(userEnum.Ptr, i); + dispNames[i] = $"{name}::{dispName}"; + } + } + + long bigEntryValue = 0; + var bigEntryName = string.Empty; + for (var i = 0; i < uenum.Names.ArrayNum; i++) + { + unsafe + { + var entry = &uenum.Names.AllocatorInstance[i]; + + if (!dispNames.TryGetValue(i, out var entryName)) + { + entryName = entry->Key.ToString(); + } + + var entryValue = entry->Value; + entries[entryName] = entryValue; + + if (bigEntryValue < entryValue) + { + bigEntryValue = entryValue; + bigEntryName = entryName; + } + } + } + + var entryConstant = $"{name}::{name}_MAX"; + if (entries.TryGetValue(entryConstant, out var entryConstantValue) && entryConstantValue == bigEntryValue) + { + entries.Remove(entryConstant); + } + + if (bigEntryValue == 256 && bigEntryName.EndsWith("_MAX")) entries.Remove(bigEntryName); + + var underlyingType = knownType ?? bigEntryValue switch + { + <= byte.MaxValue => "byte", + <= short.MaxValue => "short", + <= int.MaxValue => "int", + <= long.MaxValue => "long", + }; + + var hasNegativeValue = entries.Any(x => x.Value < 0); + if (underlyingType == "byte" && hasNegativeValue) underlyingType = "sbyte"; + if (underlyingType != "byte" && !hasNegativeValue && !underlyingType.StartsWith('u')) underlyingType = $"u{underlyingType}"; + + Context.Registry.Enums[name] = new EnumDefinition(name, underlyingType, entries); + } +} + +public class EnumDefinition(string name, string underlyingType, Dictionary entries) : ISerializable +{ + private string Name = name; + private string UnderlyingType = underlyingType; + private Dictionary Entries = entries; + + public string Serialize(Context context) + { + var sb = new StringBuilder(); + + if (Mod.Config.Mode == DumpFileMode.FilePerType) + { + context.Builtins.AddHeader(sb); + } + + sb.AppendLine($"public enum {Builtins.SanitizeName(Name)} : {UnderlyingType}\n{{"); + foreach (var entry in Entries) + { + sb.AppendLine($" {SanitizeEntryName(entry.Key)} = {entry.Value},"); + } + + sb.AppendLine("}"); + return sb.ToString(); + } + + public ObjectType Type => ObjectType.Enum; + + private static string SanitizeEntryName(string name) => Builtins.SanitizeName(name.Split("::").Last()); +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Function.cs b/UE.Toolkit.DumperMod/Definitions/Function.cs new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Function.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs new file mode 100644 index 0000000..727ff91 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -0,0 +1,333 @@ +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.DumperMod.Definitions; + +public abstract class BasePropertyDefintion(string name, int size, int offset, Func propTypeName) : ISerializable +{ + public string Name { get; set; } = name; + public int Size { get; set; } = size; + public int Offset { get; set; } = offset; + public Func PropTypeName { get; } = propTypeName; + + public abstract string Serialize(Context context); +} + +public class PropertyStructDefinition(string name, int size, int offset, Func propTypeName) + : BasePropertyDefintion(name, size, offset, propTypeName) +{ + public override string Serialize(Context context) => $"[FieldOffset(0x{Offset:X})] public {PropTypeName()} {Builtins.SanitizeName(Name)}; // Size: 0x{Size:X}"; +} + +public class PropertyClassDefinition(string name, int size, int offset, Func propTypeName, + Func accessor, Func mutator) + : BasePropertyDefintion(name, size, offset, propTypeName) +{ + private Func Accessor => accessor; + private Func Mutator => mutator; + + public override string Serialize(Context context) + { + var propName = Builtins.SanitizeName(Name); + var sb = new StringBuilder(); + var typename = PropTypeName(); + sb.AppendLine($"\tpublic unsafe {typename} {propName}"); + sb.AppendLine("\t{"); + sb.AppendLine($"\t\t{Accessor(typename)}"); + sb.AppendLine($"\t\t{Mutator(typename)}"); + sb.AppendLine("\t}"); + return sb.ToString(); + } +} + +public abstract class BasePropertyFactory(Context context) +{ + public List ResolveProperties(IEnumerable propLink, int SuperStructEnd) + { + List props = []; + foreach (var prop in propLink) + { + // Stop when we find a property with an offset lower than the size of the super struct. + // PropertyLink contains a list of fields declared by object in ascending order, then from it's super + // object and so on. + if (prop.Offset_Internal < SuperStructEnd) break; + var newProp = ResolveProperty(prop); + var numSameName = props.Count(x => x.Name.StartsWith(newProp.Name)); // Compare against sanitized name, since that's what props are using. + if (numSameName > 0) newProp.Name = $"{newProp.Name}_{numSameName + 1}"; + props.Add(newProp); + } + + return props; + } + + protected abstract BasePropertyDefintion ResolveProperty(IFProperty prop); + + /// + /// Gets the property type name, such as byte for ByteProperty. + /// This is done lazily since we only have the internal names in + /// when we want their C++/C# equivalent, which isn't known until object registrations finish. + /// + /// + /// C++/C# property type name. + protected Func GetPropTypenameStruct(IFProperty prop) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "BoolProperty": + return () => "bool"; + case "ByteProperty": + var byteProp = context.Factory.Cast(prop); + if (byteProp.Enum != null) + { + var byteEnumName = byteProp.Enum.NamePrivate.ToString(); + new EnumFactory(context, context.Factory.Cast(byteProp.Enum), "byte").Register(); + return () => Builtins.SanitizeName(byteEnumName); + } + + return () => "byte"; + case "Int8Property": + return () => "byte"; + case "Int16Property": + return () => "short"; + case "UInt16Property": + return () => "ushort"; + case "IntProperty": + return () => "int"; + case "UInt32Property": + return () => "uint"; + case "Int64Property": + return () => "long"; + case "UInt64Property": + return () => "ulong"; + case "FloatProperty": + return () => "float"; + case "DoubleProperty": + return () => "double"; + case "NameProperty": + return () => "FName"; + case "StrProperty": + return () => "FString"; + case "TextProperty": + return () => "FText"; + case "DataTableRowHandle": + return () => "FDataTableRowHandle"; + case "DelegateProperty": + return () => "FScriptDelegate"; + case "MulticastInlineDelegateProperty": + case "MulticastSparseDelegateProperty": + return () => "FMulticastScriptDelegate"; + case "WeakObjectProperty": + return () => "FWeakObjectPtr"; + case "ObjectProperty": + var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : $"{objPropType}*"); + case "SoftObjectProperty": + var softObjPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.DisplayName}>" : $"TSoftObjectPtr<{softObjPropType}>"); + case "SoftClassProperty": + var softClassPropType = context.Factory.Cast(prop).MetaClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softClassPropType, out var knownStruct) ? $"TSoftClassPtr<{knownStruct.DisplayName}>" : $"TSoftClassPtr<{softClassPropType}>"); + case "StructProperty": + var structPropType = context.Factory.Cast(prop).Struct.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(structPropType, out var knownStruct) ? knownStruct.DisplayName : structPropType); + case "ClassProperty": + case "ClassPtrProperty": + var classPropClass = context.Factory.Cast(prop).MetaClass; + var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass*"; + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : classPropType); + case "EnumProperty": + var enumProp = context.Factory.Cast(prop); + var enumPropName = enumProp.Enum.NamePrivate.ToString(); + new EnumFactory(context, enumProp.Enum, context.Classes.GetPropertyTypeName(enumProp.UnderlyingProp)).Register(); + return () => Builtins.SanitizeName(enumPropName); + case "MapProperty": + var mapProp = context.Factory.Cast(prop); + var mapPropKeyType = context.Classes.GetPropertyTypeName(mapProp.KeyProp); + var mapPropValueType = context.Classes.GetPropertyTypeName(mapProp.ValueProp); + return () => + { + var isKeyPtr = mapPropKeyType.EndsWith('*') || mapPropKeyType.Contains('<'); // Use nint for pointers and generic types. + var isValuePtr = mapPropValueType.EndsWith('*') || mapPropValueType.Contains('<'); + + context.Registry.Structs.TryGetValue(mapPropKeyType.TrimEnd('*'), out var knownKeyStruct); + context.Registry.Structs.TryGetValue(mapPropValueType.TrimEnd('*'), out var knownValueStruct); + + var keyName = Builtins.SanitizeName(knownKeyStruct?.DisplayName ?? mapPropKeyType); + var valueName = Builtins.SanitizeName(knownValueStruct?.DisplayName ?? mapPropValueType); + + var keyType = isKeyPtr ? $"Ptr<{keyName}>" : keyName; + var valueType = isValuePtr ? $"Ptr<{valueName}>" : valueName; + + return $"TMap<{keyType}, {valueType}>"; + }; + case "InterfaceProperty": + var intPropType = context.Factory.Cast(prop).InterfaceClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(intPropType, out var knownStruct) ? $"TScriptInterface<{knownStruct.DisplayName}>" : $"TScriptInterface<{intPropType}>"); + case "ArrayProperty": + var arrayPropType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).Inner); + return () => + { + var isPtrType = arrayPropType.EndsWith('*') || arrayPropType.Contains('<'); // Use nint for pointers and generic types. + context.Registry.Structs.TryGetValue(arrayPropType.TrimEnd('*'), out var knownStruct); + var arrTypeSanitized = Builtins.SanitizeName(knownStruct?.DisplayName ?? arrayPropType); + return isPtrType ? + $"TArray>" : + $"TArray<{arrTypeSanitized}>"; + }; + case "SetProperty": + var setPropType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).ElementProp); + return () => + { + var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; + context.Registry.Structs.TryGetValue(setPropType.TrimEnd('*'), out var knownStruct); + return isPtrType + ? $"TSet /* TSet<{setPropType}> */" + : $"TSet<{Builtins.SanitizeName(knownStruct?.DisplayName ?? setPropType)}>"; + }; + case "OptionalProperty": + var optionalType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).ValueProperty); + return () => + { + if (context.Registry.Structs.TryGetValue(optionalType, out var knownOptType)) + optionalType = knownOptType.DisplayName; + + return $"TOptional<{Builtins.SanitizeName(optionalType)}>"; + }; + case "FieldPathProperty": + return () => "FFieldPath"; + case "LazyObjectProperty": + var lazyObjType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => + { + if (context.Registry.Structs.TryGetValue(lazyObjType, out var knownLazyType)) + lazyObjType = knownLazyType.DisplayName; + + return $"TLazyObjectPtr<{Builtins.SanitizeName(lazyObjType)}>"; + }; + case "Utf8StrProperty": + return () => "FUtf8String"; + case "AnsiStrProperty": + return () => "FAnsiString"; + default: + Log.Warning($"Unknown Property: {className}"); + return () => className; + } + } + + protected (string Name, int Size, int Offset, string ClassName) GetBaseInfo(IFProperty prop) + { + var name = Builtins.SanitizeName(prop.NamePrivate); + var size = prop.ElementSize; + var offset = prop.Offset_Internal; + var className = prop.ClassPrivate.Name; + return (name, size, offset, className); + } +} + +public class PropertyStructFactory(Context context) : BasePropertyFactory(context) +{ + + protected override BasePropertyDefintion ResolveProperty(IFProperty prop) + { + var (name, size, offset, _) = GetBaseInfo(prop); + if (name is "bool" or "float") name += '_'; + return new PropertyStructDefinition(name, size, offset, GetPropTypenameStruct(prop)); + } +} + +public class PropertyStructReprFactory(Context context) : BasePropertyFactory(context) +{ + private Func GetPropTypenameStructRepr(IFProperty prop) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "ObjectProperty": + var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}_Repr*" : $"{objPropType}*"); + default: + return GetPropTypenameStruct(prop); + } + } + + protected override BasePropertyDefintion ResolveProperty(IFProperty prop) + { + var (name, size, offset, _) = GetBaseInfo(prop); + if (name is "bool" or "float") name += '_'; + return new PropertyStructDefinition(name, size, offset, GetPropTypenameStructRepr(prop)); + } +} + +public class PropertyClassFactory(Context context) : BasePropertyFactory(context) +{ + private Func GetPropAccessor(IFProperty prop, Func getTypeName) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" + or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" + or "NameProperty" or "StructProperty": + return propName => $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; + case "BoolProperty": + var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); + return (BoolProp.FieldMask == byte.MaxValue) switch + { + true => propName => $"get => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);", + false => propName => $"get => (*(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) & {BoolProp.FieldMask}) == 0;" + }; + case "ObjectProperty": + return propName => + $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"])));"; + default: + return _ => $"get => throw new NotSupportedException(\"!! GET TODO {className} !!\");"; + } + } + + private Func GetPropMutator(IFProperty prop, Func getTypeName) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" + or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" + or "NameProperty" or "StructProperty": + return propName => $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;"; + case "BoolProperty": + var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); + return (BoolProp.FieldMask == byte.MaxValue) switch + { + true => propName => $"set => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;", + false => propName => $"set => *(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) ^= (byte)(Convert.ToByte({propName} != value) * {BoolProp.FieldMask});" + }; + case "ObjectProperty": + return propName => $"set => *(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value.Inner.Ptr;"; + default: + return _ => $"set => throw new NotSupportedException(\"!! SET TODO {className} !!\");"; + } + } + + private Func GetPropTypenameClass(IFProperty prop) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "ObjectProperty": + var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}*"); + default: + return GetPropTypenameStruct(prop); + } + } + + protected override BasePropertyDefintion ResolveProperty(IFProperty prop) + { + var (name, size, offset, _) = GetBaseInfo(prop); + if (name is "bool" or "float") name += '_'; + var propTypename = GetPropTypenameClass(prop); + var getAccessor = GetPropAccessor(prop, propTypename); + var getMutator = GetPropMutator(prop, propTypename); + return new PropertyClassDefinition(name, size, offset, propTypename, getAccessor, getMutator); + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Registry.cs b/UE.Toolkit.DumperMod/Definitions/Registry.cs new file mode 100644 index 0000000..2119d9d --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Registry.cs @@ -0,0 +1,138 @@ +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.DumperMod.Definitions; + +public enum ObjectType +{ + Class, // inherits from UClass + Struct, // inherits from UScriptStruct + Enum, // inherits from UEnum + // Interface, // inherits from UInterface + Other, +} + +public class Registry(Context context) +{ + private Context Context = context; + + public Dictionary Structs = []; + public Dictionary Enums = []; + private Dictionary Types = []; + + private ObjectType FromTypeName(IUClass uclass) => uclass.NamePrivate.ToString() switch + { + "Class" => ObjectType.Class, + "ScriptStruct" => ObjectType.Struct, + "Enum" => ObjectType.Enum, + // "Interface" => ObjectType.Interface, + _ => ObjectType.Other + }; + + private ObjectType RetrieveObjectType(IUObject uobject) + { + string ClassName = uobject.ClassPrivate.NamePrivate.ToString(); + IUClass? CurrentClass = uobject.ClassPrivate; + while (CurrentClass != null) + { + var CurrentType = FromTypeName(CurrentClass); + if (CurrentType != ObjectType.Other) + { + Types[ClassName] = CurrentType; + return CurrentType; + } + CurrentClass = CurrentClass.GetSuperClass(); + } + Types[ClassName] = ObjectType.Other; + return ObjectType.Other; + } + + public void Register() + { + var objectArray = Context.Objects.GUObjectArray; + for (var i = 0; i < objectArray.NumElements; i++) + { + var obj = objectArray.IndexToObject(i); + if (obj == null) continue; + + /* + var typeName = obj.ClassPrivate.NamePrivate.ToString(); + var moduleName = GetModuleNameForPackage(obj.GetOutermost()); + var fileBaseName = obj.ClassPrivate.NamePrivate.ToString(); + Log.Debug($"Object {obj.NamePrivate} | Filename {fileBaseName} | Module {moduleName}"); + */ + var typeName = obj.ClassPrivate.NamePrivate.ToString(); + IObjectFactory? factory = (Types.TryGetValue(typeName, out var objectType) ? objectType : RetrieveObjectType(obj)) switch + { + ObjectType.Class => new ClassFactory(Context, Context.Factory.Cast(obj)), + ObjectType.Struct => new StructFactory(Context, Context.Factory.Cast(obj)), + ObjectType.Enum => new EnumFactory(Context, Context.Factory.Cast(obj)), + _ => null + }; + factory?.Register(); + /* + if (obj.IsChildOf()) + { + var uclass = Context.Factory.Cast(obj); + if (obj.IsChildOf()) + { + // TODO: + var interfaceName = obj.NamePrivate.ToString(); + Context.Registry.Structs[interfaceName] = new(interfaceName, interfaceName, 0, 0, [], null); + Log.Debug($"Interface: {interfaceName}"); + } + else + { + Context.ClassFactory.Register(uclass); + } + } + else if (obj.IsChildOf()) + { + Context.StructFactory.Register(Context.Factory.Cast(obj)); + } + else if (obj.IsChildOf()) + { + Context.EnumFactory.Register(Context.Factory.Cast(obj)); + } + */ + } + } + + private static string GetModuleNameForPackage(IUObject package) + { + if (package.OuterPrivate != null) + { + throw new("Encountered a package with an outer object set"); + } + + var packageName = package.NamePrivate.ToString(); + if (!packageName.StartsWith("/Script/")) + { + return string.Empty; + } + + return packageName["/Script/".Length..]; + } + + private static unsafe string GetHeaderNameForObject(IUObject obj) + { + string? headerName = null; + UObjectBase* finalObj; + + if (obj.IsA() || obj.IsA()) + { + headerName = obj.NamePrivate.ToString(); + } + else if (obj.IsA()) + { + headerName = obj.NamePrivate.ToString(); + } + else + { + // TODO: UFunction stuff; + } + + // TODO: Other stuff? + return headerName; + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Struct.cs b/UE.Toolkit.DumperMod/Definitions/Struct.cs new file mode 100644 index 0000000..5826da0 --- /dev/null +++ b/UE.Toolkit.DumperMod/Definitions/Struct.cs @@ -0,0 +1,97 @@ +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.DumperMod.Definitions; + +public class StructFactory(Context context, IUScriptStruct scriptStruct) : BaseObjectFactory(context) +{ + public override void Register() + { + var structName = scriptStruct.NamePrivate.ToString(); + var structNativeName = scriptStruct.GetNativeName(); + var size = scriptStruct.PropertiesSize; + var alignment = scriptStruct.MinAlignment; + var super = scriptStruct.SuperStruct; + var superSize = super?.PropertiesSize ?? 0; + var superName = super?.NamePrivate.ToString(); + //var superNativeName = super != null ? GetNativeStructName((UScriptStruct*)super) : null; + var props = new PropertyStructFactory(Context).ResolveProperties(scriptStruct.PropertyLink, superSize); + Context.Registry.Structs[structName] = new StructDefinition(structName, structNativeName, size, alignment, props, superName); + } +} + +public abstract class BaseStructDefinition( + string internalName, + string displayName, + int size, + int alignment, + List properties, + string? superInternalName) : ISerializable +{ + public string InternalName { get; } = internalName; + public string DisplayName { get; } = displayName; + public int Size { get; } = size; + public int Alignment { get; } = alignment; + public List Properties { get; } = properties; + public string? SuperInternalName { get; } = superInternalName; + + public abstract string Serialize(Context context); +} + +public class StructDefinition( + string internalName, + string displayName, + int size, + int alignment, + List propClass, + string? superInternalName) : BaseStructDefinition( + internalName, displayName, size, alignment, propClass, superInternalName) +{ + + protected void WriteStructBase(StringBuilder sb, string StructName, string? SuperName, int SuperSize, Context context) + { + sb.AppendLine($"[StructLayout(LayoutKind.Explicit, Pack = {Alignment}, Size = 0x{Size:X})]"); + sb.AppendLine($"public unsafe struct {StructName}\n{{"); + if (SuperName != null) + { + sb.AppendLine($"\t[FieldOffset(0x0)] public {SuperName} Super; // Size: 0x{SuperSize:X}"); + } + foreach (var prop in Properties) + sb.AppendLine($"\t{prop.Serialize(context)}"); + } + + protected (string?, int) GetSuperInfo(Context context) + { + string? superName = null; + var superSize = 0; + if (SuperInternalName != null) + { + if (context.Registry.Structs.TryGetValue(SuperInternalName, out var super)) + { + superName = Builtins.SanitizeName(super.DisplayName); + superSize = super.Size; + } + else + { + Log.Warning($"Failed to get super: {SuperInternalName}"); + } + } + return (superName, superSize); + } + + public override string Serialize(Context context) + { + var sb = new StringBuilder(); + + if (Mod.Config.Mode == DumpFileMode.FilePerType) context.Builtins.AddHeader(sb); + + var structName = Builtins.SanitizeName(DisplayName); + + var (superName, superSize) = GetSuperInfo(context); + WriteStructBase(sb, structName, superName, superSize, context); + sb.AppendLine("}"); + return sb.ToString(); + } + + public virtual ObjectType Type => ObjectType.Struct; +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Dumper.cs b/UE.Toolkit.DumperMod/Dumper.cs index 3d3039f..e122a82 100644 --- a/UE.Toolkit.DumperMod/Dumper.cs +++ b/UE.Toolkit.DumperMod/Dumper.cs @@ -4,6 +4,7 @@ using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; using UE.Toolkit.Interfaces; +using UnrealEssentials.Interfaces; // ReSharper disable InconsistentNaming @@ -87,14 +88,17 @@ public string GetCsharpText() private readonly IUnrealStrings _strs; private readonly IUnrealFactory _factory; private readonly IUnrealClasses _classes; + private readonly IUnrealEssentials _essentials; - public Dumper(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, IUnrealClasses classes, string dumpDir) + public Dumper(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, IUnrealClasses classes, + string dumpDir, IUnrealEssentials essentials) { _uobjs = uobjs; _strs = strs; _dumpDir = dumpDir; _factory = factory; _classes = classes; + _essentials = essentials; if (Directory.Exists(dumpDir)) Directory.Delete(dumpDir, true); Directory.CreateDirectory(dumpDir); diff --git a/UE.Toolkit.DumperMod/DumperEx.cs b/UE.Toolkit.DumperMod/DumperEx.cs new file mode 100644 index 0000000..99e5a71 --- /dev/null +++ b/UE.Toolkit.DumperMod/DumperEx.cs @@ -0,0 +1,83 @@ +using System.Diagnostics; +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories; +using UE.Toolkit.DumperMod.Definitions; +using UE.Toolkit.Interfaces; +using UnrealEssentials.Interfaces; + +namespace UE.Toolkit.DumperMod; + +public class DumperEx( + IUnrealFactory factory, + IUnrealObjects uobjs, + IUnrealStrings strs, + IUnrealClasses classes, + string dumpDir, + IUnrealEssentials essentials) +{ + private Context Context = new(factory, uobjs, strs, classes, dumpDir, essentials); + + public void DumpObjects() + { + Log.Information("Dumping objects..."); + + var sw = new Stopwatch(); + sw.Start(); + Context.Registry.Register(); + + StringBuilder? sb = null; + if (Mod.Config.Mode == DumpFileMode.SingleFile) + { + sb = new(); + Context.Builtins.AddHeader(sb); + Context.Builtins.AddBaseObjectDefinition(sb); + } + else + { + var outputFile = Path.Join(Context.DumpDirectory, "UObjectImpl.cs"); + var implWriter = new StringBuilder(); + Context.Builtins.AddHeader(implWriter); + Context.Builtins.AddBaseObjectDefinition(implWriter); + File.WriteAllText(outputFile, implWriter.ToString()); + } + + var numDumped = 0; + + foreach (var (_, Definition) in Context.Registry.Structs) + { + if (Mod.Config.Mode == DumpFileMode.FilePerType) + { + var outputFile = Path.Join(Context.DumpDirectory, $"{Definition.DisplayName}.cs"); + File.WriteAllText(outputFile, Definition.Serialize(Context)); + } + else sb?.AppendLine(Definition.Serialize(Context)); + numDumped++; + } + + foreach (var (Name, Definition) in Context.Registry.Enums) + { + if (Mod.Config.Mode == DumpFileMode.FilePerType) + { + var outputFile = Path.Join(Context.DumpDirectory, $"{Name}"); + File.WriteAllText(outputFile, Definition.Serialize(Context)); + } + else sb?.AppendLine(Definition.Serialize(Context)); + numDumped++; + } + + sw.Stop(); + if (Mod.Config.Mode == DumpFileMode.SingleFile) + { + var singleFileOutput = string.IsNullOrEmpty(Mod.Config.SingleFileOutputName) + ? Path.Join(Context.DumpDirectory, "Types.cs") + : Path.Join(Context.DumpDirectory, $"{Mod.Config.SingleFileOutputName.Replace(".cs", string.Empty)}.cs"); + + File.WriteAllText(singleFileOutput, sb!.ToString()); + Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput File: {singleFileOutput}"); + } + else + { + Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput Folder: {Context.DumpDirectory}"); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Mod.cs b/UE.Toolkit.DumperMod/Mod.cs index 926b6d9..5a22aa2 100644 --- a/UE.Toolkit.DumperMod/Mod.cs +++ b/UE.Toolkit.DumperMod/Mod.cs @@ -6,6 +6,7 @@ using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.DumperMod.Template; using UE.Toolkit.Interfaces; +using UnrealEssentials.Interfaces; namespace UE.Toolkit.DumperMod; @@ -18,7 +19,7 @@ public class Mod : ModBase public static Config Config = null!; private readonly IModConfig _modConfig; - private readonly Dumper _dumper; + private readonly DumperEx _dumper; public Mod(ModContext context) { @@ -33,6 +34,12 @@ public Mod(ModContext context) #endif Project.Initialize(_modConfig, _modLoader, _log, true); Log.LogLevel = Config.LogLevel; + + if (!_modLoader.GetController().TryGetTarget(out var _essentials)) + { + throw new Exception( + "Unreal Essentials is missing! Download the latest version from https://github.com/AnimatedSwine37/UnrealEssentials/releases"); + } _modLoader.GetController().TryGetTarget(out var objs); _modLoader.GetController().TryGetTarget(out var strs); @@ -41,7 +48,7 @@ public Mod(ModContext context) var dumpDir = Path.Join(_modLoader.GetDirectoryForModId(_modConfig.ModId), "dump", _modLoader.GetAppConfig().AppId); - _dumper = new(factory!, objs!, strs!, classes!, dumpDir); + _dumper = new(factory!, objs!, strs!, classes!, dumpDir, _essentials); } #region Standard Overrides diff --git a/UE.Toolkit.DumperMod/ModConfig.json b/UE.Toolkit.DumperMod/ModConfig.json index 129b54a..1789cfb 100644 --- a/UE.Toolkit.DumperMod/ModConfig.json +++ b/UE.Toolkit.DumperMod/ModConfig.json @@ -1,7 +1,7 @@ { "ModId": "UE.Toolkit.DumperMod", "ModName": "UE Toolkit: Dumper", - "ModAuthor": "RyoTune", + "ModAuthor": "RyoTune, Rirurin", "ModVersion": "1.10.0", "ModDescription": "Unreal Engine object dumper to C# types.", "ModDll": "UE.Toolkit.DumperMod.dll", @@ -44,6 +44,15 @@ "AssetFileName": "Mod.zip" }, "ReleaseMetadataName": "UE.Toolkit.Reloaded.ReleaseMetadata.json" + }, + "UnrealEssentials": { + "Config": { + "UserName": "AnimatedSwine37", + "RepositoryName": "UnrealEssentials", + "UseReleaseTag": false, + "AssetFileName": "Mod.zip" + }, + "ReleaseMetadataName": "UnrealEssentials.ReleaseMetadata.json" } } }, @@ -58,7 +67,8 @@ "ModDependencies": [ "reloaded.sharedlib.hooks", "Reloaded.Memory.SigScan.ReloadedII", - "UE.Toolkit.Reloaded" + "UE.Toolkit.Reloaded", + "UnrealEssentials" ], "OptionalDependencies": [], "SupportedAppId": [ diff --git a/UE.Toolkit.DumperMod/UE.Toolkit.DumperMod.csproj b/UE.Toolkit.DumperMod/UE.Toolkit.DumperMod.csproj index 9547de9..03cbc41 100644 --- a/UE.Toolkit.DumperMod/UE.Toolkit.DumperMod.csproj +++ b/UE.Toolkit.DumperMod/UE.Toolkit.DumperMod.csproj @@ -1,10 +1,10 @@  - net8.0-windows + net9.0 false true - 12.0 + 13.0 enable True $(RELOADEDIIMODS)/UE.Toolkit.DumperMod @@ -47,6 +47,7 @@ + From c12fc1f668de8a60af40c7c864a86a4d56819730 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Thu, 23 Jul 2026 10:48:55 +0800 Subject: [PATCH 04/10] Dumper: Improved type generation for classes --- README.md | 2 +- .../Types/Interfaces/IFunctionParam.cs | 69 +- .../Interfaces/ITypeReflectionInternal.cs | 21 + .../Unreal/Common/FunctionParam/BoolParam.cs | 56 ++ .../Common/FunctionParam/StringParam.cs | 22 + .../Common/FunctionParam/StructParam.cs | 41 ++ .../Unreal/Common/FunctionParam/TextParam.cs | 43 ++ .../Unreal/Factories/UE5_2_1/UnrealFactory.cs | 31 +- UE.Toolkit.DumperMod/Config.cs | 14 + UE.Toolkit.DumperMod/Definitions/Base.cs | 3 +- UE.Toolkit.DumperMod/Definitions/Builtins.cs | 18 +- UE.Toolkit.DumperMod/Definitions/Class.cs | 49 +- UE.Toolkit.DumperMod/Definitions/Enum.cs | 3 +- UE.Toolkit.DumperMod/Definitions/Function.cs | 130 +++- UE.Toolkit.DumperMod/Definitions/Property.cs | 94 ++- UE.Toolkit.DumperMod/Definitions/Registry.cs | 134 ++-- UE.Toolkit.DumperMod/Definitions/Struct.cs | 12 +- UE.Toolkit.DumperMod/Dumper.cs | 625 +----------------- UE.Toolkit.DumperMod/DumperEx.cs | 83 --- UE.Toolkit.DumperMod/Mod.cs | 2 +- UE.Toolkit.Interfaces/ITypeReflection.cs | 23 +- UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs | 21 +- 22 files changed, 591 insertions(+), 905 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Interfaces/ITypeReflectionInternal.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/BoolParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StructParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/TextParam.cs delete mode 100644 UE.Toolkit.DumperMod/DumperEx.cs diff --git a/README.md b/README.md index f8c7412..c64308a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ | Object Logging |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Object Editing |✅|❔|❔|✅|❔|✅|❔|❔|❔ | `FMemory` Functions |✅|✅|✅|✅|✅|✅|✅|✅|✅ -| Dumper |✅|✅|✅|✅|️️️️️️✅|✅|✅|✅️|️️️️✅️ +| Dumper |⚠️|⚠️|⚠️|⚠️|⚠️️️️️️|⚠️|⚠️|⚠️️|⚠️️️️️️ | Property Editing (Object XML) |✅|❔|❔|✅|❔|✅|❔|❔|❔ | Add List Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ | Add Map Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ diff --git a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs index ff40405..cb7f56c 100644 --- a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs +++ b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs @@ -1,4 +1,9 @@ -namespace UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; +using UE.Toolkit.Core.Types.Unreal.Factories; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Interfaces; public interface IFunctionParam { @@ -24,7 +29,7 @@ public abstract class FunctionParamCopyable(Ptr rvalue, string propertyTyp public unsafe void Read(nint Destination) => Copy((T*)Destination, RValue.Value); - private bool IsDisposed; + protected bool IsDisposed; public void Dispose() { @@ -37,11 +42,7 @@ protected virtual void Dispose(bool disposing) if (!IsDisposed) { if (disposing) { } - - if (Memory != null) - { - unsafe { Memory.Free((nint)RValue.Value); } - } + unsafe { Memory?.Free((nint)RValue.Value); } IsDisposed = true; } } @@ -54,4 +55,58 @@ public enum ProcessEventResult Success, CouldNotFindFunction, ParameterTypeMismatch, +} + +public static class FunctionParamFactory +{ + + private static Dictionary PropertyNameToParamName = []; + + private static IFunctionParam CreateStructParam(IFStructProperty property, IUnrealMemoryInternal? Memory) + => new StructParam(Memory?.Malloc(property.ElementSize) ?? nint.Zero, property.ElementSize, Memory); + + private static unsafe IFunctionParam CreateBoolParam(IFBoolProperty property, IUnrealMemoryInternal? Memory) + => new BoolParam(new((bool*)(Memory?.Malloc(sizeof(bool)) ?? nint.Zero)), property.FieldMask, Memory); + + public static unsafe IFunctionParam CreateParam(IFProperty property, IUnrealFactory factory, + ITypeReflectionInternal reflection, IUnrealMemoryInternal? Memory) + { + return property.ClassPrivate.Name switch + { + "BoolProperty" => CreateBoolParam(factory.CreateFBoolProperty(property.Ptr), Memory), + "Int8Property" => new Int8Param(new((byte*)(Memory?.Malloc(sizeof(byte)) ?? nint.Zero)), Memory), + "Int16Property" => new Int16Param(new((short*)(Memory?.Malloc(sizeof(short)) ?? nint.Zero)), Memory), + "Int32Property" => new Int32Param(new((int*)(Memory?.Malloc(sizeof(int)) ?? nint.Zero)), Memory), + "IntProperty" => new IntParam(new((int*)(Memory?.Malloc(sizeof(int)) ?? nint.Zero)), Memory), + "Int64Property" => new Int64Param(new((long*)(Memory?.Malloc(sizeof(long)) ?? nint.Zero)), Memory), + "UInt16Property" => new UInt16Param(new((ushort*)(Memory?.Malloc(sizeof(ushort)) ?? nint.Zero)), Memory), + "UInt32Property" => new UInt32Param(new((uint*)(Memory?.Malloc(sizeof(uint)) ?? nint.Zero)), Memory), + "UInt64Property" => new UInt64Param(new((ulong*)(Memory?.Malloc(sizeof(ulong)) ?? nint.Zero)), Memory), + "FloatProperty" => new FloatParam(new((float*)(Memory?.Malloc(sizeof(float)) ?? nint.Zero)), Memory), + "DoubleProperty" => new DoubleParam(new((double*)(Memory?.Malloc(sizeof(double)) ?? nint.Zero)), Memory), + "NameProperty" => new NameParam(new((FName*)(Memory?.Malloc(sizeof(FName)) ?? nint.Zero)), Memory), + "StrProperty" => new StringParam(new((FString*)(Memory?.Malloc(sizeof(FString)) ?? nint.Zero)), Memory), + "StructProperty" => CreateStructParam(factory.CreateFStructProperty(property.Ptr), Memory), + "TextProperty" => new TextParam(Memory?.Malloc(reflection.GetFTextSize()) ?? nint.Zero, reflection.GetFTextSize(), Memory), + "ObjectProperty" or "ClassProperty" or "ClassPtrProperty" + => new ObjectParam(new((nint*)(Memory?.Malloc(sizeof(nint)) ?? nint.Zero)), Memory), + _ => throw new NotSupportedException($"CreateParam with property {property.ClassPrivate.Name}") + }; + } + + public static string? GetParamNameFromProperty(IFProperty property, IUnrealFactory factory, ITypeReflectionInternal reflection) + { + try + { + var PropertyName = property.ClassPrivate.Name; + if (PropertyNameToParamName.TryGetValue(PropertyName, out var ParamName)) return ParamName; + var BlankParam = CreateParam(property, factory, reflection, null); + PropertyNameToParamName[PropertyName] = BlankParam.GetType().Name; + return PropertyNameToParamName[PropertyName]; + } + catch (NotSupportedException ex) + { + return null; + } + } } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Interfaces/ITypeReflectionInternal.cs b/UE.Toolkit.Core/Types/Interfaces/ITypeReflectionInternal.cs new file mode 100644 index 0000000..3dc4a13 --- /dev/null +++ b/UE.Toolkit.Core/Types/Interfaces/ITypeReflectionInternal.cs @@ -0,0 +1,21 @@ +namespace UE.Toolkit.Core.Types.Interfaces; + +public interface ITypeReflectionInternal +{ + #region FText + + /// + /// Get the FText type used by the currently running version of the engine. + /// UE 5.4 and later have a significantly different FText implement compared to earlier versions. + /// + /// Type information for the FText. + Type GetFText(); + /// + /// Get the size of the FText type used by the currently running version of the engine. + /// UE 5.4 and later have a significantly different FText implement compared to earlier versions. + /// + /// Size of FText. + int GetFTextSize(); + + #endregion +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/BoolParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/BoolParam.cs new file mode 100644 index 0000000..30ce7cb --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/BoolParam.cs @@ -0,0 +1,56 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class BoolParam(Ptr rvalue, int fieldMask, IUnrealMemoryInternal? memory = null) : IFunctionParam, IDisposable +{ + public unsafe bool Value => *RValue.Value; + + protected Ptr RValue => rvalue; + + protected IUnrealMemoryInternal? Memory => memory; + + public string PropertyType => "BoolProperty"; + + public int FieldMask => fieldMask; + + private static unsafe void CopyByte(byte* source, byte* destination) => *destination = *source; + + private unsafe bool IsStateDifferent(nint Destination) + { + var isDestTrue = (*(byte*)Destination & Convert.ToByte(FieldMask)) != 0; + return isDestTrue != Value; + } + + public unsafe void Write(nint Destination) + { + if (FieldMask == byte.MaxValue) CopyByte((byte*)RValue.Value, (byte*)Destination); + else *(byte*)Destination ^= (byte)(Convert.ToByte(IsStateDifferent(Destination)) * FieldMask); + } + + public unsafe void Read(nint Destination) + { + if (FieldMask == byte.MaxValue) CopyByte((byte*)Destination, (byte*)RValue.Value); + else *RValue.Value = IsStateDifferent(Destination); + } + + protected bool IsDisposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + unsafe { Memory?.Free((nint)RValue.Value); } + IsDisposed = true; + } + } + + ~BoolParam() => Dispose(false); +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs new file mode 100644 index 0000000..bd59c12 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs @@ -0,0 +1,22 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class StringParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "StrProperty", memory) +{ + protected override void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + + unsafe + { + // Memory?.Free((nint)RValue.Value->Data.AllocatorInstance); + Memory?.Free((nint)RValue.Value); + } + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StructParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StructParam.cs new file mode 100644 index 0000000..e085f8d --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StructParam.cs @@ -0,0 +1,41 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class StructParam(nint ptr, int size, IUnrealMemoryInternal? memory = null) : IFunctionParam, IDisposable +{ + public string PropertyType => "StructProperty"; + + private int Size => size; + + private nint Ptr => ptr; + + private IUnrealMemoryInternal? Memory => memory; + + private unsafe void Copy(nint source, nint destination) + => NativeMemory.Copy((void*)source, (void*)destination, (nuint)Size); + + public void Write(nint Destination) => Copy(Ptr, Destination); + public void Read(nint Destination) => Copy(Destination, Ptr); + + private bool IsDisposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + Memory?.Free(Ptr); + IsDisposed = true; + } + } + + ~StructParam() => Dispose(false); +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/TextParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/TextParam.cs new file mode 100644 index 0000000..bae8d77 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/TextParam.cs @@ -0,0 +1,43 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class TextParam(nint value, int ftextSize, IUnrealMemoryInternal? memory = null) + : IFunctionParam, IDisposable +{ + public unsafe nint Value => value; + + protected IUnrealMemoryInternal? Memory => memory; + + public string PropertyType => "TextProperty"; + + private int FTextSize => ftextSize; + + private unsafe void Copy(nint source, nint destination) => + NativeMemory.Copy((void*)source, (void*)destination, (nuint)FTextSize); + + public void Write(nint Destination) => Copy(Value, Destination); + + public void Read(nint Destination) => Copy(Destination, Value); + + protected bool IsDisposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + Memory?.Free(Value); + IsDisposed = true; + } + } + + ~TextParam() => Dispose(false); +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs index 2322f16..02d35d2 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs @@ -22,6 +22,8 @@ using FPropertyParamsBase = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FPropertyParamsBase; using FStructParams = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FStructParams; +using FField = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FField; + namespace UE.Toolkit.Core.Types.Unreal.Factories.UE5_2_1; public class UnrealFactory : BaseUnrealFactory @@ -83,7 +85,7 @@ public override nint SizeOf() public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); - public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); + public override IFField CreateFField(nint ptr) => new FField_UE5_2_1(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new UE4_27_2.FFieldVariantUE4_27_2(ptr, this); public override IFStructParams CreateFStructParams(nint ptr) => new FStructParams_UE5_2_1(ptr, this); public override IFPropertyParams CreateFPropertyParams(nint ptr) => new FPropertyParams_UE5_2_1(ptr, this); @@ -215,4 +217,31 @@ public unsafe class FGenericPropertyParams_UE5_2_1(nint ptr, IUnrealFactory fact public int ArrayDim => _self->Super.ArrayDim; public int Offset => _self->Super.Offset; +} + +public unsafe class FField_UE5_2_1(nint ptr, IUnrealFactory factory) + : IFField +{ + private readonly FField* _self = (FField*)ptr; + protected readonly IUnrealFactory _factory = factory; + + public nint Ptr => ptr; + public nint VTable => _self->_vtable; + public IFFieldClass ClassPrivate => _factory.CreateFFieldClass((nint)_self->class_private); + public IFFieldVariant Owner => factory.CreateFFieldVariant((nint)(&_self->owner)); + public IFField? Next => _self->next != null ? _factory.CreateFField((nint)_self->next) : null; + public string NamePrivate => _self->name_private.ToString(); + public EObjectFlags FlagsPrivate => _self->flags_private; + + public void SetOwnerUObject(IUObject owner) + { + _self->owner.Object = (Unreal.UE4_27_2.UObjectBase*)owner.Ptr; + _self->owner.bIsUObject = true; + } + + public void SetOwnerFField(IFField owner) + { + _self->owner.Field = (FField*)owner.Ptr; + _self->owner.bIsUObject = false; + } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Config.cs b/UE.Toolkit.DumperMod/Config.cs index 81007cf..0a92b67 100644 --- a/UE.Toolkit.DumperMod/Config.cs +++ b/UE.Toolkit.DumperMod/Config.cs @@ -11,8 +11,14 @@ public class Config : Configurable public LogLevel LogLevel { get; set; } = LogLevel.Information; [DisplayName("Dump Mode")] + [Description("Defines how the dump will be saved onto the file system")] [DefaultValue(DumpFileMode.SingleFile)] public DumpFileMode Mode { get; set; } = DumpFileMode.SingleFile; + + [DisplayName("Dump Schema")] + [Description("Defines the format for the type dump")] + [DefaultValue(DumpSchema.Dynamic)] + public DumpSchema Schema { get; set; } = DumpSchema.Dynamic; [DisplayName("File Namespace")] [DefaultValue("")] @@ -39,6 +45,14 @@ public enum DumpFileMode FilePerModule, } +public enum DumpSchema +{ + [Display(Name = "Structs Only")] + Static, + [Display(Name = "Structs and Classes")] + Dynamic, +} + /// /// Allows you to override certain aspects of the configuration creation process (e.g. create multiple configurations). /// Override elements in for finer control. diff --git a/UE.Toolkit.DumperMod/Definitions/Base.cs b/UE.Toolkit.DumperMod/Definitions/Base.cs index bd94de3..442a550 100644 --- a/UE.Toolkit.DumperMod/Definitions/Base.cs +++ b/UE.Toolkit.DumperMod/Definitions/Base.cs @@ -10,9 +10,10 @@ public interface ISerializable string Serialize(Context context); } -public abstract class BaseObjectFactory(Context context) : IObjectFactory +public abstract class BaseObjectFactory(Context context, ObjectType objectType) : IObjectFactory { protected Context Context = context; + protected ObjectType ObjectType = objectType; public abstract void Register(); } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs index 63a43ff..ed15896 100644 --- a/UE.Toolkit.DumperMod/Definitions/Builtins.cs +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -1,4 +1,5 @@ -using System.Text; +using System.Runtime.CompilerServices; +using System.Text; using UnrealEssentials.Interfaces; namespace UE.Toolkit.DumperMod.Definitions; @@ -9,11 +10,17 @@ public class Builtins(IUnrealEssentials essentials) private List GetDefaultUsings() { - List Usings = [ + List Usings = []; + if (Mod.Config.Schema == DumpSchema.Dynamic) + Usings.Add("System.Runtime.CompilerServices"); + Usings.AddRange([ "System.Runtime.InteropServices", "UE.Toolkit.Core.Types", "UE.Toolkit.Core.Types.Unreal.UE5_4_4", - ]; + "UE.Toolkit.Core.Types.Unreal.Factories", + "UE.Toolkit.Core.Types.Unreal.Factories.Interfaces", + "UE.Toolkit.Core.Types.Unreal.Common.FunctionParam", + ]); var VerParts = Essentials.GetEngineVersion().Split("-")[^1].Split("."); // FText definition is different for versions below UE 5.4 if (int.Parse(VerParts[0]) < 5 || int.Parse(VerParts[1]) < 4) @@ -62,11 +69,11 @@ public interface ITypeRepr where TRepr: unmanaged public abstract class ObjectImpl { public IUObject Inner { get; } - protected static Dictionary? FieldOffsets; + protected static Dictionary FieldOffsets; protected ObjectImpl(IUObject inner) { Inner = inner; - FieldOffsets ??= Inner.ClassPrivate.PropertyLink + FieldOffsets = Inner.ClassPrivate.PropertyLink .Select(x => (x.NamePrivate, x.Offset_Internal)).ToDictionary(); } } @@ -91,6 +98,7 @@ public static string SanitizeName(string name) name = name.Replace(')', '_'); name = name.Replace('[', '_'); name = name.Replace(']', '_'); + //name = name.Replace('>', '_'); if (name == "object") name = "_object"; if (char.IsDigit(name[0])) diff --git a/UE.Toolkit.DumperMod/Definitions/Class.cs b/UE.Toolkit.DumperMod/Definitions/Class.cs index 31cbd98..bd0bb0a 100644 --- a/UE.Toolkit.DumperMod/Definitions/Class.cs +++ b/UE.Toolkit.DumperMod/Definitions/Class.cs @@ -1,36 +1,50 @@ using System.Text; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; namespace UE.Toolkit.DumperMod.Definitions; -public class ClassFactory(Context context, IUClass uclass) : BaseObjectFactory(context) +public class ClassFactory(Context context, ObjectType objectType, IUClass uclass) : BaseObjectFactory(context, objectType) { public override void Register() { - var className = uclass.NamePrivate.ToString(); - var classNativeName = uclass.GetNativeName(); + var innerName = uclass.NamePrivate.ToString(); + var displayName = uclass.GetNativeName(); var size = uclass.PropertiesSize; var alignment = uclass.MinAlignment; // TODO: Flag stuff? - var super = uclass.GetSuperClass(); var superSize = super?.PropertiesSize ?? 0; var superName = super?.NamePrivate.ToString(); - //var superNativeName = super != null ? GetNativeClassName(super) : "UObjectBaseUtility"; - - // TODO: Add super header data. // TODO: Generate delegates. var propClass = new PropertyClassFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); - var propStruct = new PropertyStructReprFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); + var propStruct = new PropertyStructFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); - // TODO: Generate functions. + // TODO + // var functions = new FunctionFactory(Context).ResolveFunctions(uclass); + + Context.Registry.Structs[innerName] = + new ClassDefinition(innerName, displayName, size, alignment, propClass, propStruct, [], superName); + } +} - Context.Registry.Structs[className] = - new ClassDefinition(className, classNativeName, size, alignment, propClass, propStruct, superName); +public class ClassFactoryStatic(Context context, ObjectType objectType, IUClass uclass) + : BaseObjectFactory(context, objectType) +{ + public override void Register() { + var innerName = uclass.NamePrivate.ToString(); + var displayName = uclass.GetNativeName(); + var size = uclass.PropertiesSize; + var alignment = uclass.MinAlignment; - Log.Debug($"UObject: {classNativeName}"); + var super = uclass.GetSuperClass(); + var superSize = super?.PropertiesSize ?? 0; + var superName = super?.NamePrivate.ToString(); + + var propStruct = new PropertyStructFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); + Context.Registry.Structs[innerName] = new StructDefinition(innerName, displayName, size, alignment, propStruct, superName); } } @@ -41,10 +55,12 @@ public class ClassDefinition( int alignment, List propClass, List propStruct, + List functions, string? superInternalName) : StructDefinition(internalName, displayName, size, alignment, propClass, superInternalName) { private List PropStruct => propStruct; + private List Functions => functions; public override string Serialize(Context context) { @@ -56,10 +72,12 @@ public override string Serialize(Context context) if (SuperInternalName != null) context.Registry.Structs.TryGetValue(SuperInternalName, out SuperDef); var SuperDefName = SuperDef != null ? Builtins.SanitizeName(SuperDef.DisplayName) : "ObjectImpl"; sb.AppendLine($"public class {DisplayNameCS}(IUObject inner) : {SuperDefName}(inner), ITypeRepr<{DisplayNameRepr}>\n{{"); - var ReprNewModifier = DisplayNameCS != "UObject" ? "new " : string.Empty; + var ReprNewModifier = SuperDef != null ? "new " : string.Empty; sb.AppendLine($"\tpublic {ReprNewModifier}unsafe {DisplayNameRepr}* Repr => ({DisplayNameRepr}*)Inner.Ptr;\n"); foreach (var prop in Properties) sb.AppendLine(prop.Serialize(context)); + foreach (var func in Functions) + sb.AppendLine(func.Serialize(context)); sb.AppendLine("}\n"); var reprDef = new StructDefinitionRepr(InternalName, DisplayName, Size, Alignment, PropStruct, SuperInternalName); sb.AppendLine(reprDef.Serialize(context)); @@ -67,6 +85,8 @@ public override string Serialize(Context context) } public override ObjectType Type => ObjectType.Class; + + public override string GetUnmanagedTypeName() => DisplayName + "_Repr"; } public class StructDefinitionRepr( @@ -89,7 +109,8 @@ public override string Serialize(Context context) var (superName, superSize) = GetSuperInfo(context); var superNameFmt = superName != null ? $"{superName}_Repr" : null; WriteStructBase(sb, DisplayNameRepr, superNameFmt, superSize, context); - sb.AppendLine($"\n\tpublic {DisplayNameCS} ToManaged(IUnrealFactory factory)\n\t{{"); + var sep = Properties.Count > 0 ? "\n" : string.Empty; + sb.AppendLine($"{sep}\tpublic {DisplayNameCS} ToManaged(IUnrealFactory factory)\n\t{{"); sb.AppendLine($"\t\tfixed ({DisplayNameRepr}* self = &this) return new(factory.CreateUObject((nint)self));"); sb.AppendLine("\t}\n}"); return sb.ToString(); diff --git a/UE.Toolkit.DumperMod/Definitions/Enum.cs b/UE.Toolkit.DumperMod/Definitions/Enum.cs index 2a7bff5..12f4d6e 100644 --- a/UE.Toolkit.DumperMod/Definitions/Enum.cs +++ b/UE.Toolkit.DumperMod/Definitions/Enum.cs @@ -4,7 +4,8 @@ namespace UE.Toolkit.DumperMod.Definitions; -public class EnumFactory(Context context, IUEnum uenum, string? knownType = null) : BaseObjectFactory(context) +public class EnumFactory(Context context, ObjectType objectType, IUEnum uenum, string? knownType = null) + : BaseObjectFactory(context, objectType) { public override void Register() { diff --git a/UE.Toolkit.DumperMod/Definitions/Function.cs b/UE.Toolkit.DumperMod/Definitions/Function.cs index 5f28270..07ab9a9 100644 --- a/UE.Toolkit.DumperMod/Definitions/Function.cs +++ b/UE.Toolkit.DumperMod/Definitions/Function.cs @@ -1 +1,129 @@ - \ No newline at end of file +using System.Text; +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.DumperMod.Definitions; + +public abstract class ParameterDefinition(string name, Func propTypeName, bool isReference) : ISerializable +{ + public string Name { get; } = name; + public Func PropTypeName { get; } = propTypeName; + public bool IsReference { get; } = isReference; + + public abstract string Serialize(Context context); +} + +public class ParameterHeadDefinition(string name, Func propTypeName, bool isReference) + : ParameterDefinition(name, propTypeName, isReference) +{ + public override string Serialize(Context context) => $"{(IsReference ? "ref" : string.Empty)} {PropTypeName()} {Name}"; +} + +public class ParameterBodyDefinition(string name, Func propTypeName, bool isReference, string? paramTypeName, bool isLast) + : ParameterDefinition(name, propTypeName, isReference) +{ + public string? ParamTypeName => paramTypeName; + public bool IsLast { get; set; } = isLast; + + public override string Serialize(Context context) + { + var typeName = propTypeName(); + if (ParamTypeName == null) + { + return $"// PARAM TODO {typeName}"; + } + var ptrCast = IsReference ? $"&{Name}" : $"({typeName}*)Unsafe.AsPointer(ref {Name})"; + var paramDecl = ParamTypeName switch + { + _ => $"new {ParamTypeName}(new({ptrCast}))" + }; + return $"{paramDecl}{(IsLast ? string.Empty : ",")}"; + } +} + +public class ReturnValueDefinition(string? paramTypeName) : ISerializable +{ + public string? ParamTypeName => paramTypeName; + + public string Serialize(Context context) + { + if (ParamTypeName == null) + { + return $"return null; // RETURN VALUE TODO {ParamTypeName}"; + } + var ConstructValue = ParamTypeName switch + { + "ObjectProperty" or "ClassProperty" or "ClassPtrProperty" + => $"new(Inner.GetFactory().GetUObject((({ParamTypeName})Return).Value))", + _ => $"(({ParamTypeName})Return).Value" + }; + return $"return {ConstructValue};"; + } +} + +public class FunctionFactory(Context context) +{ + public List ResolveFunctions(IUClass uclass) + { + var PropFactory = new PropertyClassFactory(context); + return uclass.GetFunctions() + .Where(x => !x.NamePrivate.ToString().StartsWith("ExecuteUbergraph")) + .Select(x => + { + List HeadParams = []; + List BodyParams = []; + ReturnValueDefinition? ReturnValue = null; + Func? returnTypeName = null; + foreach (var Field in x.ChildProperties) + { + var Param = context.Factory.CreateFProperty(Field.Ptr); + if (Param.PropertyFlags.HasFlag(EPropertyFlags.CPF_ReturnParm)) + { + returnTypeName = PropFactory.GetPropTypenameStruct(Param); + var returnParamName = FunctionParamFactory.GetParamNameFromProperty(Param, context.Factory, context.Classes); + ReturnValue = new(returnParamName); + break; + } + var IsReference = Param.PropertyFlags.HasFlag(EPropertyFlags.CPF_OutParm); + var paramCSName = FunctionParamFactory.GetParamNameFromProperty(Param, context.Factory, context.Classes); + var typeName = Builtins.SanitizeName(Param.NamePrivate); + var propTypeName = PropFactory.GetPropTypenameStruct(Param); + HeadParams.Add(new ParameterHeadDefinition(typeName, propTypeName, IsReference )); + BodyParams.Add(new ParameterBodyDefinition(typeName, propTypeName, IsReference, paramCSName, false )); + } + if (BodyParams.Count > 0) BodyParams.Last().IsLast = true; + return new FunctionDefinition(Builtins.SanitizeName(x.NamePrivate.ToString()), returnTypeName, HeadParams, BodyParams, ReturnValue); + }).ToList(); + } +} + +public class FunctionDefinition(string name, Func? returnTypeName, List headParams, + List bodyParams, ReturnValueDefinition? returnParam) + : ISerializable +{ + public string Name { get; } = name; + public Func? ReturnTypeName { get; } = returnTypeName; + public List HeadParams { get; } = headParams; + public List BodyParams { get; } = bodyParams; + public ReturnValueDefinition? ReturnParam { get; } = returnParam; + + public string Serialize(Context context) + { + var sb = new StringBuilder(); + var returnType = ReturnTypeName?.Invoke() ?? "void"; + sb.AppendLine($"\tpublic unsafe {returnType} {Name}({string.Join(",", HeadParams.Select(x => x.Serialize(context)))})"); + sb.AppendLine("\t{"); + var outVar = (ReturnTypeName != null ? "var Return" : "_"); + if (BodyParams.Count > 0) + { + sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{Name}\", ["); + foreach (var bodyParam in BodyParams) sb.AppendLine($"\t\t\t{bodyParam.Serialize(context)}"); + sb.AppendLine($"\t\t], out {outVar});"); + } + else sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{Name}\", [], out {outVar});"); + if (ReturnParam != null) sb.AppendLine($"\t\t{ReturnParam.Serialize(context)}"); + sb.AppendLine("\t}"); + return sb.ToString(); + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs index 727ff91..c7c34aa 100644 --- a/UE.Toolkit.DumperMod/Definitions/Property.cs +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -30,11 +30,10 @@ public override string Serialize(Context context) { var propName = Builtins.SanitizeName(Name); var sb = new StringBuilder(); - var typename = PropTypeName(); - sb.AppendLine($"\tpublic unsafe {typename} {propName}"); + sb.AppendLine($"\tpublic unsafe {PropTypeName()} {propName}"); sb.AppendLine("\t{"); - sb.AppendLine($"\t\t{Accessor(typename)}"); - sb.AppendLine($"\t\t{Mutator(typename)}"); + sb.AppendLine($"\t\t{Accessor(Name)}"); + sb.AppendLine($"\t\t{Mutator(Name)}"); sb.AppendLine("\t}"); return sb.ToString(); } @@ -69,7 +68,7 @@ public List ResolveProperties(IEnumerable pro /// /// /// C++/C# property type name. - protected Func GetPropTypenameStruct(IFProperty prop) + public Func GetPropTypenameStruct(IFProperty prop) { var className = prop.ClassPrivate.Name; switch (className) @@ -81,7 +80,7 @@ protected Func GetPropTypenameStruct(IFProperty prop) if (byteProp.Enum != null) { var byteEnumName = byteProp.Enum.NamePrivate.ToString(); - new EnumFactory(context, context.Factory.Cast(byteProp.Enum), "byte").Register(); + new EnumFactory(context, ObjectType.Enum, context.Factory.Cast(byteProp.Enum), "byte").Register(); return () => Builtins.SanitizeName(byteEnumName); } @@ -121,40 +120,37 @@ protected Func GetPropTypenameStruct(IFProperty prop) return () => "FWeakObjectPtr"; case "ObjectProperty": var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : $"{objPropType}*"); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.GetUnmanagedTypeName()}*" : $"{objPropType}*"); case "SoftObjectProperty": var softObjPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.DisplayName}>" : $"TSoftObjectPtr<{softObjPropType}>"); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.GetUnmanagedTypeName()}>" : $"TSoftObjectPtr<{softObjPropType}>"); case "SoftClassProperty": var softClassPropType = context.Factory.Cast(prop).MetaClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softClassPropType, out var knownStruct) ? $"TSoftClassPtr<{knownStruct.DisplayName}>" : $"TSoftClassPtr<{softClassPropType}>"); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softClassPropType, out var knownStruct) ? $"TSoftClassPtr<{knownStruct.GetUnmanagedTypeName()}>" : $"TSoftClassPtr<{softClassPropType}>"); case "StructProperty": var structPropType = context.Factory.Cast(prop).Struct.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(structPropType, out var knownStruct) ? knownStruct.DisplayName : structPropType); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(structPropType, out var knownStruct) ? knownStruct.GetUnmanagedTypeName() : structPropType); case "ClassProperty": case "ClassPtrProperty": var classPropClass = context.Factory.Cast(prop).MetaClass; var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass*"; - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : classPropType); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.GetUnmanagedTypeName()}*" : classPropType); case "EnumProperty": var enumProp = context.Factory.Cast(prop); var enumPropName = enumProp.Enum.NamePrivate.ToString(); - new EnumFactory(context, enumProp.Enum, context.Classes.GetPropertyTypeName(enumProp.UnderlyingProp)).Register(); + new EnumFactory(context, ObjectType.Enum, enumProp.Enum, context.Classes.GetPropertyTypeName(enumProp.UnderlyingProp)).Register(); return () => Builtins.SanitizeName(enumPropName); case "MapProperty": var mapProp = context.Factory.Cast(prop); - var mapPropKeyType = context.Classes.GetPropertyTypeName(mapProp.KeyProp); - var mapPropValueType = context.Classes.GetPropertyTypeName(mapProp.ValueProp); return () => { + var mapPropKeyType = GetPropTypenameStruct(mapProp.KeyProp)(); + var mapPropValueType = GetPropTypenameStruct(mapProp.ValueProp)(); var isKeyPtr = mapPropKeyType.EndsWith('*') || mapPropKeyType.Contains('<'); // Use nint for pointers and generic types. var isValuePtr = mapPropValueType.EndsWith('*') || mapPropValueType.Contains('<'); - - context.Registry.Structs.TryGetValue(mapPropKeyType.TrimEnd('*'), out var knownKeyStruct); - context.Registry.Structs.TryGetValue(mapPropValueType.TrimEnd('*'), out var knownValueStruct); - var keyName = Builtins.SanitizeName(knownKeyStruct?.DisplayName ?? mapPropKeyType); - var valueName = Builtins.SanitizeName(knownValueStruct?.DisplayName ?? mapPropValueType); + var keyName = Builtins.SanitizeName(mapPropKeyType.TrimEnd('*')); + var valueName = Builtins.SanitizeName(mapPropValueType.TrimEnd('*')); var keyType = isKeyPtr ? $"Ptr<{keyName}>" : keyName; var valueType = isValuePtr ? $"Ptr<{valueName}>" : valueName; @@ -163,34 +159,32 @@ protected Func GetPropTypenameStruct(IFProperty prop) }; case "InterfaceProperty": var intPropType = context.Factory.Cast(prop).InterfaceClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(intPropType, out var knownStruct) ? $"TScriptInterface<{knownStruct.DisplayName}>" : $"TScriptInterface<{intPropType}>"); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(intPropType, out var knownStruct) ? $"TScriptInterface<{knownStruct.GetUnmanagedTypeName()}>" : $"TScriptInterface<{intPropType}>"); case "ArrayProperty": - var arrayPropType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).Inner); return () => { + var arrayPropType = GetPropTypenameStruct(context.Factory.Cast(prop).Inner)(); var isPtrType = arrayPropType.EndsWith('*') || arrayPropType.Contains('<'); // Use nint for pointers and generic types. - context.Registry.Structs.TryGetValue(arrayPropType.TrimEnd('*'), out var knownStruct); - var arrTypeSanitized = Builtins.SanitizeName(knownStruct?.DisplayName ?? arrayPropType); + var arrTypeSanitized = Builtins.SanitizeName(arrayPropType.TrimEnd('*')); return isPtrType ? $"TArray>" : $"TArray<{arrTypeSanitized}>"; }; case "SetProperty": - var setPropType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).ElementProp); return () => { + var setPropType = GetPropTypenameStruct(context.Factory.Cast(prop).ElementProp)(); var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; - context.Registry.Structs.TryGetValue(setPropType.TrimEnd('*'), out var knownStruct); return isPtrType ? $"TSet /* TSet<{setPropType}> */" - : $"TSet<{Builtins.SanitizeName(knownStruct?.DisplayName ?? setPropType)}>"; + : $"TSet<{Builtins.SanitizeName(setPropType.TrimEnd('*'))}>"; }; case "OptionalProperty": - var optionalType = context.Classes.GetPropertyTypeName(context.Factory.Cast(prop).ValueProperty); return () => { + var optionalType = GetPropTypenameStruct(context.Factory.Cast(prop).ValueProperty)(); if (context.Registry.Structs.TryGetValue(optionalType, out var knownOptType)) - optionalType = knownOptType.DisplayName; + optionalType = knownOptType.GetUnmanagedTypeName(); return $"TOptional<{Builtins.SanitizeName(optionalType)}>"; }; @@ -201,7 +195,7 @@ protected Func GetPropTypenameStruct(IFProperty prop) return () => { if (context.Registry.Structs.TryGetValue(lazyObjType, out var knownLazyType)) - lazyObjType = knownLazyType.DisplayName; + lazyObjType = knownLazyType.GetUnmanagedTypeName(); return $"TLazyObjectPtr<{Builtins.SanitizeName(lazyObjType)}>"; }; @@ -236,29 +230,6 @@ protected override BasePropertyDefintion ResolveProperty(IFProperty prop) } } -public class PropertyStructReprFactory(Context context) : BasePropertyFactory(context) -{ - private Func GetPropTypenameStructRepr(IFProperty prop) - { - var className = prop.ClassPrivate.Name; - switch (className) - { - case "ObjectProperty": - var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}_Repr*" : $"{objPropType}*"); - default: - return GetPropTypenameStruct(prop); - } - } - - protected override BasePropertyDefintion ResolveProperty(IFProperty prop) - { - var (name, size, offset, _) = GetBaseInfo(prop); - if (name is "bool" or "float") name += '_'; - return new PropertyStructDefinition(name, size, offset, GetPropTypenameStructRepr(prop)); - } -} - public class PropertyClassFactory(Context context) : BasePropertyFactory(context) { private Func GetPropAccessor(IFProperty prop, Func getTypeName) @@ -268,7 +239,9 @@ private Func GetPropAccessor(IFProperty prop, Func getTy { case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" - or "NameProperty" or "StructProperty": + or "NameProperty" or "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "EnumProperty" or "TextProperty" + or "WeakObjectProperty" or "SetProperty": return propName => $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); @@ -277,7 +250,7 @@ private Func GetPropAccessor(IFProperty prop, Func getTy true => propName => $"get => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);", false => propName => $"get => (*(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) & {BoolProp.FieldMask}) == 0;" }; - case "ObjectProperty": + case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": return propName => $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"])));"; default: @@ -292,7 +265,9 @@ private Func GetPropMutator(IFProperty prop, Func getTyp { case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" - or "NameProperty" or "StructProperty": + or "NameProperty" or "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "EnumProperty" or "TextProperty" + or "WeakObjectProperty" or "SetProperty": return propName => $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;"; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); @@ -301,7 +276,7 @@ private Func GetPropMutator(IFProperty prop, Func getTyp true => propName => $"set => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;", false => propName => $"set => *(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) ^= (byte)(Convert.ToByte({propName} != value) * {BoolProp.FieldMask});" }; - case "ObjectProperty": + case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": return propName => $"set => *(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value.Inner.Ptr;"; default: return _ => $"set => throw new NotSupportedException(\"!! SET TODO {className} !!\");"; @@ -313,9 +288,14 @@ private Func GetPropTypenameClass(IFProperty prop) var className = prop.ClassPrivate.Name; switch (className) { + case "ClassProperty": + case "ClassPtrProperty": + var classPropClass = context.Factory.Cast(prop).MetaClass; + var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass"; + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : classPropType); case "ObjectProperty": var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}*"); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); default: return GetPropTypenameStruct(prop); } diff --git a/UE.Toolkit.DumperMod/Definitions/Registry.cs b/UE.Toolkit.DumperMod/Definitions/Registry.cs index 2119d9d..55b8b63 100644 --- a/UE.Toolkit.DumperMod/Definitions/Registry.cs +++ b/UE.Toolkit.DumperMod/Definitions/Registry.cs @@ -1,15 +1,18 @@ -using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using System.Text; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; namespace UE.Toolkit.DumperMod.Definitions; +[Flags] public enum ObjectType { - Class, // inherits from UClass - Struct, // inherits from UScriptStruct - Enum, // inherits from UEnum - // Interface, // inherits from UInterface - Other, + None = 0, + Class = 1 << 0, // inherits from UClass + Struct = 1 << 1, // inherits from UScriptStruct + Enum = 1 << 2, // inherits from UEnum + Interface = 1 << 3, // inherits from UInterface + Function = 1 << 5, // inherits from UFunction } public class Registry(Context context) @@ -18,33 +21,24 @@ public class Registry(Context context) public Dictionary Structs = []; public Dictionary Enums = []; - private Dictionary Types = []; - private ObjectType FromTypeName(IUClass uclass) => uclass.NamePrivate.ToString() switch + private IObjectFactory? GetObjectFactory(IUObject obj) { - "Class" => ObjectType.Class, - "ScriptStruct" => ObjectType.Struct, - "Enum" => ObjectType.Enum, - // "Interface" => ObjectType.Interface, - _ => ObjectType.Other - }; - - private ObjectType RetrieveObjectType(IUObject uobject) - { - string ClassName = uobject.ClassPrivate.NamePrivate.ToString(); - IUClass? CurrentClass = uobject.ClassPrivate; - while (CurrentClass != null) + if (obj.IsChildOf()) { - var CurrentType = FromTypeName(CurrentClass); - if (CurrentType != ObjectType.Other) + var objectType = ObjectType.Class; + if (obj.IsChildOf()) objectType |= ObjectType.Interface; + return Mod.Config.Schema switch { - Types[ClassName] = CurrentType; - return CurrentType; - } - CurrentClass = CurrentClass.GetSuperClass(); + DumpSchema.Static => new ClassFactoryStatic(Context, objectType, Context.Factory.Cast(obj)), + DumpSchema.Dynamic => new ClassFactory(Context, objectType, Context.Factory.Cast(obj)), + }; } - Types[ClassName] = ObjectType.Other; - return ObjectType.Other; + if (obj.IsChildOf()) + return new StructFactory(Context, ObjectType.Struct, Context.Factory.Cast(obj)); + if (obj.IsChildOf()) + return new EnumFactory(Context, ObjectType.Enum, Context.Factory.Cast(obj)); + return null; } public void Register() @@ -54,49 +48,29 @@ public void Register() { var obj = objectArray.IndexToObject(i); if (obj == null) continue; - - /* - var typeName = obj.ClassPrivate.NamePrivate.ToString(); - var moduleName = GetModuleNameForPackage(obj.GetOutermost()); - var fileBaseName = obj.ClassPrivate.NamePrivate.ToString(); - Log.Debug($"Object {obj.NamePrivate} | Filename {fileBaseName} | Module {moduleName}"); - */ - var typeName = obj.ClassPrivate.NamePrivate.ToString(); - IObjectFactory? factory = (Types.TryGetValue(typeName, out var objectType) ? objectType : RetrieveObjectType(obj)) switch - { - ObjectType.Class => new ClassFactory(Context, Context.Factory.Cast(obj)), - ObjectType.Struct => new StructFactory(Context, Context.Factory.Cast(obj)), - ObjectType.Enum => new EnumFactory(Context, Context.Factory.Cast(obj)), - _ => null - }; - factory?.Register(); - /* - if (obj.IsChildOf()) - { - var uclass = Context.Factory.Cast(obj); - if (obj.IsChildOf()) - { - // TODO: - var interfaceName = obj.NamePrivate.ToString(); - Context.Registry.Structs[interfaceName] = new(interfaceName, interfaceName, 0, 0, [], null); - Log.Debug($"Interface: {interfaceName}"); - } - else - { - Context.ClassFactory.Register(uclass); - } - } - else if (obj.IsChildOf()) - { - Context.StructFactory.Register(Context.Factory.Cast(obj)); - } - else if (obj.IsChildOf()) - { - Context.EnumFactory.Register(Context.Factory.Cast(obj)); - } - */ + // Interfaces? (Interface) + GetObjectFactory(obj)?.Register(); } } + + private void SerializeDefinition(string Name, ISerializable ser, StringBuilder? sb, ref int numDumped) + { + if (Mod.Config.Mode == DumpFileMode.FilePerType) + { + var outputFile = Path.Join(Context.DumpDirectory, $"{Name}.cs"); + File.WriteAllText(outputFile, ser.Serialize(Context)); + } + else sb?.AppendLine(ser.Serialize(Context)); + numDumped++; + } + + public void Serialize(StringBuilder? sb, ref int numDumped) + { + foreach (var (_, Definition) in Context.Registry.Structs) + SerializeDefinition(Definition.DisplayName, Definition, sb, ref numDumped); + foreach (var (Name, Definition) in Context.Registry.Enums) + SerializeDefinition(Name, Definition, sb, ref numDumped); + } private static string GetModuleNameForPackage(IUObject package) { @@ -113,26 +87,4 @@ private static string GetModuleNameForPackage(IUObject package) return packageName["/Script/".Length..]; } - - private static unsafe string GetHeaderNameForObject(IUObject obj) - { - string? headerName = null; - UObjectBase* finalObj; - - if (obj.IsA() || obj.IsA()) - { - headerName = obj.NamePrivate.ToString(); - } - else if (obj.IsA()) - { - headerName = obj.NamePrivate.ToString(); - } - else - { - // TODO: UFunction stuff; - } - - // TODO: Other stuff? - return headerName; - } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Struct.cs b/UE.Toolkit.DumperMod/Definitions/Struct.cs index 5826da0..c4bd867 100644 --- a/UE.Toolkit.DumperMod/Definitions/Struct.cs +++ b/UE.Toolkit.DumperMod/Definitions/Struct.cs @@ -3,18 +3,22 @@ namespace UE.Toolkit.DumperMod.Definitions; -public class StructFactory(Context context, IUScriptStruct scriptStruct) : BaseObjectFactory(context) +public class StructFactory(Context context, ObjectType objectType, IUScriptStruct scriptStruct) : BaseObjectFactory(context, objectType) { public override void Register() { var structName = scriptStruct.NamePrivate.ToString(); - var structNativeName = scriptStruct.GetNativeName(); + var structNativeName = "F" + structName; var size = scriptStruct.PropertiesSize; var alignment = scriptStruct.MinAlignment; var super = scriptStruct.SuperStruct; var superSize = super?.PropertiesSize ?? 0; var superName = super?.NamePrivate.ToString(); - //var superNativeName = super != null ? GetNativeStructName((UScriptStruct*)super) : null; + if ((superName ?? string.Empty) == structName) + { + Log.Warning($"{nameof(StructFactory)} || '{structNativeName}' is recursive"); + return; + } var props = new PropertyStructFactory(Context).ResolveProperties(scriptStruct.PropertyLink, superSize); Context.Registry.Structs[structName] = new StructDefinition(structName, structNativeName, size, alignment, props, superName); } @@ -94,4 +98,6 @@ public override string Serialize(Context context) } public virtual ObjectType Type => ObjectType.Struct; + + public virtual string GetUnmanagedTypeName() => DisplayName; } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Dumper.cs b/UE.Toolkit.DumperMod/Dumper.cs index e122a82..659adf1 100644 --- a/UE.Toolkit.DumperMod/Dumper.cs +++ b/UE.Toolkit.DumperMod/Dumper.cs @@ -1,637 +1,62 @@ -using System.Diagnostics; +using System.Diagnostics; using System.Text; using UE.Toolkit.Core.Types.Unreal.Factories; -using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UE.Toolkit.DumperMod.Definitions; using UE.Toolkit.Interfaces; using UnrealEssentials.Interfaces; -// ReSharper disable InconsistentNaming - namespace UE.Toolkit.DumperMod; -public unsafe class Dumper +public class Dumper( + IUnrealFactory factory, + IUnrealObjects uobjs, + IUnrealStrings strs, + IUnrealClasses classes, + string dumpDir, + IUnrealEssentials essentials) { - private record PropertyDefinition(string Name, int Size, int Offset, Func PropTypeName) : ICsharpText - { - public string GetCsharpText() - { - return $"[FieldOffset(0x{Offset:X})] public {PropTypeName()} {SanitizeName(Name)}; // Size: 0x{Size:X}"; - } - } - - private record UStructDefinition(string InternalName, string DisplayName, int Size, int Alignment, PropertyDefinition[] Properties, string? SuperInternalName) : ICsharpText - { - public string Name => SanitizeName(DisplayName); - - public string GetCsharpText() - { - var sb = new StringBuilder(); - - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - AddHeader(sb); - } - - sb.AppendLine($"[StructLayout(LayoutKind.Explicit, Pack = {Alignment}, Size = 0x{Size:X})]"); - sb.AppendLine($"public unsafe struct {SanitizeName(DisplayName)}\n{{"); - if (SuperInternalName != null) - { - if (_UStructDefinitions.TryGetValue(SuperInternalName, out var super)) - { - sb.AppendLine($" [FieldOffset(0x0)] public {SanitizeName(super.DisplayName)} Super; // Size: 0x{super.Size:X}"); - } - else - { - Log.Warning($"Failed to get super: {SuperInternalName}"); - } - } - - foreach (var prop in Properties) - { - sb.AppendLine($" {prop.GetCsharpText()}"); - } - - sb.AppendLine("}"); - return sb.ToString(); - } - } - - private record UEnumDefinition(string Name, string UnderlyingType, Dictionary Entries) : ICsharpText - { - public string GetCsharpText() - { - var sb = new StringBuilder(); - - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - AddHeader(sb); - } - - sb.AppendLine($"public enum {SanitizeName(Name)} : {UnderlyingType}\n{{"); - foreach (var entry in Entries) - { - sb.AppendLine($" {SanitizeEntryName(entry.Key)} = {entry.Value},"); - } - - sb.AppendLine("}"); - return sb.ToString(); - } - - private static string SanitizeEntryName(string name) => SanitizeName(name.Split("::").Last()); - } - - private static readonly Dictionary _UStructDefinitions = []; - private readonly Dictionary _UEnumDefinitions = []; - private readonly string _dumpDir; - private readonly IUnrealObjects _uobjs; - private readonly IUnrealStrings _strs; - private readonly IUnrealFactory _factory; - private readonly IUnrealClasses _classes; - private readonly IUnrealEssentials _essentials; - - public Dumper(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, IUnrealClasses classes, - string dumpDir, IUnrealEssentials essentials) - { - _uobjs = uobjs; - _strs = strs; - _dumpDir = dumpDir; - _factory = factory; - _classes = classes; - _essentials = essentials; - - if (Directory.Exists(dumpDir)) Directory.Delete(dumpDir, true); - Directory.CreateDirectory(dumpDir); - } + private Context Context = new(factory, uobjs, strs, classes, dumpDir, essentials); public void DumpObjects() { Log.Information("Dumping objects..."); var sw = new Stopwatch(); - sw.Start(); - RegisterObjects(); - + Context.Registry.Register(); + sw.Stop(); + StringBuilder? sb = null; if (Mod.Config.Mode == DumpFileMode.SingleFile) { sb = new(); - AddHeader(sb); - AddPtrDefinition(sb); + Context.Builtins.AddHeader(sb); + if (Mod.Config.Schema == DumpSchema.Dynamic) Context.Builtins.AddBaseObjectDefinition(sb); } else { - var outputFile = Path.Join(_dumpDir, "Builtin_Ptr.cs"); - var sbPtr = new StringBuilder(); - AddHeader(sbPtr); - AddPtrDefinition(sbPtr); - File.WriteAllText(outputFile, sbPtr.ToString()); - } - - var numDumped = 0; - foreach (var item in _UStructDefinitions) - { - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - var outputFile = Path.Join(_dumpDir, $"{item.Value.DisplayName}.cs"); - File.WriteAllText(outputFile, item.Value.GetCsharpText()); - } - else - { - sb?.AppendLine(item.Value.GetCsharpText()); - } - - numDumped++; + var outputFile = Path.Join(Context.DumpDirectory, "UObjectImpl.cs"); + var implWriter = new StringBuilder(); + Context.Builtins.AddHeader(implWriter); + if (Mod.Config.Schema == DumpSchema.Dynamic) Context.Builtins.AddBaseObjectDefinition(implWriter); + File.WriteAllText(outputFile, implWriter.ToString()); } - foreach (var item in _UEnumDefinitions) - { - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - var outputFile = Path.Join(_dumpDir, $"{item.Key}"); - File.WriteAllText(outputFile, item.Value.GetCsharpText()); - } - else - { - sb?.AppendLine(item.Value.GetCsharpText()); - } - - numDumped++; - } - + var numDumped = 0; + Context.Registry.Serialize(sb, ref numDumped); sw.Stop(); if (Mod.Config.Mode == DumpFileMode.SingleFile) { var singleFileOutput = string.IsNullOrEmpty(Mod.Config.SingleFileOutputName) - ? Path.Join(_dumpDir, "Types.cs") - : Path.Join(_dumpDir, $"{Mod.Config.SingleFileOutputName.Replace(".cs", string.Empty)}.cs"); + ? Path.Join(Context.DumpDirectory, "Types.cs") + : Path.Join(Context.DumpDirectory, $"{Mod.Config.SingleFileOutputName.Replace(".cs", string.Empty)}.cs"); File.WriteAllText(singleFileOutput, sb!.ToString()); Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput File: {singleFileOutput}"); } else { - Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput Folder: {_dumpDir}"); - } - } - - private void RegisterObjects() - { - var objArray = _uobjs.GUObjectArray; - for (int i = 0; i < _uobjs.GUObjectArray.NumElements; i++) - { - //if (i > _uobjs.GUObjectArray.ObjLastNonGCIndex) break; - - var obj = _uobjs.GUObjectArray.IndexToObject(i); - if (obj == null) continue; - - // var uclass = (Core.Types.Unreal.UE4_27_2.UClass*)obj.Ptr; - // continue; - var moduleName = GetModuleNameForPackage(obj.GetOutermost()); - var fileBaseName = GetHeaderNameForObject(obj); - - if (obj.IsChildOf()) - { - var uclass = _factory.Cast(obj); - if (obj.IsChildOf()) - { - // TODO: - var interfaceName = obj.NamePrivate.ToString(); - _UStructDefinitions[interfaceName] = new(interfaceName, interfaceName, 0, 0, [], null); - Log.Debug($"Interface: {interfaceName}"); - } - else - { - GenerateObjectDefinition(uclass); - } - } - else if (obj.IsChildOf()) - { - GenerateStructDefinition(_factory.Cast(obj)); - } - else if (obj.IsChildOf()) - { - GenerateEnumDefinition(_factory.Cast(obj)); - } - } - } - - private static string GetModuleNameForPackage(IUObject package) - { - if (package.OuterPrivate != null) - { - throw new("Encountered a package with an outer object set"); - } - - var packageName = package.NamePrivate.ToString(); - if (!packageName.StartsWith("/Script/")) - { - return string.Empty; - } - - return packageName["/Script/".Length..]; - } - - private static string GetHeaderNameForObject(IUObject obj) - { - string? headerName = null; - UObjectBase* finalObj; - - if (obj.IsA() || obj.IsA()) - { - headerName = obj.NamePrivate.ToString(); - } - else if (obj.IsA()) - { - headerName = obj.NamePrivate.ToString(); - } - else - { - // TODO: UFunction stuff; - } - - // TODO: Other stuff? - return headerName; - } - - private static void AddHeader(StringBuilder sb) - { - sb.AppendLine("/* Generated with UE Toolkit: Dumper (1.9.0) */"); - sb.AppendLine("/* GitHub: https://github.com/RyoTune/UE.Toolkit */"); - sb.AppendLine("/* Author: RyoTune */"); - sb.AppendLine("/* Special thanks to UE4SS team and Rirurin */"); - sb.AppendLine("/* whose code was used for reference. */"); - sb.AppendLine(); - - sb.AppendLine("using System.Runtime.InteropServices;"); - - if (!string.IsNullOrEmpty(Mod.Config.FileUsings)) - { - var usings = Mod.Config.FileUsings.Split(';', - StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Replace("using ", string.Empty)); - foreach (var use in usings) sb.AppendLine($"using {use};"); - } - - sb.AppendLine(); - if (!string.IsNullOrEmpty(Mod.Config.FileNamespace)) - { - sb.AppendLine($"namespace {Mod.Config.FileNamespace.TrimEnd(';').Replace("namespace ", string.Empty)};"); - sb.AppendLine(); - } - } - - private static void AddPtrDefinition(StringBuilder sb) - { - sb.AppendLine("public readonly unsafe struct Ptr(T* value) : IEquatable>"); - sb.AppendLine(" where T : unmanaged"); - sb.AppendLine("{"); - sb.AppendLine(" public readonly T* Value = value;"); - sb.AppendLine(); - sb.AppendLine(" public bool Equals(Ptr other) => Value == other.Value;"); - sb.AppendLine(" public static bool operator ==(Ptr a, Ptr b) => a.Equals(b);"); - sb.AppendLine(" public static bool operator !=(Ptr a, Ptr b) => !a.Equals(b);"); - sb.AppendLine("}"); - sb.AppendLine(); - } - - private void GenerateObjectDefinition(IUClass uclass) - { - var className = uclass.NamePrivate.ToString(); - var classNativeName = uclass.GetNativeName(); - var size = uclass.PropertiesSize; - var alignment = uclass.MinAlignment; - - // TODO: Flag stuff? - - var super = uclass.GetSuperClass(); - var superSize = super?.PropertiesSize ?? 0; - var superName = super?.NamePrivate.ToString(); - //var superNativeName = super != null ? GetNativeClassName(super) : "UObjectBaseUtility"; - - // TODO: Add super header data. - - // TODO: Generate delegates. - - var properties = ResolveProperties(uclass.PropertyLink, superSize); - - // TODO: Generate functions. - - _UStructDefinitions[className] = - new(className, classNativeName, size, alignment, properties, superName); - - Log.Debug($"UObject: {classNativeName}"); - } - - private void GenerateStructDefinition(IUScriptStruct scriptStruct) - { - var structName = scriptStruct.NamePrivate.ToString(); - var structNativeName = scriptStruct.GetNativeName(); - var size = scriptStruct.PropertiesSize; - var alignment = scriptStruct.MinAlignment; - var super = scriptStruct.SuperStruct; - var superSize = super?.PropertiesSize ?? 0; - var superName = super?.NamePrivate.ToString(); - //var superNativeName = super != null ? GetNativeStructName((UScriptStruct*)super) : null; - - var props = ResolveProperties(scriptStruct.PropertyLink, superSize); - - _UStructDefinitions[structName] = new(structName, structNativeName, size, alignment, props, superName); - Log.Debug($"UScriptStruct: {structNativeName}"); - } - - private void GenerateEnumDefinition(IUEnum uenum, string? knownType = null) - { - var name = uenum.NamePrivate.ToString(); - if (_UEnumDefinitions.ContainsKey(name) && knownType == null) return; - - var entries = new Dictionary(); - - var dispNames = new Dictionary(); - if (uenum.IsChildOf()) - { - var userEnum = _factory.Cast(uenum); - for (var i = 0; i < uenum.Names.ArrayNum; i++) - { - var dispName = _strs.UEnumGetDisplayNameTextByIndex(userEnum.Ptr, i); - dispNames[i] = $"{name}::{dispName}"; - } - } - - long bigEntryValue = 0; - string bigEntryName = string.Empty; - for (int i = 0; i < uenum.Names.ArrayNum; i++) - { - var entry = &uenum.Names.AllocatorInstance[i]; - - if (!dispNames.TryGetValue(i, out var entryName)) - { - entryName = entry->Key.ToString(); - } - - var entryValue = entry->Value; - entries[entryName] = entryValue; - - if (bigEntryValue < entryValue) - { - bigEntryValue = entryValue; - bigEntryName = entryName; - } - } - - var entryConstant = $"{name}::{name}_MAX"; - if (entries.TryGetValue(entryConstant, out var entryConstantValue) && entryConstantValue == bigEntryValue) - { - entries.Remove(entryConstant); + Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput Folder: {Context.DumpDirectory}"); } - - if (bigEntryValue == 256 && bigEntryName.EndsWith("_MAX")) entries.Remove(bigEntryName); - - var underlyingType = knownType ?? bigEntryValue switch - { - <= byte.MaxValue => "byte", - <= short.MaxValue => "short", - <= int.MaxValue => "int", - <= long.MaxValue => "long", - }; - - bool hasNegativeValue = entries.Any(x => x.Value < 0); - if (underlyingType == "byte" && hasNegativeValue) underlyingType = "sbyte"; - if (underlyingType != "byte" && !hasNegativeValue && !underlyingType.StartsWith('u')) underlyingType = $"u{underlyingType}"; - - _UEnumDefinitions[name] = new(name, underlyingType, entries); - } - - private PropertyDefinition[] ResolveProperties(IEnumerable propLink, int SuperStructEnd) - { - var props = new List(); - foreach (var prop in propLink) - { - // Stop when we find a property with an offset lower than the size of the super struct. - // PropertyLink contains a list of fields declared by object in ascending order, then from it's super - // object and so on. - if (prop.Offset_Internal < SuperStructEnd) - break; - var newProp = ResolveProperty(prop); - var numSameName = props.Count(x => x.Name.StartsWith(newProp.Name)); // Compare against sanitized name, since that's what props are using. - - // Handle multiple properties with the same name. - if (numSameName == 0) - { - props.Add(newProp); - } - else - { - props.Add(newProp with { Name = $"{newProp.Name}_{numSameName + 1}" }); - } - } - - return props.ToArray(); - } - - private PropertyDefinition ResolveProperty(IFProperty prop) - { - var (name, size, offset, _) = GetBaseInfo(prop); - if (name == "bool" || name == "float") name += '_'; - - var resProp = new PropertyDefinition(SanitizeName(name), size, offset, GetPropertyTypeNameLazy(prop)); - return resProp; - } - - /// - /// Gets the property type name, such as byte for ByteProperty. - /// This is done lazily since we only have the internal names in - /// when we want their C++/C# equivalent, which isn't known until object registrations finish. - /// - /// - /// C++/C# property type name. - private Func GetPropertyTypeNameLazy(IFProperty prop) - { - var className = prop.ClassPrivate.Name; - switch (className) - { - case "BoolProperty": - return () => "bool"; - case "ByteProperty": - var byteProp = _factory.Cast(prop); - if (byteProp.Enum != null) - { - var byteEnumName = byteProp.Enum.NamePrivate.ToString(); - GenerateEnumDefinition(_factory.Cast(byteProp.Enum), "byte"); - return () => SanitizeName(byteEnumName); - } - - return () => "byte"; - case "Int8Property": - return () => "byte"; - case "Int16Property": - return () => "short"; - case "UInt16Property": - return () => "ushort"; - case "IntProperty": - return () => "int"; - case "UInt32Property": - return () => "uint"; - case "Int64Property": - return () => "long"; - case "UInt64Property": - return () => "ulong"; - case "FloatProperty": - return () => "float"; - case "DoubleProperty": - return () => "double"; - case "NameProperty": - return () => "FName"; - case "StrProperty": - return () => "FString"; - case "TextProperty": - return () => "FText"; - case "DataTableRowHandle": - return () => "FDataTableRowHandle"; - case "DelegateProperty": - return () => "FScriptDelegate"; - case "MulticastInlineDelegateProperty": - case "MulticastSparseDelegateProperty": - return () => "FMulticastScriptDelegate"; - case "WeakObjectProperty": - return () => "FWeakObjectPtr"; - case "ObjectProperty": - var objPropType = _factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => SanitizeName(_UStructDefinitions.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : $"{objPropType}*"); - case "SoftObjectProperty": - var softObjPropType = _factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => SanitizeName(_UStructDefinitions.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.DisplayName}>" : $"TSoftObjectPtr<{softObjPropType}>"); - case "SoftClassProperty": - var softClassPropType = _factory.Cast(prop).MetaClass.NamePrivate.ToString(); - return () => SanitizeName(_UStructDefinitions.TryGetValue(softClassPropType, out var knownStruct) ? $"TSoftClassPtr<{knownStruct.DisplayName}>" : $"TSoftClassPtr<{softClassPropType}>"); - case "StructProperty": - var structPropType = _factory.Cast(prop).Struct.NamePrivate.ToString(); - return () => SanitizeName(_UStructDefinitions.TryGetValue(structPropType, out var knownStruct) ? knownStruct.DisplayName : structPropType); - case "ClassProperty": - case "ClassPtrProperty": - var classPropClass = _factory.Cast(prop).MetaClass; - var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass*"; - return () => SanitizeName(_UStructDefinitions.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}*" : classPropType); - case "EnumProperty": - var enumProp = _factory.Cast(prop); - var enumPropName = enumProp.Enum.NamePrivate.ToString(); - GenerateEnumDefinition(enumProp.Enum, _classes.GetPropertyTypeName(enumProp.UnderlyingProp)); - return () => SanitizeName(enumPropName); - case "MapProperty": - var mapProp = _factory.Cast(prop); - var mapPropKeyType = _classes.GetPropertyTypeName(mapProp.KeyProp); - var mapPropValueType = _classes.GetPropertyTypeName(mapProp.ValueProp); - return () => - { - var isKeyPtr = mapPropKeyType.EndsWith('*') || mapPropKeyType.Contains('<'); // Use nint for pointers and generic types. - var isValuePtr = mapPropValueType.EndsWith('*') || mapPropValueType.Contains('<'); - - _UStructDefinitions.TryGetValue(mapPropKeyType.TrimEnd('*'), out var knownKeyStruct); - _UStructDefinitions.TryGetValue(mapPropValueType.TrimEnd('*'), out var knownValueStruct); - - var keyName = SanitizeName(knownKeyStruct?.DisplayName ?? mapPropKeyType); - var valueName = SanitizeName(knownValueStruct?.DisplayName ?? mapPropValueType); - - var keyType = isKeyPtr ? $"Ptr<{keyName}>" : keyName; - var valueType = isValuePtr ? $"Ptr<{valueName}>" : valueName; - - return $"TMap<{keyType}, {valueType}>"; - }; - case "InterfaceProperty": - var intPropType = _factory.Cast(prop).InterfaceClass.NamePrivate.ToString(); - return () => SanitizeName(_UStructDefinitions.TryGetValue(intPropType, out var knownStruct) ? $"TScriptInterface<{knownStruct.DisplayName}>" : $"TScriptInterface<{intPropType}>"); - case "ArrayProperty": - var arrayPropType = _classes.GetPropertyTypeName(_factory.Cast(prop).Inner); - return () => - { - var isPtrType = arrayPropType.EndsWith('*') || arrayPropType.Contains('<'); // Use nint for pointers and generic types. - _UStructDefinitions.TryGetValue(arrayPropType.TrimEnd('*'), out var knownStruct); - var arrTypeSanitized = SanitizeName(knownStruct?.DisplayName ?? arrayPropType); - return isPtrType ? - $"TArray>" : - $"TArray<{arrTypeSanitized}>"; - }; - case "SetProperty": - var setPropType = _classes.GetPropertyTypeName(_factory.Cast(prop).ElementProp); - return () => - { - var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; - _UStructDefinitions.TryGetValue(setPropType.TrimEnd('*'), out var knownStruct); - return isPtrType - ? $"TSet /* TSet<{setPropType}> */" - : $"TSet<{SanitizeName(knownStruct?.DisplayName ?? setPropType)}>"; - }; - case "OptionalProperty": - var optionalType = _classes.GetPropertyTypeName(_factory.Cast(prop).ValueProperty); - return () => - { - if (_UStructDefinitions.TryGetValue(optionalType, out var knownOptType)) - optionalType = knownOptType.DisplayName; - - return $"TOptional<{SanitizeName(optionalType)}>"; - }; - case "FieldPathProperty": - return () => "FFieldPath"; - case "LazyObjectProperty": - var lazyObjType = _factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => - { - if (_UStructDefinitions.TryGetValue(lazyObjType, out var knownLazyType)) - lazyObjType = knownLazyType.DisplayName; - - return $"TLazyObjectPtr<{SanitizeName(lazyObjType)}>"; - }; - case "Utf8StrProperty": - return () => "FUtf8String"; - case "AnsiStrProperty": - return () => "FAnsiString"; - default: - Log.Warning($"Unknown Property: {className}"); - return () => className; - } - } - - private (string Name, int Size, int Offset, string ClassName) GetBaseInfo(IFProperty prop) - { - var name = SanitizeName(prop.NamePrivate); - var size = prop.ElementSize; - var offset = prop.Offset_Internal; - var className = prop.ClassPrivate.Name; - return (name, size, offset, className); - } - - private static string SanitizeName(string name) - { - var parts = name.Split('_'); - if (parts.Last().Length == 32 && parts.Length > 2) - { - name = string.Join(string.Empty, parts[..^2]); - } - - name = name.Replace(' ', '_'); - name = name.Replace('-', '_'); - name = name.Replace('/', '_'); - name = name.Replace("?", string.Empty); - name = name.Replace("&", string.Empty); - name = name.Replace('(', '_'); - name = name.Replace(')', '_'); - name = name.Replace('[', '_'); - name = name.Replace(']', '_'); - if (name == "object") name = "_object"; - - if (char.IsDigit(name[0])) - { - name = '_' + name; - } - - return name; - } - - private interface ICsharpText - { - string Name { get; } - - string GetCsharpText(); } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/DumperEx.cs b/UE.Toolkit.DumperMod/DumperEx.cs deleted file mode 100644 index 99e5a71..0000000 --- a/UE.Toolkit.DumperMod/DumperEx.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Diagnostics; -using System.Text; -using UE.Toolkit.Core.Types.Unreal.Factories; -using UE.Toolkit.DumperMod.Definitions; -using UE.Toolkit.Interfaces; -using UnrealEssentials.Interfaces; - -namespace UE.Toolkit.DumperMod; - -public class DumperEx( - IUnrealFactory factory, - IUnrealObjects uobjs, - IUnrealStrings strs, - IUnrealClasses classes, - string dumpDir, - IUnrealEssentials essentials) -{ - private Context Context = new(factory, uobjs, strs, classes, dumpDir, essentials); - - public void DumpObjects() - { - Log.Information("Dumping objects..."); - - var sw = new Stopwatch(); - sw.Start(); - Context.Registry.Register(); - - StringBuilder? sb = null; - if (Mod.Config.Mode == DumpFileMode.SingleFile) - { - sb = new(); - Context.Builtins.AddHeader(sb); - Context.Builtins.AddBaseObjectDefinition(sb); - } - else - { - var outputFile = Path.Join(Context.DumpDirectory, "UObjectImpl.cs"); - var implWriter = new StringBuilder(); - Context.Builtins.AddHeader(implWriter); - Context.Builtins.AddBaseObjectDefinition(implWriter); - File.WriteAllText(outputFile, implWriter.ToString()); - } - - var numDumped = 0; - - foreach (var (_, Definition) in Context.Registry.Structs) - { - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - var outputFile = Path.Join(Context.DumpDirectory, $"{Definition.DisplayName}.cs"); - File.WriteAllText(outputFile, Definition.Serialize(Context)); - } - else sb?.AppendLine(Definition.Serialize(Context)); - numDumped++; - } - - foreach (var (Name, Definition) in Context.Registry.Enums) - { - if (Mod.Config.Mode == DumpFileMode.FilePerType) - { - var outputFile = Path.Join(Context.DumpDirectory, $"{Name}"); - File.WriteAllText(outputFile, Definition.Serialize(Context)); - } - else sb?.AppendLine(Definition.Serialize(Context)); - numDumped++; - } - - sw.Stop(); - if (Mod.Config.Mode == DumpFileMode.SingleFile) - { - var singleFileOutput = string.IsNullOrEmpty(Mod.Config.SingleFileOutputName) - ? Path.Join(Context.DumpDirectory, "Types.cs") - : Path.Join(Context.DumpDirectory, $"{Mod.Config.SingleFileOutputName.Replace(".cs", string.Empty)}.cs"); - - File.WriteAllText(singleFileOutput, sb!.ToString()); - Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput File: {singleFileOutput}"); - } - else - { - Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput Folder: {Context.DumpDirectory}"); - } - } -} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Mod.cs b/UE.Toolkit.DumperMod/Mod.cs index 5a22aa2..765bf65 100644 --- a/UE.Toolkit.DumperMod/Mod.cs +++ b/UE.Toolkit.DumperMod/Mod.cs @@ -19,7 +19,7 @@ public class Mod : ModBase public static Config Config = null!; private readonly IModConfig _modConfig; - private readonly DumperEx _dumper; + private readonly Dumper _dumper; public Mod(ModContext context) { diff --git a/UE.Toolkit.Interfaces/ITypeReflection.cs b/UE.Toolkit.Interfaces/ITypeReflection.cs index 6119f96..58e849e 100644 --- a/UE.Toolkit.Interfaces/ITypeReflection.cs +++ b/UE.Toolkit.Interfaces/ITypeReflection.cs @@ -1,4 +1,5 @@ -using UE.Toolkit.Core.Types.Unreal.Common; +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; namespace UE.Toolkit.Interfaces; @@ -6,26 +7,8 @@ namespace UE.Toolkit.Interfaces; /// /// API for obtaining special type reflection info which cannot be obtained entirely through the runtime reflection system. /// -public interface ITypeReflection +public interface ITypeReflection : ITypeReflectionInternal { - - #region FText - - /// - /// Get the FText type used by the currently running version of the engine. - /// UE 5.4 and later have a significantly different FText implement compared to earlier versions. - /// - /// Type information for the FText. - Type GetFText(); - /// - /// Get the size of the FText type used by the currently running version of the engine. - /// UE 5.4 and later have a significantly different FText implement compared to earlier versions. - /// - /// Size of FText. - int GetFTextSize(); - - #endregion - #region FSoftObjectPath /// diff --git a/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs b/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs index 099dc2e..26f11f5 100644 --- a/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs +++ b/UE.Toolkit.Reloaded/Unreal/UnrealMethods.cs @@ -338,25 +338,8 @@ internal unsafe void CallProcessEvent(nint Object, IUFunction Function, nint All ProcessEventWrapper(Object, Function.Ptr, Alloc); } - internal unsafe IFunctionParam CreateReturnParam(IFProperty property) - { - return property.ClassPrivate.Name switch - { - "Int8Property" => new Int8Param(new((byte*)Memory.Malloc(sizeof(byte))), Memory), - "Int16Property" => new Int16Param(new((short*)Memory.Malloc(sizeof(short))), Memory), - "Int32Property" => new Int32Param(new((int*)Memory.Malloc(sizeof(int))), Memory), - "IntProperty" => new IntParam(new((int*)Memory.Malloc(sizeof(int))), Memory), - "Int64Property" => new Int64Param(new((long*)Memory.Malloc(sizeof(long))), Memory), - "UInt16Property" => new UInt16Param(new((ushort*)Memory.Malloc(sizeof(ushort))), Memory), - "UInt32Property" => new UInt32Param(new((uint*)Memory.Malloc(sizeof(uint))), Memory), - "UInt64Property" => new UInt64Param(new((ulong*)Memory.Malloc(sizeof(ulong))), Memory), - "FloatProperty" => new FloatParam(new((float*)Memory.Malloc(sizeof(float))), Memory), - "DoubleProperty" => new DoubleParam(new((double*)Memory.Malloc(sizeof(double))), Memory), - "NameProperty" => new NameParam(new((FName*)Memory.Malloc(sizeof(FName))), Memory), - "ObjectProperty" => new ObjectParam(new((nint*)Memory.Malloc(sizeof(nint))), Memory), - _ => throw new NotSupportedException($"CreateReturnParam with property {property.ClassPrivate.Name}") - }; - } + internal IFunctionParam CreateReturnParam(IFProperty property) + => FunctionParamFactory.CreateParam(property, Factory, Classes, Memory); #region Unreal Toolkit API References From d6520d885a85fad939b9d31c81522b3f9a18a5ff Mon Sep 17 00:00:00 2001 From: Rirurin Date: Thu, 23 Jul 2026 20:14:47 +0800 Subject: [PATCH 05/10] Dumper: Generate classes with methods --- README.md | 2 +- .../Types/Interfaces/IFunctionParam.cs | 11 ++ .../Common/FunctionParam/AnsiStringParam.cs | 7 + .../Unreal/Common/FunctionParam/ArrayParam.cs | 7 + .../Unreal/Common/FunctionParam/ByteParam.cs | 6 + .../Unreal/Common/FunctionParam/EnumParam.cs | 41 ++++++ .../Common/FunctionParam/InterfaceParam.cs | 7 + .../Unreal/Common/FunctionParam/MapParam.cs | 7 + .../Unreal/Common/FunctionParam/SetParam.cs | 7 + .../Common/FunctionParam/SoftClassParam.cs | 7 + .../Common/FunctionParam/SoftObjectParam.cs | 7 + .../Common/FunctionParam/StringParam.cs | 17 +-- .../Common/FunctionParam/Utf8StringParam.cs | 7 + .../Unreal/Factories/UE5_6_1/UnrealFactory.cs | 21 ++- .../Unreal/Factories/UE5_7_4/UnrealFactory.cs | 2 +- .../Types/Unreal/UE5_4_4/FString.cs | 3 +- .../Types/Unreal/UE5_6_1/FAnsiString.cs | 23 ++++ .../Types/Unreal/UE5_6_1/FUtf8String.cs | 23 ++++ UE.Toolkit.DumperMod/Config.cs | 4 + UE.Toolkit.DumperMod/Definitions/Builtins.cs | 22 ++++ UE.Toolkit.DumperMod/Definitions/Class.cs | 12 +- UE.Toolkit.DumperMod/Definitions/Function.cs | 123 +++++++++++++----- UE.Toolkit.DumperMod/Definitions/Property.cs | 72 +++++++--- UE.Toolkit.DumperMod/Definitions/Struct.cs | 2 +- 24 files changed, 358 insertions(+), 82 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/AnsiStringParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ArrayParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ByteParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/EnumParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/InterfaceParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MapParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SetParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftClassParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftObjectParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Utf8StringParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/UE5_6_1/FAnsiString.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/UE5_6_1/FUtf8String.cs diff --git a/README.md b/README.md index c64308a..2198da5 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ | Object Logging |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Object Editing |✅|❔|❔|✅|❔|✅|❔|❔|❔ | `FMemory` Functions |✅|✅|✅|✅|✅|✅|✅|✅|✅ -| Dumper |⚠️|⚠️|⚠️|⚠️|⚠️️️️️️|⚠️|⚠️|⚠️️|⚠️️️️️️ +| Dumper |✅|✅|✅|✅|✅|✅|✅|✅|⚠️️️️️️ | Property Editing (Object XML) |✅|❔|❔|✅|❔|✅|❔|❔|❔ | Add List Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ | Add Map Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ diff --git a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs index cb7f56c..56586bb 100644 --- a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs +++ b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs @@ -2,6 +2,7 @@ using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UE.Toolkit.Core.Types.Unreal.UE5_6_1; namespace UE.Toolkit.Core.Types.Interfaces; @@ -75,6 +76,7 @@ public static unsafe IFunctionParam CreateParam(IFProperty property, IUnrealFact { "BoolProperty" => CreateBoolParam(factory.CreateFBoolProperty(property.Ptr), Memory), "Int8Property" => new Int8Param(new((byte*)(Memory?.Malloc(sizeof(byte)) ?? nint.Zero)), Memory), + "ByteProperty" => new ByteParam(new((byte*)(Memory?.Malloc(sizeof(byte)) ?? nint.Zero)), Memory), "Int16Property" => new Int16Param(new((short*)(Memory?.Malloc(sizeof(short)) ?? nint.Zero)), Memory), "Int32Property" => new Int32Param(new((int*)(Memory?.Malloc(sizeof(int)) ?? nint.Zero)), Memory), "IntProperty" => new IntParam(new((int*)(Memory?.Malloc(sizeof(int)) ?? nint.Zero)), Memory), @@ -90,6 +92,15 @@ public static unsafe IFunctionParam CreateParam(IFProperty property, IUnrealFact "TextProperty" => new TextParam(Memory?.Malloc(reflection.GetFTextSize()) ?? nint.Zero, reflection.GetFTextSize(), Memory), "ObjectProperty" or "ClassProperty" or "ClassPtrProperty" => new ObjectParam(new((nint*)(Memory?.Malloc(sizeof(nint)) ?? nint.Zero)), Memory), + "ArrayProperty" => new ArrayParam(new((TArray*)(Memory?.Malloc(sizeof(TArray)) ?? nint.Zero)), Memory), + "EnumProperty" => new EnumParam(new(Memory?.Malloc(property.ElementSize) ?? nint.Zero), property.ElementSize, Memory), + "MapProperty" => new MapParam(new((TMap*)(Memory?.Malloc(sizeof(TMap)) ?? nint.Zero)), Memory), + "SetProperty" => new SetParam(new((TSet*)(Memory?.Malloc(sizeof(TSet)) ?? nint.Zero)), Memory), + "InterfaceProperty" => new InterfaceParam(new((TScriptInterface*)(Memory?.Malloc(sizeof(TScriptInterface)) ?? nint.Zero)), Memory), + "SoftClassProperty" => new SoftClassParam(new((TSoftClassPtr*)(Memory?.Malloc(sizeof(TSoftClassPtr)) ?? nint.Zero)), Memory), + "SoftObjectProperty" => new SoftObjectParam(new((TSoftObjectPtr*)(Memory?.Malloc(sizeof(TSoftObjectPtr)) ?? nint.Zero)), Memory), + "Utf8StrProperty" => new Utf8StringParam(new((FUtf8String*)(Memory?.Malloc(sizeof(FUtf8String)) ?? nint.Zero)), Memory), + "AnsiStrProperty" => new AnsiStringParam(new((FAnsiString*)(Memory?.Malloc(sizeof(FAnsiString)) ?? nint.Zero)), Memory), _ => throw new NotSupportedException($"CreateParam with property {property.ClassPrivate.Name}") }; } diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/AnsiStringParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/AnsiStringParam.cs new file mode 100644 index 0000000..901f425 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/AnsiStringParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_6_1; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class AnsiStringParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "AnsiStrProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ArrayParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ArrayParam.cs new file mode 100644 index 0000000..02bee07 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ArrayParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class ArrayParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "ArrayProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ByteParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ByteParam.cs new file mode 100644 index 0000000..23c2e6f --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/ByteParam.cs @@ -0,0 +1,6 @@ +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class ByteParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "ByteProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/EnumParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/EnumParam.cs new file mode 100644 index 0000000..d59aa13 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/EnumParam.cs @@ -0,0 +1,41 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class EnumParam(nint ptr, int size, IUnrealMemoryInternal? memory = null) : IFunctionParam, IDisposable +{ + public string PropertyType => "EnumProperty"; + + private int Size => size; + + private nint Ptr => ptr; + + private IUnrealMemoryInternal? Memory => memory; + + private unsafe void Copy(nint source, nint destination) + => NativeMemory.Copy((void*)source, (void*)destination, (nuint)Size); + + public void Write(nint Destination) => Copy(Ptr, Destination); + public void Read(nint Destination) => Copy(Destination, Ptr); + + private bool IsDisposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!IsDisposed) + { + if (disposing) { } + Memory?.Free(Ptr); + IsDisposed = true; + } + } + + ~EnumParam() => Dispose(false); +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/InterfaceParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/InterfaceParam.cs new file mode 100644 index 0000000..17ecfb7 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/InterfaceParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class InterfaceParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "InterfaceProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MapParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MapParam.cs new file mode 100644 index 0000000..7920cbb --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MapParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class MapParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "MapProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SetParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SetParam.cs new file mode 100644 index 0000000..adf5203 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SetParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class SetParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "SetProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftClassParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftClassParam.cs new file mode 100644 index 0000000..7c30737 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftClassParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class SoftClassParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "SoftClassProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftObjectParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftObjectParam.cs new file mode 100644 index 0000000..6a49269 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/SoftObjectParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class SoftObjectParam(Ptr> rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable>(rvalue, "SoftObjectProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs index bd59c12..7d8b541 100644 --- a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/StringParam.cs @@ -4,19 +4,4 @@ namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; public class StringParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) - : FunctionParamCopyable(rvalue, "StrProperty", memory) -{ - protected override void Dispose(bool disposing) - { - if (!IsDisposed) - { - if (disposing) { } - - unsafe - { - // Memory?.Free((nint)RValue.Value->Data.AllocatorInstance); - Memory?.Free((nint)RValue.Value); - } - } - } -} \ No newline at end of file + : FunctionParamCopyable(rvalue, "StrProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Utf8StringParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Utf8StringParam.cs new file mode 100644 index 0000000..a84e284 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/Utf8StringParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_6_1; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class Utf8StringParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "Utf8StrProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs index f0ca8b0..2ceb828 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs @@ -62,7 +62,7 @@ public override nint SizeOf() public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); public override IUStruct CreateUStruct(nint ptr) => new UStruct_UE5_6_1(ptr, this, Memory); public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_6_1(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_4_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); @@ -150,4 +150,23 @@ public IUObject? ClassDefaultObject public nint Constructor => _self->ClassConstructor; public EClassFlags ClassFlags => _self->ClassFlags; public EClassCastFlags ClassCastFlags => _self->ClassCastFlags; +} + +public unsafe class UFunction_UE5_6_1(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_6_1(ptr, factory, memory), IUFunction +{ + private readonly UFunction* _self = (UFunction*)ptr; + + public EFunctionFlags FunctionFlags => _self->FunctionFlags; + public int ParamCount => _self->NumParms; + public int ParamSize => _self->ParmsSize; + public int ReturnValueOffset => _self->ReturnValueOffset; + + public int GetTotalParameterSize() + { + var LastProperty = ChildProperties.Any() ? _factory.CreateFProperty(ChildProperties.Last().Ptr) : null; + return LastProperty != null ? LastProperty.Offset_Internal + LastProperty.ElementSize : 0; + } + + public nint FunctionPtr => (nint)_self->Func; } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs index ad0f10b..31bf440 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs @@ -63,7 +63,7 @@ public override nint SizeOf() public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); public override IUStruct CreateUStruct(nint ptr) => new UE5_6_1.UStruct_UE5_6_1(ptr, this, Memory); public override IUUserDefinedEnum CreateUUserDefinedEnum(nint ptr) => new UUserDefinedEnum_UE5_4_4(ptr, this, Memory); - public override IUFunction CreateUFunction(nint ptr) => new UFunction_UE5_4_4(ptr, this, Memory); + public override IUFunction CreateUFunction(nint ptr) => new UE5_6_1.UFunction_UE5_6_1(ptr, this, Memory); public override IFFieldClass CreateFFieldClass(nint ptr) => new FFieldClass_UE5_7_4(ptr, this); public override IFField CreateFField(nint ptr) => new FField_UE5_4_4(ptr, this); public override IFFieldVariant CreateFFieldVariant(nint ptr) => new FFieldVariantUE5_4_4(ptr, this); diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs index 5bed2e0..93bad21 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs @@ -5,8 +5,7 @@ namespace UE.Toolkit.Core.Types.Unreal.UE5_4_4; [StructLayout(LayoutKind.Sequential)] public unsafe struct FString : IMapHashable { - - private static uint[] CRC_HASH = + public static uint[] CRC_HASH = [ 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FAnsiString.cs b/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FAnsiString.cs new file mode 100644 index 0000000..559416d --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FAnsiString.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using System.Text; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.UE5_6_1; + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct FAnsiString : IMapHashable +{ + public TArray Data; + + public override string ToString() + => Data.ArrayNum > 0 ? Marshal.PtrToStringAnsi((nint)Data.AllocatorInstance, Data.ArrayNum - 1) : string.Empty; + + public uint GetTypeHash() + { + var Bytes = Encoding.ASCII.GetBytes(ToString()); + uint Hash = 0; + foreach (var Byte in Bytes) + Hash = ((Hash >> 8) & 0xFFFFFF) ^ FString.CRC_HASH[(Hash ^ Byte) & 0xFF]; + return Hash; + } +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FUtf8String.cs b/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FUtf8String.cs new file mode 100644 index 0000000..e83c518 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE5_6_1/FUtf8String.cs @@ -0,0 +1,23 @@ +using System.Runtime.InteropServices; +using System.Text; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.UE5_6_1; + +[StructLayout(LayoutKind.Sequential)] +public unsafe struct FUtf8String : IMapHashable +{ + public TArray Data; + + public override string ToString() + => Data.ArrayNum > 0 ? Marshal.PtrToStringUTF8((nint)Data.AllocatorInstance, Data.ArrayNum - 1) : string.Empty; + + public uint GetTypeHash() + { + var Bytes = Encoding.UTF8.GetBytes(ToString()); + uint Hash = 0; + foreach (var Byte in Bytes) + Hash = ((Hash >> 8) & 0xFFFFFF) ^ FString.CRC_HASH[(Hash ^ Byte) & 0xFF]; + return Hash; + } +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Config.cs b/UE.Toolkit.DumperMod/Config.cs index 0a92b67..b6a9ee7 100644 --- a/UE.Toolkit.DumperMod/Config.cs +++ b/UE.Toolkit.DumperMod/Config.cs @@ -31,6 +31,10 @@ public class Config : Configurable [DisplayName("Single-File File Name")] [DefaultValue("")] public string SingleFileOutputName { get; set; } = string.Empty; + + [DisplayName("Dump Functions")] + [DefaultValue(true)] + public bool DumpFunctions { get; set; } = true; } public enum DumpFileMode diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs index ed15896..40bbc19 100644 --- a/UE.Toolkit.DumperMod/Definitions/Builtins.cs +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -25,6 +25,12 @@ private List GetDefaultUsings() // FText definition is different for versions below UE 5.4 if (int.Parse(VerParts[0]) < 5 || int.Parse(VerParts[1]) < 4) Usings.Add("FText = UE.Toolkit.Core.Types.Unreal.UE4_27_2.FText"); + // Added FAnsiString and FUtf8String for UE 5.6+ + if (int.Parse(VerParts[0]) == 5 && int.Parse(VerParts[1]) >= 6) + { + Usings.Add("FAnsiString = UE.Toolkit.Core.Types.Unreal.UE5_6_1.FAnsiString"); + Usings.Add("FUtf8String = UE.Toolkit.Core.Types.Unreal.UE5_6_1.FUtf8String"); + } return Usings; } @@ -100,6 +106,8 @@ public static string SanitizeName(string name) name = name.Replace(']', '_'); //name = name.Replace('>', '_'); if (name == "object") name = "_object"; + + // name = SanitizeForParam(name); if (char.IsDigit(name[0])) { @@ -108,4 +116,18 @@ public static string SanitizeName(string name) return name; } + + // Extra sanitization step to prevent C# treating a parameter named after a primitive data type as a data type + public static string SanitizeForTypename(string name) + { + return name switch + { + "base" => "Base", + "bool" => "Bool", + "float" => "Float", + "ref" => "Ref", + "int" => "Int", + _ => name + }; + } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Class.cs b/UE.Toolkit.DumperMod/Definitions/Class.cs index bd0bb0a..f30fcc5 100644 --- a/UE.Toolkit.DumperMod/Definitions/Class.cs +++ b/UE.Toolkit.DumperMod/Definitions/Class.cs @@ -8,11 +8,11 @@ public class ClassFactory(Context context, ObjectType objectType, IUClass uclass { public override void Register() { var innerName = uclass.NamePrivate.ToString(); - var displayName = uclass.GetNativeName(); + // TODO: Fix GetNativeName on UE4 to retrieve AActor attribute from classes + var displayName = (uclass.ClassCastFlags.HasFlag(EClassCastFlags.AActor) ? "A" : "U") + innerName; var size = uclass.PropertiesSize; var alignment = uclass.MinAlignment; - // TODO: Flag stuff? var super = uclass.GetSuperClass(); var superSize = super?.PropertiesSize ?? 0; var superName = super?.NamePrivate.ToString(); @@ -22,11 +22,9 @@ public override void Register() { var propClass = new PropertyClassFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); var propStruct = new PropertyStructFactory(Context).ResolveProperties(uclass.PropertyLink, superSize); - // TODO - // var functions = new FunctionFactory(Context).ResolveFunctions(uclass); - + var functions = Mod.Config.DumpFunctions ? new FunctionFactory(Context).ResolveFunctions(uclass) : []; Context.Registry.Structs[innerName] = - new ClassDefinition(innerName, displayName, size, alignment, propClass, propStruct, [], superName); + new ClassDefinition(innerName, displayName, size, alignment, propClass, propStruct, functions, superName); } } @@ -35,7 +33,7 @@ public class ClassFactoryStatic(Context context, ObjectType objectType, IUClass { public override void Register() { var innerName = uclass.NamePrivate.ToString(); - var displayName = uclass.GetNativeName(); + var displayName = (uclass.ClassCastFlags.HasFlag(EClassCastFlags.AActor) ? "A" : "U") + innerName; var size = uclass.PropertiesSize; var alignment = uclass.MinAlignment; diff --git a/UE.Toolkit.DumperMod/Definitions/Function.cs b/UE.Toolkit.DumperMod/Definitions/Function.cs index 07ab9a9..ea3df63 100644 --- a/UE.Toolkit.DumperMod/Definitions/Function.cs +++ b/UE.Toolkit.DumperMod/Definitions/Function.cs @@ -5,61 +5,111 @@ namespace UE.Toolkit.DumperMod.Definitions; -public abstract class ParameterDefinition(string name, Func propTypeName, bool isReference) : ISerializable +public abstract class ParameterDefinition(string name, Func propTypeName, IFProperty meta) : ISerializable { public string Name { get; } = name; public Func PropTypeName { get; } = propTypeName; - public bool IsReference { get; } = isReference; + public IFProperty Meta { get; } = meta; + + public bool IsReference => Meta.PropertyFlags.HasFlag(EPropertyFlags.CPF_OutParm); public abstract string Serialize(Context context); } -public class ParameterHeadDefinition(string name, Func propTypeName, bool isReference) - : ParameterDefinition(name, propTypeName, isReference) +public class ParameterHeadDefinition(string name, Func propTypeName, IFProperty meta) + : ParameterDefinition(name, propTypeName, meta) { public override string Serialize(Context context) => $"{(IsReference ? "ref" : string.Empty)} {PropTypeName()} {Name}"; } -public class ParameterBodyDefinition(string name, Func propTypeName, bool isReference, string? paramTypeName, bool isLast) - : ParameterDefinition(name, propTypeName, isReference) +public class ParameterBodyDefinition(string name, Func propTypeName, IFProperty meta, bool isLast) + : ParameterDefinition(name, propTypeName, meta) { - public string? ParamTypeName => paramTypeName; public bool IsLast { get; set; } = isLast; public override string Serialize(Context context) { var typeName = propTypeName(); - if (ParamTypeName == null) + var paramTypeName = FunctionParamFactory.GetParamNameFromProperty(Meta, context.Factory, context.Classes); + if (paramTypeName == null) return $"// PARAM TODO {typeName}"; + typeName = Meta.ClassPrivate.Name switch { - return $"// PARAM TODO {typeName}"; + "ArrayProperty" => "TArray", + "MapProperty" => "TMap", + "SetProperty" => "TSet", + "ByteProperty" => "byte", + "InterfaceProperty" => "TScriptInterface", + "SoftClassProperty" => "TSoftClassPtr", + "SoftObjectProperty" => "TSoftObjectPtr", + _ => typeName + }; + var next = IsLast ? string.Empty : ","; + if (Meta.ClassPrivate.Name is "ObjectProperty" or "ClassProperty" or "ClassPtrProperty") + return $"new {paramTypeName}(new(&{Name + "_Ptr"})){next}"; + var ptrCast = IsReference ? $"({typeName}*)Unsafe.AsPointer(ref {Name})" : $"&{Name}"; + if (!IsReference) + { + ptrCast = Meta.ClassPrivate.Name switch + { + "ByteProperty" => $"(byte*)(&{Name})", + "ArrayProperty" => $"(TArray*)(&{Name})", + "MapProperty" => $"(TMap*)(&{Name})", + "SetProperty" => $"(TSet*)(&{Name})", + "InterfaceProperty" => $"(TScriptInterface*)(&{Name})", + "SoftClassProperty" => $"(TSoftClassPtr*)(&{Name})", + "SoftObjectProperty" => $"(TSoftObjectPtr*)(&{Name})", + _ => ptrCast + }; } - var ptrCast = IsReference ? $"&{Name}" : $"({typeName}*)Unsafe.AsPointer(ref {Name})"; - var paramDecl = ParamTypeName switch + var paramDecl = Meta.ClassPrivate.Name switch { - _ => $"new {ParamTypeName}(new({ptrCast}))" + "BoolProperty" => $"new {paramTypeName}(new({ptrCast}), {context.Factory.CreateFBoolProperty(Meta.Ptr).FieldMask})", + "StructProperty" => $"new {paramTypeName}(new({ptrCast}), {context.Factory.CreateFStructProperty(Meta.Ptr).Struct.PropertiesSize})", + "EnumProperty" => $"new {paramTypeName}(new({ptrCast}), {Meta.ElementSize})", + "TextProperty" => $"new {paramTypeName}(new({ptrCast}), {context.Classes.GetFTextSize()})", + _ => $"new {paramTypeName}(new({ptrCast}))" }; - return $"{paramDecl}{(IsLast ? string.Empty : ",")}"; + return $"{paramDecl}{next}"; } } -public class ReturnValueDefinition(string? paramTypeName) : ISerializable +public class ReturnValueDefinition(Func typeName, string metaName, string? paramTypeName) : ISerializable { + public Func TypeName => typeName; + public string MetaName => metaName; public string? ParamTypeName => paramTypeName; - public string Serialize(Context context) + private string CreateStructPropertyReturn() { - if (ParamTypeName == null) - { - return $"return null; // RETURN VALUE TODO {ParamTypeName}"; - } - var ConstructValue = ParamTypeName switch + var Result = $"{TypeName()} ReturnValue;"; + Result += $"\n\t\t(({ParamTypeName}?)Return)!.Write((nint)(&ReturnValue));"; + Result += "\n\t\treturn ReturnValue;"; + return Result; + } + + private string CreateSingleLineReturn() + { + var ConstructValue = MetaName switch { "ObjectProperty" or "ClassProperty" or "ClassPtrProperty" - => $"new(Inner.GetFactory().GetUObject((({ParamTypeName})Return).Value))", - _ => $"(({ParamTypeName})Return).Value" + => $"new(Inner.GetFactory().CreateUObject((({ParamTypeName}?)Return)!.Value))", + "ByteProperty" => $"({TypeName()})((({ParamTypeName}?)Return)!.Value)", + _ => $"(({ParamTypeName}?)Return)!.Value" }; return $"return {ConstructValue};"; } + + public string Serialize(Context context) + { + if (ParamTypeName == null) return $"return null; // RETURN VALUE TODO {TypeName()}"; + return MetaName switch + { + "StructProperty" or "EnumProperty" or "TextProperty" or "ArrayProperty" + or "MapProperty" or "SetProperty" or "InterfaceProperty" + or "SoftClassProperty" or "SoftObjectProperty" => CreateStructPropertyReturn(), + _ => CreateSingleLineReturn() + }; + } } public class FunctionFactory(Context context) @@ -69,10 +119,13 @@ public List ResolveFunctions(IUClass uclass) var PropFactory = new PropertyClassFactory(context); return uclass.GetFunctions() .Where(x => !x.NamePrivate.ToString().StartsWith("ExecuteUbergraph")) + .DistinctBy(x => x.NamePrivate.ToString()) .Select(x => { List HeadParams = []; List BodyParams = []; + List BodyPrefix = []; + List BodyPostfix = []; ReturnValueDefinition? ReturnValue = null; Func? returnTypeName = null; foreach (var Field in x.ChildProperties) @@ -80,26 +133,29 @@ public List ResolveFunctions(IUClass uclass) var Param = context.Factory.CreateFProperty(Field.Ptr); if (Param.PropertyFlags.HasFlag(EPropertyFlags.CPF_ReturnParm)) { - returnTypeName = PropFactory.GetPropTypenameStruct(Param); + returnTypeName = PropFactory.GetPropTypenameFunctionParam(Param); var returnParamName = FunctionParamFactory.GetParamNameFromProperty(Param, context.Factory, context.Classes); - ReturnValue = new(returnParamName); + ReturnValue = new(returnTypeName, Param.ClassPrivate.Name, returnParamName); break; } - var IsReference = Param.PropertyFlags.HasFlag(EPropertyFlags.CPF_OutParm); - var paramCSName = FunctionParamFactory.GetParamNameFromProperty(Param, context.Factory, context.Classes); - var typeName = Builtins.SanitizeName(Param.NamePrivate); - var propTypeName = PropFactory.GetPropTypenameStruct(Param); - HeadParams.Add(new ParameterHeadDefinition(typeName, propTypeName, IsReference )); - BodyParams.Add(new ParameterBodyDefinition(typeName, propTypeName, IsReference, paramCSName, false )); + var paramName = Builtins.SanitizeForTypename(Builtins.SanitizeName(Param.NamePrivate)); + var propTypeName = PropFactory.GetPropTypenameFunctionParam(Param); + HeadParams.Add(new ParameterHeadDefinition(paramName, propTypeName, Param )); + BodyParams.Add(new ParameterBodyDefinition(paramName, propTypeName, Param, false )); + if (Param.ClassPrivate.Name is "ObjectProperty" or "ClassProperty" or "ClassPtrProperty") + { + BodyPrefix.Add($"nint {paramName}_Ptr = {paramName}.Inner.Ptr;"); + BodyPostfix.Add($"{paramName} = new(Inner.GetFactory().CreateUObject({paramName}_Ptr));"); + } } if (BodyParams.Count > 0) BodyParams.Last().IsLast = true; - return new FunctionDefinition(Builtins.SanitizeName(x.NamePrivate.ToString()), returnTypeName, HeadParams, BodyParams, ReturnValue); + return new FunctionDefinition(Builtins.SanitizeName(x.NamePrivate.ToString()), returnTypeName, HeadParams, BodyParams, ReturnValue, BodyPrefix, BodyPostfix); }).ToList(); } } public class FunctionDefinition(string name, Func? returnTypeName, List headParams, - List bodyParams, ReturnValueDefinition? returnParam) + List bodyParams, ReturnValueDefinition? returnParam, List bodyPrefix, List bodyPostfix) : ISerializable { public string Name { get; } = name; @@ -107,6 +163,8 @@ public class FunctionDefinition(string name, Func? returnTypeName, List< public List HeadParams { get; } = headParams; public List BodyParams { get; } = bodyParams; public ReturnValueDefinition? ReturnParam { get; } = returnParam; + public List BodyPrefix { get; } = bodyPrefix; + public List BodyPostfix { get; } = bodyPostfix; public string Serialize(Context context) { @@ -114,6 +172,7 @@ public string Serialize(Context context) var returnType = ReturnTypeName?.Invoke() ?? "void"; sb.AppendLine($"\tpublic unsafe {returnType} {Name}({string.Join(",", HeadParams.Select(x => x.Serialize(context)))})"); sb.AppendLine("\t{"); + foreach (var preLine in BodyPrefix) sb.AppendLine($"\t\t{preLine}"); var outVar = (ReturnTypeName != null ? "var Return" : "_"); if (BodyParams.Count > 0) { diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs index c7c34aa..dd391a0 100644 --- a/UE.Toolkit.DumperMod/Definitions/Property.cs +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -20,20 +20,21 @@ public class PropertyStructDefinition(string name, int size, int offset, Func propTypeName, - Func accessor, Func mutator) + Func accessor, Func? mutator) : BasePropertyDefintion(name, size, offset, propTypeName) { private Func Accessor => accessor; - private Func Mutator => mutator; + private Func? Mutator => mutator; public override string Serialize(Context context) { - var propName = Builtins.SanitizeName(Name); + var propName = Builtins.SanitizeForTypename(Builtins.SanitizeName(Name)); + if (propName == "Inner") propName += "_"; var sb = new StringBuilder(); sb.AppendLine($"\tpublic unsafe {PropTypeName()} {propName}"); sb.AppendLine("\t{"); sb.AppendLine($"\t\t{Accessor(Name)}"); - sb.AppendLine($"\t\t{Mutator(Name)}"); + if (Mutator != null) sb.AppendLine($"\t\t{Mutator(Name)}"); sb.AppendLine("\t}"); return sb.ToString(); } @@ -176,7 +177,7 @@ public Func GetPropTypenameStruct(IFProperty prop) var setPropType = GetPropTypenameStruct(context.Factory.Cast(prop).ElementProp)(); var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; return isPtrType - ? $"TSet /* TSet<{setPropType}> */" + ? $"TSet>" : $"TSet<{Builtins.SanitizeName(setPropType.TrimEnd('*'))}>"; }; case "OptionalProperty": @@ -211,21 +212,20 @@ public Func GetPropTypenameStruct(IFProperty prop) protected (string Name, int Size, int Offset, string ClassName) GetBaseInfo(IFProperty prop) { - var name = Builtins.SanitizeName(prop.NamePrivate); + var name = Builtins.SanitizeForTypename(Builtins.SanitizeName(prop.NamePrivate)); var size = prop.ElementSize; var offset = prop.Offset_Internal; var className = prop.ClassPrivate.Name; + // name = Builtins.SanitizeForParam(name); return (name, size, offset, className); } } public class PropertyStructFactory(Context context) : BasePropertyFactory(context) { - protected override BasePropertyDefintion ResolveProperty(IFProperty prop) { var (name, size, offset, _) = GetBaseInfo(prop); - if (name is "bool" or "float") name += '_'; return new PropertyStructDefinition(name, size, offset, GetPropTypenameStruct(prop)); } } @@ -237,12 +237,16 @@ private Func GetPropAccessor(IFProperty prop, Func getTy var className = prop.ClassPrivate.Name; switch (className) { + // Pass by value case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" - or "NameProperty" or "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" - or "SoftObjectProperty" or "SoftClassProperty" or "EnumProperty" or "TextProperty" - or "WeakObjectProperty" or "SetProperty": + or "NameProperty" or "EnumProperty" : return propName => $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; + // Pass by reference + case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" + or "WeakObjectProperty" or "SetProperty": + return propName => $"get => ({getTypeName()})(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); return (BoolProp.FieldMask == byte.MaxValue) switch @@ -258,17 +262,21 @@ private Func GetPropAccessor(IFProperty prop, Func getTy } } - private Func GetPropMutator(IFProperty prop, Func getTypeName) + private Func? GetPropMutator(IFProperty prop, Func getTypeName) { var className = prop.ClassPrivate.Name; switch (className) { + // Pass by value case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" - or "NameProperty" or "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" - or "SoftObjectProperty" or "SoftClassProperty" or "EnumProperty" or "TextProperty" - or "WeakObjectProperty" or "SetProperty": + or "NameProperty" or "EnumProperty": return propName => $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;"; + // Pass by reference, don't create mutator + case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" + or "WeakObjectProperty" or "SetProperty": + return null; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); return (BoolProp.FieldMask == byte.MaxValue) switch @@ -283,16 +291,39 @@ private Func GetPropMutator(IFProperty prop, Func getTyp } } + private Func GetClassPropTypenameManaged(IUClass classPropClass) + { + var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass"; + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : classPropType); + } + private Func GetPropTypenameClass(IFProperty prop) { var className = prop.ClassPrivate.Name; switch (className) { - case "ClassProperty": - case "ClassPtrProperty": - var classPropClass = context.Factory.Cast(prop).MetaClass; - var classPropType = classPropClass != null ? classPropClass.NamePrivate.ToString() : "UClass"; - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : classPropType); + // Passed by reference + case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" + or "WeakObjectProperty" or "SetProperty": + return () => GetPropTypenameStruct(prop)() + "*"; + case "ClassProperty" or "ClassPtrProperty": + return GetClassPropTypenameManaged(context.Factory.Cast(prop).MetaClass); + case "ObjectProperty": + var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); + default: + return GetPropTypenameStruct(prop); + } + } + + internal Func GetPropTypenameFunctionParam(IFProperty prop) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "ClassProperty" or "ClassPtrProperty": + return GetClassPropTypenameManaged(context.Factory.Cast(prop).MetaClass); case "ObjectProperty": var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); @@ -304,7 +335,6 @@ private Func GetPropTypenameClass(IFProperty prop) protected override BasePropertyDefintion ResolveProperty(IFProperty prop) { var (name, size, offset, _) = GetBaseInfo(prop); - if (name is "bool" or "float") name += '_'; var propTypename = GetPropTypenameClass(prop); var getAccessor = GetPropAccessor(prop, propTypename); var getMutator = GetPropMutator(prop, propTypename); diff --git a/UE.Toolkit.DumperMod/Definitions/Struct.cs b/UE.Toolkit.DumperMod/Definitions/Struct.cs index c4bd867..d36d722 100644 --- a/UE.Toolkit.DumperMod/Definitions/Struct.cs +++ b/UE.Toolkit.DumperMod/Definitions/Struct.cs @@ -61,7 +61,7 @@ protected void WriteStructBase(StringBuilder sb, string StructName, string? Supe sb.AppendLine($"\t[FieldOffset(0x0)] public {SuperName} Super; // Size: 0x{SuperSize:X}"); } foreach (var prop in Properties) - sb.AppendLine($"\t{prop.Serialize(context)}"); + sb.AppendLine($"\t{prop.Serialize(context)}"); } protected (string?, int) GetSuperInfo(Context context) From fd463c6c1549f4478cd1d6d8907225357d141cc3 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Fri, 24 Jul 2026 21:24:16 +0800 Subject: [PATCH 06/10] Fixed function dumping on 5.7 and retrieving class field offsets dynamically --- README.md | 2 +- .../Types/Interfaces/IFunctionParam.cs | 3 + .../Common/FunctionParam/DelegateParam.cs | 7 ++ .../FunctionParam/MulticastDelegateParam.cs | 10 ++ .../Unreal/Factories/UE5_7_4/UnrealFactory.cs | 38 ++++++- .../Types/Unreal/UE5_4_4/TBitArray.cs | 22 ++-- UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs | 8 +- .../Types/Unreal/UE5_7_4/UClass.cs | 32 ++++++ UE.Toolkit.DumperMod/Definitions/Builtins.cs | 27 +++-- UE.Toolkit.DumperMod/Definitions/Class.cs | 18 ++- UE.Toolkit.DumperMod/Definitions/Function.cs | 26 +++-- UE.Toolkit.DumperMod/Definitions/Property.cs | 106 ++++++++++-------- UE.Toolkit.DumperMod/Definitions/Struct.cs | 7 +- UE.Toolkit.Reloaded/Unreal/UnrealObjects.cs | 15 ++- 14 files changed, 232 insertions(+), 89 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DelegateParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MulticastDelegateParam.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/UE5_7_4/UClass.cs diff --git a/README.md b/README.md index 2198da5..76832db 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ | Object Logging |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Object Editing |✅|❔|❔|✅|❔|✅|❔|❔|❔ | `FMemory` Functions |✅|✅|✅|✅|✅|✅|✅|✅|✅ -| Dumper |✅|✅|✅|✅|✅|✅|✅|✅|⚠️️️️️️ +| Dumper |✅|✅|✅|✅|✅|✅|✅|✅|️️️️️️✅ | Property Editing (Object XML) |✅|❔|❔|✅|❔|✅|❔|❔|❔ | Add List Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ | Add Map Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ diff --git a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs index 56586bb..11c96c4 100644 --- a/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs +++ b/UE.Toolkit.Core/Types/Interfaces/IFunctionParam.cs @@ -101,6 +101,9 @@ public static unsafe IFunctionParam CreateParam(IFProperty property, IUnrealFact "SoftObjectProperty" => new SoftObjectParam(new((TSoftObjectPtr*)(Memory?.Malloc(sizeof(TSoftObjectPtr)) ?? nint.Zero)), Memory), "Utf8StrProperty" => new Utf8StringParam(new((FUtf8String*)(Memory?.Malloc(sizeof(FUtf8String)) ?? nint.Zero)), Memory), "AnsiStrProperty" => new AnsiStringParam(new((FAnsiString*)(Memory?.Malloc(sizeof(FAnsiString)) ?? nint.Zero)), Memory), + "DelegateProperty" => new DelegateParam(new((FScriptDelegate*)(Memory?.Malloc(sizeof(FScriptDelegate)) ?? nint.Zero)), Memory), + "MulticastInlineDelegateProperty" => new MulticastInlineDelegateParam(new((FMulticastScriptDelegate*)(Memory?.Malloc(sizeof(FMulticastScriptDelegate)) ?? nint.Zero)), Memory), + "MulticastSparseDelegateProperty" => new MulticastSparseDelegateParam(new((FMulticastSparseDelegateProperty*)(Memory?.Malloc(sizeof(FMulticastSparseDelegateProperty)) ?? nint.Zero)), Memory), _ => throw new NotSupportedException($"CreateParam with property {property.ClassPrivate.Name}") }; } diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DelegateParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DelegateParam.cs new file mode 100644 index 0000000..be1bb2a --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/DelegateParam.cs @@ -0,0 +1,7 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class DelegateParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "DelegateProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MulticastDelegateParam.cs b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MulticastDelegateParam.cs new file mode 100644 index 0000000..289f984 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/FunctionParam/MulticastDelegateParam.cs @@ -0,0 +1,10 @@ +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.Common.FunctionParam; + +public class MulticastInlineDelegateParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "MulticastInlineDelegateProperty", memory); + +public class MulticastSparseDelegateParam(Ptr rvalue, IUnrealMemoryInternal? memory = null) + : FunctionParamCopyable(rvalue, "MulticastSparseDelegateProperty", memory); \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs index 31bf440..63e7671 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs @@ -5,6 +5,7 @@ using UE.Toolkit.Core.Types.Unreal.Factories.UE5_4_4; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; using FFieldClass = UE.Toolkit.Core.Types.Unreal.UE5_7_4.FFieldClass; +using UClass = UE.Toolkit.Core.Types.Unreal.UE5_7_4.UClass; namespace UE.Toolkit.Core.Types.Unreal.Factories.UE5_7_4; @@ -57,7 +58,7 @@ public override nint SizeOf() public override IFDelegateProperty CreateFDelegateProperty(nint ptr) => new FDelegateProperty_UE5_4_4(ptr, this); public override IUObjectArray CreateUObjectArray(nint ptr) => new UObjectArray_UE5_7_4(ptr, this); public override IUObject CreateUObject(nint ptr) => new UObject_UE5_4_4(ptr, this, Memory); - public override IUClass CreateUClass(nint ptr) => new UE5_6_1.UClass_UE5_6_1(ptr, this, Memory); + public override IUClass CreateUClass(nint ptr) => new UClass_UE5_7_4(ptr, this, Memory); public override IUScriptStruct CreateUScriptStruct(nint ptr) => new UE5_6_1.UScriptStruct_UE5_6_1(ptr, this, Memory); public override IUEnum CreateUEnum(nint ptr) => new UEnum_UE5_7_4(ptr, this, Memory); public override IUField CreateUField(nint ptr) => new UField_UE5_4_4(ptr, this, Memory); @@ -200,4 +201,39 @@ protected virtual void Dispose(bool disposing) if (disposing && CachedNames.Value.AllocatorInstance != null) _factory.Memory.Free((nint)CachedNames.Value.AllocatorInstance); } +} + +public unsafe class UClass_UE5_7_4(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UE5_6_1.UStruct_UE5_6_1(ptr, factory, memory), IUClass +{ + private readonly UClass* _self = (UClass*)ptr; + + public IUClass? GetSuperClass() + => _self->GetSuperClass() != null ? _factory.CreateUClass((nint)_self->GetSuperClass()) : null; + + public IUFunction? GetFunction(string Name) + { + var FuncMapDict = new TMapDictionary( + (TMap*)(&_self->FuncMap), factory.Memory + ); + return FuncMapDict.TryGetValue(new(Name), out var Function) + ? factory.CreateUFunction((nint)Function.Value) + : null; + } + + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), + _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + + public IUObject? ClassDefaultObject + => _self->ClassDefaultObject != null ? factory.CreateUObject((nint)_self->ClassDefaultObject ) : null; + + public nint Constructor => _self->ClassConstructor; + public EClassFlags ClassFlags => _self->ClassFlags; + public EClassCastFlags ClassCastFlags => _self->ClassCastFlags; } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TBitArray.cs b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TBitArray.cs index 683f624..0b3a6df 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TBitArray.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TBitArray.cs @@ -40,18 +40,21 @@ public unsafe class TBitArrayList : IDisposable, IList private int InlineAllocatorSize; protected bool OwnsInstance; protected bool Disposed = false; + protected bool InlineGoesFirst; /// /// Wraps a TArrayList around an existing TBitArray created in C++ /// /// Pointer to an existing TBitArray /// The Unreal allocator, used for methods that modify the TBitArray - public TBitArrayList(byte* _Self, IUnrealMemoryInternal _Allocator, int _InlineAllocatorSize = TBitArrayConstants.DEFAULT_ALLOCATOR_SIZE) + public TBitArrayList(byte* _Self, IUnrealMemoryInternal _Allocator, int _InlineAllocatorSize = TBitArrayConstants.DEFAULT_ALLOCATOR_SIZE, + bool inlineGoesFirst = true) { Allocator = _Allocator; InlineAllocatorSize = _InlineAllocatorSize; Self = _Self; OwnsInstance = false; + InlineGoesFirst = inlineGoesFirst; } /// @@ -65,19 +68,20 @@ public TBitArrayList(IUnrealMemoryInternal _Allocator, int _InlineAllocatorSize Self = (byte*)Allocator.MallocZeroed(GetStructSize()); ArrayMax = InlineBits; OwnsInstance = true; + InlineGoesFirst = true; } protected byte* Inline { - get => Self; + get => Self + (InlineGoesFirst ? 0 : InlineAllocatorSize); } internal int InlineBits => InlineAllocatorSize * TBitArrayConstants.BITS_PER_BYTE; protected byte* Allocation { - get => *(byte**)(Self + InlineAllocatorSize); - set => *(byte**)(Self + InlineAllocatorSize) = value; + get => *(byte**)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0)); + set => *(byte**)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0)) = value; } protected byte* Data @@ -94,17 +98,17 @@ protected byte* Data public int ArrayNum { - get => *(int*)(Self + InlineAllocatorSize + sizeof(nint)); - protected set => *(int*)(Self + InlineAllocatorSize + sizeof(nint)) = value; + get => *(int*)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0) + sizeof(nint)); + protected set => *(int*)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0) + sizeof(nint)) = value; } public int ArrayMax { - get => *(int*)(Self + InlineAllocatorSize + sizeof(nint) + sizeof(int)); - protected set => *(int*)(Self + InlineAllocatorSize + sizeof(nint) + sizeof(int)) = value; + get => *(int*)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0) + sizeof(nint) + sizeof(int)); + protected set => *(int*)(Self + (InlineGoesFirst ? InlineAllocatorSize : 0) + sizeof(nint) + sizeof(int)) = value; } - bool InBounds(int index) => index is >= 0 && index < ArrayNum; + bool InBounds(int index) => index >= 0 && index < ArrayNum; bool InBoundsForInsertion(int index) => index >= 0 && index <= ArrayNum; /// /// Relinquish ownership of this TArray. This is used in cases where you know that Unreal will deallocate it or it otherwise diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs index 6ae1f71..dfebc2a 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs @@ -396,13 +396,13 @@ private int HashSize /// /// Pointer to an existing TMap /// The Unreal allocator, used for methods that modify the TMap - public TMapDictionary(TMap* _Self, IUnrealMemoryInternal _Allocator, Action? _DebugCallback = null) + public TMapDictionary(TMap* _Self, IUnrealMemoryInternal _Allocator, Action? _DebugCallback = null, bool inlineGoesFirst = true) { Self = (nint)_Self; Allocator = _Allocator; Elements = new(ElementsRaw, Allocator); OwnsInstance = false; - BitAllocator = new(BitAllocatorRaw, Allocator); + BitAllocator = new(BitAllocatorRaw, Allocator, TBitArrayConstants.DEFAULT_ALLOCATOR_SIZE, inlineGoesFirst); DebugCallback = _DebugCallback; } @@ -411,13 +411,13 @@ public TMapDictionary(TMap* _Self, IUnrealMemoryInternal _ /// taken out of scope. /// /// The Unreal allocator, used for methods that modify the TMap - public TMapDictionary(IUnrealMemoryInternal _Allocator) + public TMapDictionary(IUnrealMemoryInternal _Allocator, bool inlineGoesFirst = true) { Self = _Allocator.MallocZeroed(SizeOf); Allocator = _Allocator; Elements = new(ElementsRaw, Allocator); OwnsInstance = true; - BitAllocator = new(BitAllocatorRaw, Allocator); + BitAllocator = new(BitAllocatorRaw, Allocator, TBitArrayConstants.DEFAULT_ALLOCATOR_SIZE, inlineGoesFirst); // Sets ArrayMax for BitAllocator to number of inline bits (128) BitAllocator.Clear(); } diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_7_4/UClass.cs b/UE.Toolkit.Core/Types/Unreal/UE5_7_4/UClass.cs new file mode 100644 index 0000000..41dd27e --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE5_7_4/UClass.cs @@ -0,0 +1,32 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.UE5_7_4; + +[StructLayout(LayoutKind.Sequential, Size = 0x208)] +public unsafe struct UClass +{ + public UStruct Super; + public nint ClassConstructor; + public nint ClassVTableHelperCtorCaller; + public nint CppClassStaticFunctions; + public int ClassUnique; + public int FirstOwnedClassRep; + public bool bCooked; + public bool bLayoutChanging; + public EClassFlags ClassFlags; + public EClassCastFlags ClassCastFlags; + public UClass* ClassWithin; + //public UObjectBase* ClassGeneratedBy; // WITH_EDITORONLY_DATA + //public FField* PropertiesPendingDestruction; // WITH_EDITORONLY_DATA + public FName ClassConfigName; + public TArray ClassReps; + public TArray NetFields; + public UObjectBase* ClassDefaultObject; + public nint SparseClassData; + public UScriptStruct* SparseClassDataStruct; + public bool bNeedsDynamicSubobjectInstancing; + public TMap FuncMap; + + public readonly UClass* GetSuperClass() => (UClass*)Super.SuperStruct; +} \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs index 40bbc19..115884f 100644 --- a/UE.Toolkit.DumperMod/Definitions/Builtins.cs +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -17,10 +17,13 @@ private List GetDefaultUsings() "System.Runtime.InteropServices", "UE.Toolkit.Core.Types", "UE.Toolkit.Core.Types.Unreal.UE5_4_4", - "UE.Toolkit.Core.Types.Unreal.Factories", - "UE.Toolkit.Core.Types.Unreal.Factories.Interfaces", - "UE.Toolkit.Core.Types.Unreal.Common.FunctionParam", ]); + if (Mod.Config.Schema == DumpSchema.Dynamic) + Usings.AddRange([ + "UE.Toolkit.Core.Types.Unreal.Factories", + "UE.Toolkit.Core.Types.Unreal.Factories.Interfaces", + "UE.Toolkit.Core.Types.Unreal.Common.FunctionParam", + ]); var VerParts = Essentials.GetEngineVersion().Split("-")[^1].Split("."); // FText definition is different for versions below UE 5.4 if (int.Parse(VerParts[0]) < 5 || int.Parse(VerParts[1]) < 4) @@ -72,16 +75,9 @@ public interface ITypeRepr where TRepr: unmanaged unsafe TRepr* Repr { get; } } -public abstract class ObjectImpl +public abstract class ObjectImpl(IUObject inner) { - public IUObject Inner { get; } - protected static Dictionary FieldOffsets; - - protected ObjectImpl(IUObject inner) { - Inner = inner; - FieldOffsets = Inner.ClassPrivate.PropertyLink - .Select(x => (x.NamePrivate, x.Offset_Internal)).ToDictionary(); - } + public IUObject Inner { get; } = inner; } """); @@ -130,4 +126,11 @@ public static string SanitizeForTypename(string name) _ => name }; } + + public static string SanitizeForFunctionName(string name) + { + name = name.Replace(".", "_"); + name = name.Replace(">", "_"); + return name; + } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Class.cs b/UE.Toolkit.DumperMod/Definitions/Class.cs index f30fcc5..2366f76 100644 --- a/UE.Toolkit.DumperMod/Definitions/Class.cs +++ b/UE.Toolkit.DumperMod/Definitions/Class.cs @@ -14,7 +14,9 @@ public override void Register() { var alignment = uclass.MinAlignment; var super = uclass.GetSuperClass(); - var superSize = super?.PropertiesSize ?? 0; + var superLastProp = super?.PropertyLink.MaxBy(x => x.Offset_Internal); + var superSize = superLastProp?.Offset_Internal + superLastProp?.ElementSize ?? 0; + // var superSize = super?.PropertiesSize ?? 0; var superName = super?.NamePrivate.ToString(); // TODO: Generate delegates. @@ -69,8 +71,18 @@ public override string Serialize(Context context) StructDefinition? SuperDef = null; if (SuperInternalName != null) context.Registry.Structs.TryGetValue(SuperInternalName, out SuperDef); var SuperDefName = SuperDef != null ? Builtins.SanitizeName(SuperDef.DisplayName) : "ObjectImpl"; - sb.AppendLine($"public class {DisplayNameCS}(IUObject inner) : {SuperDefName}(inner), ITypeRepr<{DisplayNameRepr}>\n{{"); - var ReprNewModifier = SuperDef != null ? "new " : string.Empty; + // sb.AppendLine($"public class {DisplayNameCS}(IUObject inner) : {SuperDefName}(inner), ITypeRepr<{DisplayNameRepr}>\n{{"); + sb.AppendLine($"public class {DisplayNameCS} : {SuperDefName}, ITypeRepr<{DisplayNameRepr}>\n{{"); + sb.AppendLine("\tprivate static Dictionary? FieldOffsets;"); + var callBaseCtor = SuperDef != null ? "base(inner, false)" : "base(inner)"; + sb.AppendLine($"\tpublic {DisplayNameCS}(IUObject inner, bool genOffsets = true) : {callBaseCtor}\n\t{{"); + sb.AppendLine(""" + if (genOffsets) + FieldOffsets ??= Inner.ClassPrivate.PropertyLink + .Select(x => (x.NamePrivate, x.Offset_Internal)).ToDictionary(); +"""); + sb.AppendLine("\t}"); + var ReprNewModifier = SuperDef != null ? "new " : string.Empty; sb.AppendLine($"\tpublic {ReprNewModifier}unsafe {DisplayNameRepr}* Repr => ({DisplayNameRepr}*)Inner.Ptr;\n"); foreach (var prop in Properties) sb.AppendLine(prop.Serialize(context)); diff --git a/UE.Toolkit.DumperMod/Definitions/Function.cs b/UE.Toolkit.DumperMod/Definitions/Function.cs index ea3df63..94bed29 100644 --- a/UE.Toolkit.DumperMod/Definitions/Function.cs +++ b/UE.Toolkit.DumperMod/Definitions/Function.cs @@ -106,7 +106,9 @@ public string Serialize(Context context) { "StructProperty" or "EnumProperty" or "TextProperty" or "ArrayProperty" or "MapProperty" or "SetProperty" or "InterfaceProperty" - or "SoftClassProperty" or "SoftObjectProperty" => CreateStructPropertyReturn(), + or "SoftClassProperty" or "SoftObjectProperty" or "DelegateProperty" + or "MulticastInlineDelegateProperty" or "MulticastSparseDelegateProperty" + => CreateStructPropertyReturn(), _ => CreateSingleLineReturn() }; } @@ -118,8 +120,11 @@ public List ResolveFunctions(IUClass uclass) { var PropFactory = new PropertyClassFactory(context); return uclass.GetFunctions() - .Where(x => !x.NamePrivate.ToString().StartsWith("ExecuteUbergraph")) - .DistinctBy(x => x.NamePrivate.ToString()) + .Where(x => + { + var Name = x.NamePrivate.ToString(); + return !(Name.StartsWith("ExecuteUbergraph") || Name.StartsWith("EvaluateGraphExposedInputs")); + }) .Select(x => { List HeadParams = []; @@ -145,20 +150,24 @@ public List ResolveFunctions(IUClass uclass) if (Param.ClassPrivate.Name is "ObjectProperty" or "ClassProperty" or "ClassPtrProperty") { BodyPrefix.Add($"nint {paramName}_Ptr = {paramName}.Inner.Ptr;"); - BodyPostfix.Add($"{paramName} = new(Inner.GetFactory().CreateUObject({paramName}_Ptr));"); + if (Param.PropertyFlags.HasFlag(EPropertyFlags.CPF_OutParm)) + BodyPostfix.Add($"{paramName} = new(Inner.GetFactory().CreateUObject({paramName}_Ptr));"); } } if (BodyParams.Count > 0) BodyParams.Last().IsLast = true; - return new FunctionDefinition(Builtins.SanitizeName(x.NamePrivate.ToString()), returnTypeName, HeadParams, BodyParams, ReturnValue, BodyPrefix, BodyPostfix); + var funcName = x.NamePrivate.ToString(); + var funcNameSanitized = Builtins.SanitizeForFunctionName(Builtins.SanitizeName(funcName)); + return new FunctionDefinition(funcNameSanitized, funcName, returnTypeName, HeadParams, BodyParams, ReturnValue, BodyPrefix, BodyPostfix); }).ToList(); } } -public class FunctionDefinition(string name, Func? returnTypeName, List headParams, +public class FunctionDefinition(string name, string rawName, Func? returnTypeName, List headParams, List bodyParams, ReturnValueDefinition? returnParam, List bodyPrefix, List bodyPostfix) : ISerializable { public string Name { get; } = name; + public string RawName { get; } = rawName; public Func? ReturnTypeName { get; } = returnTypeName; public List HeadParams { get; } = headParams; public List BodyParams { get; } = bodyParams; @@ -176,11 +185,12 @@ public string Serialize(Context context) var outVar = (ReturnTypeName != null ? "var Return" : "_"); if (BodyParams.Count > 0) { - sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{Name}\", ["); + sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{RawName}\", ["); foreach (var bodyParam in BodyParams) sb.AppendLine($"\t\t\t{bodyParam.Serialize(context)}"); sb.AppendLine($"\t\t], out {outVar});"); } - else sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{Name}\", [], out {outVar});"); + else sb.AppendLine($"\t\t_ = Inner.ProcessEvent(\"{RawName}\", [], out {outVar});"); + foreach (var postLine in BodyPostfix) sb.AppendLine($"\t\t{postLine}"); if (ReturnParam != null) sb.AppendLine($"\t\t{ReturnParam.Serialize(context)}"); sb.AppendLine("\t}"); return sb.ToString(); diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs index dd391a0..00e6a3a 100644 --- a/UE.Toolkit.DumperMod/Definitions/Property.cs +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -16,25 +16,26 @@ public abstract class BasePropertyDefintion(string name, int size, int offset, F public class PropertyStructDefinition(string name, int size, int offset, Func propTypeName) : BasePropertyDefintion(name, size, offset, propTypeName) { - public override string Serialize(Context context) => $"[FieldOffset(0x{Offset:X})] public {PropTypeName()} {Builtins.SanitizeName(Name)}; // Size: 0x{Size:X}"; + public override string Serialize(Context context) => $"[FieldOffset(0x{Offset:X})] public {PropTypeName()} {Name}; // Size: 0x{Size:X}"; } -public class PropertyClassDefinition(string name, int size, int offset, Func propTypeName, - Func accessor, Func? mutator) +public class PropertyClassDefinition(string name, string rawName, int size, int offset, Func propTypeName, + Func accessor, Func? mutator) : BasePropertyDefintion(name, size, offset, propTypeName) { - private Func Accessor => accessor; - private Func? Mutator => mutator; + private string RawName => rawName; + private Func Accessor => accessor; + private Func? Mutator => mutator; public override string Serialize(Context context) { - var propName = Builtins.SanitizeForTypename(Builtins.SanitizeName(Name)); + var propName = Name; if (propName == "Inner") propName += "_"; var sb = new StringBuilder(); sb.AppendLine($"\tpublic unsafe {PropTypeName()} {propName}"); sb.AppendLine("\t{"); - sb.AppendLine($"\t\t{Accessor(Name)}"); - if (Mutator != null) sb.AppendLine($"\t\t{Mutator(Name)}"); + sb.AppendLine($"\t\t{Accessor(Name, RawName)}"); + if (Mutator != null) sb.AppendLine($"\t\t{Mutator(Name, RawName)}"); sb.AppendLine("\t}"); return sb.ToString(); } @@ -61,6 +62,12 @@ public List ResolveProperties(IEnumerable pro } protected abstract BasePropertyDefintion ResolveProperty(IFProperty prop); + + private static string SafelyCreateGenericType(string Type, bool makeGeneric) + => makeGeneric ? Type : $"nint /* {Type} */"; + + private static string SafelyCreatePointerType(string Type, bool makeGeneric) + => makeGeneric ? $"Ptr<{Type}>" : $"nint /* Ptr<{Type}> */"; /// /// Gets the property type name, such as byte for ByteProperty. @@ -69,7 +76,7 @@ public List ResolveProperties(IEnumerable pro /// /// /// C++/C# property type name. - public Func GetPropTypenameStruct(IFProperty prop) + public Func GetPropTypenameStruct(IFProperty prop, bool makeGenerics) { var className = prop.ClassPrivate.Name; switch (className) @@ -124,10 +131,15 @@ public Func GetPropTypenameStruct(IFProperty prop) return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.GetUnmanagedTypeName()}*" : $"{objPropType}*"); case "SoftObjectProperty": var softObjPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.GetUnmanagedTypeName()}>" : $"TSoftObjectPtr<{softObjPropType}>"); + // return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? $"TSoftObjectPtr<{knownStruct.GetUnmanagedTypeName()}>" : $"TSoftObjectPtr<{softObjPropType}>"); + return () => context.Registry.Structs.TryGetValue(softObjPropType, out var knownStruct) ? + $"TSoftObjectPtr<{SafelyCreateGenericType(knownStruct.GetUnmanagedTypeName(), makeGenerics)}>" : + $"TSoftObjectPtr<{SafelyCreateGenericType(Builtins.SanitizeName(softObjPropType), makeGenerics)}>"; case "SoftClassProperty": var softClassPropType = context.Factory.Cast(prop).MetaClass.NamePrivate.ToString(); - return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(softClassPropType, out var knownStruct) ? $"TSoftClassPtr<{knownStruct.GetUnmanagedTypeName()}>" : $"TSoftClassPtr<{softClassPropType}>"); + return () => context.Registry.Structs.TryGetValue(softClassPropType, out var knownStruct) ? + $"TSoftClassPtr<{SafelyCreateGenericType(knownStruct.GetUnmanagedTypeName(), makeGenerics)}>" : + $"TSoftClassPtr<{SafelyCreateGenericType(Builtins.SanitizeName(softClassPropType), makeGenerics)}>"; case "StructProperty": var structPropType = context.Factory.Cast(prop).Struct.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(structPropType, out var knownStruct) ? knownStruct.GetUnmanagedTypeName() : structPropType); @@ -145,16 +157,16 @@ public Func GetPropTypenameStruct(IFProperty prop) var mapProp = context.Factory.Cast(prop); return () => { - var mapPropKeyType = GetPropTypenameStruct(mapProp.KeyProp)(); - var mapPropValueType = GetPropTypenameStruct(mapProp.ValueProp)(); + var mapPropKeyType = GetPropTypenameStruct(mapProp.KeyProp, makeGenerics)(); + var mapPropValueType = GetPropTypenameStruct(mapProp.ValueProp, makeGenerics)(); var isKeyPtr = mapPropKeyType.EndsWith('*') || mapPropKeyType.Contains('<'); // Use nint for pointers and generic types. var isValuePtr = mapPropValueType.EndsWith('*') || mapPropValueType.Contains('<'); var keyName = Builtins.SanitizeName(mapPropKeyType.TrimEnd('*')); var valueName = Builtins.SanitizeName(mapPropValueType.TrimEnd('*')); - var keyType = isKeyPtr ? $"Ptr<{keyName}>" : keyName; - var valueType = isValuePtr ? $"Ptr<{valueName}>" : valueName; + var keyType = isKeyPtr ? SafelyCreatePointerType(keyName, makeGenerics) : keyName; + var valueType = isValuePtr ? SafelyCreatePointerType(valueName, makeGenerics) : valueName; return $"TMap<{keyType}, {valueType}>"; }; @@ -164,26 +176,27 @@ public Func GetPropTypenameStruct(IFProperty prop) case "ArrayProperty": return () => { - var arrayPropType = GetPropTypenameStruct(context.Factory.Cast(prop).Inner)(); + var arrayPropType = GetPropTypenameStruct(context.Factory.Cast(prop).Inner, makeGenerics)(); var isPtrType = arrayPropType.EndsWith('*') || arrayPropType.Contains('<'); // Use nint for pointers and generic types. var arrTypeSanitized = Builtins.SanitizeName(arrayPropType.TrimEnd('*')); return isPtrType ? - $"TArray>" : + $"TArray<{SafelyCreatePointerType(arrTypeSanitized, makeGenerics)}>" : $"TArray<{arrTypeSanitized}>"; }; case "SetProperty": return () => { - var setPropType = GetPropTypenameStruct(context.Factory.Cast(prop).ElementProp)(); + var setPropType = GetPropTypenameStruct(context.Factory.Cast(prop).ElementProp, makeGenerics)(); var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; - return isPtrType - ? $"TSet>" - : $"TSet<{Builtins.SanitizeName(setPropType.TrimEnd('*'))}>"; + var setPropTypeSanitized = Builtins.SanitizeName(setPropType.TrimEnd('*')); + return isPtrType ? + $"TSet<{SafelyCreatePointerType(setPropTypeSanitized, makeGenerics)}>" : + $"TSet<{setPropTypeSanitized}>"; }; case "OptionalProperty": return () => { - var optionalType = GetPropTypenameStruct(context.Factory.Cast(prop).ValueProperty)(); + var optionalType = GetPropTypenameStruct(context.Factory.Cast(prop).ValueProperty, makeGenerics)(); if (context.Registry.Structs.TryGetValue(optionalType, out var knownOptType)) optionalType = knownOptType.GetUnmanagedTypeName(); @@ -198,7 +211,7 @@ public Func GetPropTypenameStruct(IFProperty prop) if (context.Registry.Structs.TryGetValue(lazyObjType, out var knownLazyType)) lazyObjType = knownLazyType.GetUnmanagedTypeName(); - return $"TLazyObjectPtr<{Builtins.SanitizeName(lazyObjType)}>"; + return $"TLazyObjectPtr<{SafelyCreateGenericType(Builtins.SanitizeName(lazyObjType), makeGenerics)}>"; }; case "Utf8StrProperty": return () => "FUtf8String"; @@ -226,13 +239,13 @@ public class PropertyStructFactory(Context context) : BasePropertyFactory(contex protected override BasePropertyDefintion ResolveProperty(IFProperty prop) { var (name, size, offset, _) = GetBaseInfo(prop); - return new PropertyStructDefinition(name, size, offset, GetPropTypenameStruct(prop)); + return new PropertyStructDefinition(name, size, offset, GetPropTypenameStruct(prop, false)); } } public class PropertyClassFactory(Context context) : BasePropertyFactory(context) { - private Func GetPropAccessor(IFProperty prop, Func getTypeName) + private Func GetPropAccessor(IFProperty prop, Func getTypeName) { var className = prop.ClassPrivate.Name; switch (className) @@ -241,28 +254,29 @@ private Func GetPropAccessor(IFProperty prop, Func getTy case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" or "NameProperty" or "EnumProperty" : - return propName => $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; + return (_, raw) => $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets![\"{raw}\"]);"; // Pass by reference case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" - or "WeakObjectProperty" or "SetProperty": - return propName => $"get => ({getTypeName()})(Inner.Ptr + FieldOffsets[\"{propName}\"]);"; + or "WeakObjectProperty" or "SetProperty" or "DelegateProprety" + or "MulticastInlineDelegateProperty" or "MulticastSparseDelegateProperty": + return (_, raw) => $"get => ({getTypeName()})(Inner.Ptr + FieldOffsets![\"{raw}\"]);"; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); return (BoolProp.FieldMask == byte.MaxValue) switch { - true => propName => $"get => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]);", - false => propName => $"get => (*(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) & {BoolProp.FieldMask}) == 0;" + true => (_, raw) => $"get => *(bool*)(Inner.Ptr + FieldOffsets![\"{raw}\"]);", + false => (_, raw) => $"get => (*(byte*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) & {BoolProp.FieldMask}) == 0;" }; case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": - return propName => - $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"])));"; + return (_, raw) => + $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + FieldOffsets![\"{raw}\"])));"; default: - return _ => $"get => throw new NotSupportedException(\"!! GET TODO {className} !!\");"; + return (_, _) => $"get => throw new NotSupportedException(\"!! GET TODO {className} !!\");"; } } - private Func? GetPropMutator(IFProperty prop, Func getTypeName) + private Func? GetPropMutator(IFProperty prop, Func getTypeName) { var className = prop.ClassPrivate.Name; switch (className) @@ -271,23 +285,24 @@ private Func GetPropAccessor(IFProperty prop, Func getTy case "ByteProperty" or "Int8Property" or "Int16Property" or "UInt16Property" or "IntProperty" or "UInt32Property" or "Int64Property" or "UInt64Property" or "FloatProperty" or "DoubleProperty" or "NameProperty" or "EnumProperty": - return propName => $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;"; + return (_, raw) => $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value;"; // Pass by reference, don't create mutator case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" - or "WeakObjectProperty" or "SetProperty": + or "WeakObjectProperty" or "SetProperty" or "DelegateProprety" + or "MulticastInlineDelegateProperty" or "MulticastSparseDelegateProperty": return null; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); return (BoolProp.FieldMask == byte.MaxValue) switch { - true => propName => $"set => *(bool*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value;", - false => propName => $"set => *(byte*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) ^= (byte)(Convert.ToByte({propName} != value) * {BoolProp.FieldMask});" + true => (_, raw) => $"set => *(bool*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value;", + false => (param, raw) => $"set => *(byte*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) ^= (byte)(Convert.ToByte({param} != value) * {BoolProp.FieldMask});" }; case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": - return propName => $"set => *(nint*)(Inner.Ptr + FieldOffsets[\"{propName}\"]) = value.Inner.Ptr;"; + return (_, raw) => $"set => *(nint*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value.Inner.Ptr;"; default: - return _ => $"set => throw new NotSupportedException(\"!! SET TODO {className} !!\");"; + return (_, _) => $"set => throw new NotSupportedException(\"!! SET TODO {className} !!\");"; } } @@ -305,15 +320,16 @@ private Func GetPropTypenameClass(IFProperty prop) // Passed by reference case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" - or "WeakObjectProperty" or "SetProperty": - return () => GetPropTypenameStruct(prop)() + "*"; + or "WeakObjectProperty" or "SetProperty" or "DelegateProprety" + or "MulticastInlineDelegateProperty" or "MulticastSparseDelegateProperty": + return () => GetPropTypenameStruct(prop, true)() + "*"; case "ClassProperty" or "ClassPtrProperty": return GetClassPropTypenameManaged(context.Factory.Cast(prop).MetaClass); case "ObjectProperty": var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); default: - return GetPropTypenameStruct(prop); + return GetPropTypenameStruct(prop, true); } } @@ -328,7 +344,7 @@ internal Func GetPropTypenameFunctionParam(IFProperty prop) var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); default: - return GetPropTypenameStruct(prop); + return GetPropTypenameStruct(prop, true); } } @@ -338,6 +354,6 @@ protected override BasePropertyDefintion ResolveProperty(IFProperty prop) var propTypename = GetPropTypenameClass(prop); var getAccessor = GetPropAccessor(prop, propTypename); var getMutator = GetPropMutator(prop, propTypename); - return new PropertyClassDefinition(name, size, offset, propTypename, getAccessor, getMutator); + return new PropertyClassDefinition(name, prop.NamePrivate, size, offset, propTypename, getAccessor, getMutator); } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Struct.cs b/UE.Toolkit.DumperMod/Definitions/Struct.cs index d36d722..f34e999 100644 --- a/UE.Toolkit.DumperMod/Definitions/Struct.cs +++ b/UE.Toolkit.DumperMod/Definitions/Struct.cs @@ -9,10 +9,13 @@ public override void Register() { var structName = scriptStruct.NamePrivate.ToString(); var structNativeName = "F" + structName; - var size = scriptStruct.PropertiesSize; var alignment = scriptStruct.MinAlignment; + var align = alignment - 1; + var size = (scriptStruct.PropertiesSize + align) & ~align; var super = scriptStruct.SuperStruct; - var superSize = super?.PropertiesSize ?? 0; + var superLastProp = super?.PropertyLink.MaxBy(x => x.Offset_Internal); + var superSize = superLastProp?.Offset_Internal + superLastProp?.ElementSize ?? 0; + // var superSize = super?.PropertiesSize ?? 0; var superName = super?.NamePrivate.ToString(); if ((superName ?? string.Empty) == structName) { diff --git a/UE.Toolkit.Reloaded/Unreal/UnrealObjects.cs b/UE.Toolkit.Reloaded/Unreal/UnrealObjects.cs index c0e746c..db4830c 100644 --- a/UE.Toolkit.Reloaded/Unreal/UnrealObjects.cs +++ b/UE.Toolkit.Reloaded/Unreal/UnrealObjects.cs @@ -86,6 +86,13 @@ public UnrealObjects(IUnrealFactory factory) public IUObjectArray GUObjectArray { get; private set; } = null!; + // Remove ending _Repr from object types, to handle class based object dumps created in UE Toolkit 1.10+ + private static string GetObjectTypeName() where TObject : unmanaged + { + var Name = typeof(TObject).Name; + return Name.EndsWith("_Repr") ? Name[1..^5] : Name[1..]; + } + public void OnObjectLoadedByName(string objName, Action> callback) where TObject : unmanaged { @@ -101,7 +108,7 @@ public void OnObjectLoadedByName(string objName, Action(Action> callback) - where TObject : unmanaged => OnObjectLoadedByName(typeof(TObject).Name, callback); + where TObject : unmanaged => OnObjectLoadedByName(GetObjectTypeName(), callback); public void OnObjectLoadedByClass(string objClass, Action> callback) where TObject : unmanaged @@ -113,7 +120,7 @@ public void OnObjectLoadedByClass(string objClass, Action(Action> callback) - where TObject : unmanaged => OnObjectLoadedByClass(typeof(TObject).Name, callback); + where TObject : unmanaged => OnObjectLoadedByClass(GetObjectTypeName(), callback); public void OnObjectLoadedByPath(string objectPath, Action> callback) where TObject : unmanaged @@ -148,7 +155,7 @@ private void ForEachObject(Func Callback) public ToolkitUObject? FindObjectByName(string objectName) where TObject : unmanaged { - var Object = FindObjectByName(objectName, typeof(TObject).Name); + var Object = FindObjectByName(objectName, GetObjectTypeName()); return Object != null ? new ToolkitUObject((TObject*)Object.Ptr) : null; } @@ -165,7 +172,7 @@ private void ForEachObject(Func Callback) public ToolkitUObject? FindObjectByClass() where TObject : unmanaged { - var Object = FindObjectByClass(typeof(TObject).Name); + var Object = FindObjectByClass(GetObjectTypeName()); return Object != null ? new ToolkitUObject((TObject*)Object.Ptr) : null; } From 9557460421d080905fb5fb48eb5826d47b94f742 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Sat, 25 Jul 2026 16:17:59 +0800 Subject: [PATCH 07/10] Retrieve field offsets and functions from super classes --- .../Unreal/Factories/Interfaces/IUObject.cs | 9 +++++++- .../Types/Unreal/UE5_4_4/TArray.cs | 5 +++++ UE.Toolkit.DumperMod/Definitions/Builtins.cs | 19 +++++++++++++++++ UE.Toolkit.DumperMod/Definitions/Class.cs | 9 +++----- UE.Toolkit.DumperMod/Definitions/Enum.cs | 2 +- UE.Toolkit.DumperMod/Definitions/Property.cs | 21 +++++++++++-------- 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs index a6a08b2..b41de37 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs @@ -68,7 +68,14 @@ public abstract unsafe class BaseUObject(nint ptr, IUnrealFactory public ProcessEventResult ProcessEvent(string Name, List Params, out IFunctionParam? Return) { Return = null; - var Function = ClassPrivate.GetFunction(Name); + // var Function = ClassPrivate.GetFunction(Name); + var CurrentClass = ClassPrivate; + IUFunction? Function = null; + while (Function == null && CurrentClass != null) + { + Function = CurrentClass.GetFunction(Name); + CurrentClass = CurrentClass.GetSuperClass(); + } if (Function == null) { return ProcessEventResult.CouldNotFindFunction; diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TArray.cs b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TArray.cs index 8065fa0..1c69ec2 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TArray.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TArray.cs @@ -12,6 +12,11 @@ public unsafe struct TArray where T : unmanaged public T* AllocatorInstance; public int ArrayNum; public int ArrayMax; + + public TArrayList ToManaged(IUnrealMemoryInternal Memory) + { + fixed (TArray* self = &this) return new TArrayList(self, Memory); + } } public unsafe class TArrayListStatic diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs index 115884f..53ddc3b 100644 --- a/UE.Toolkit.DumperMod/Definitions/Builtins.cs +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -78,6 +78,17 @@ public interface ITypeRepr where TRepr: unmanaged public abstract class ObjectImpl(IUObject inner) { public IUObject Inner { get; } = inner; + + protected Dictionary CreateFieldOffsets() + { + Dictionary Result = []; + foreach (var Prop in Inner.ClassPrivate.PropertyLink) + if (!Result.ContainsKey(Prop.NamePrivate)) + Result[Prop.NamePrivate] = Prop.Offset_Internal; + return Result; + } + + protected abstract int GetFieldOffset(string Name); } """); @@ -100,6 +111,8 @@ public static string SanitizeName(string name) name = name.Replace(')', '_'); name = name.Replace('[', '_'); name = name.Replace(']', '_'); + name = name.Replace("+", "_"); + name = name.Replace("'", "_"); //name = name.Replace('>', '_'); if (name == "object") name = "_object"; @@ -133,4 +146,10 @@ public static string SanitizeForFunctionName(string name) name = name.Replace(">", "_"); return name; } + + public static string SanitizeForEnum(string name) + { + name = name.Replace(".", "_"); + return name; + } } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Class.cs b/UE.Toolkit.DumperMod/Definitions/Class.cs index 2366f76..7b05317 100644 --- a/UE.Toolkit.DumperMod/Definitions/Class.cs +++ b/UE.Toolkit.DumperMod/Definitions/Class.cs @@ -76,12 +76,9 @@ public override string Serialize(Context context) sb.AppendLine("\tprivate static Dictionary? FieldOffsets;"); var callBaseCtor = SuperDef != null ? "base(inner, false)" : "base(inner)"; sb.AppendLine($"\tpublic {DisplayNameCS}(IUObject inner, bool genOffsets = true) : {callBaseCtor}\n\t{{"); - sb.AppendLine(""" - if (genOffsets) - FieldOffsets ??= Inner.ClassPrivate.PropertyLink - .Select(x => (x.NamePrivate, x.Offset_Internal)).ToDictionary(); -"""); - sb.AppendLine("\t}"); + sb.AppendLine("\t\tif (genOffsets) FieldOffsets ??= CreateFieldOffsets();"); + sb.AppendLine("\t}\n"); + sb.AppendLine("\tprotected override int GetFieldOffset(string Name) => FieldOffsets![Name];\n"); var ReprNewModifier = SuperDef != null ? "new " : string.Empty; sb.AppendLine($"\tpublic {ReprNewModifier}unsafe {DisplayNameRepr}* Repr => ({DisplayNameRepr}*)Inner.Ptr;\n"); foreach (var prop in Properties) diff --git a/UE.Toolkit.DumperMod/Definitions/Enum.cs b/UE.Toolkit.DumperMod/Definitions/Enum.cs index 12f4d6e..06666ad 100644 --- a/UE.Toolkit.DumperMod/Definitions/Enum.cs +++ b/UE.Toolkit.DumperMod/Definitions/Enum.cs @@ -100,5 +100,5 @@ public string Serialize(Context context) public ObjectType Type => ObjectType.Enum; - private static string SanitizeEntryName(string name) => Builtins.SanitizeName(name.Split("::").Last()); + private static string SanitizeEntryName(string name) => Builtins.SanitizeForEnum(Builtins.SanitizeName(name.Split("::").Last())); } \ No newline at end of file diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs index 00e6a3a..5e60858 100644 --- a/UE.Toolkit.DumperMod/Definitions/Property.cs +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -43,6 +43,8 @@ public override string Serialize(Context context) public abstract class BasePropertyFactory(Context context) { + protected virtual bool AllowGenerationForProperty(IFProperty prop) => true; + public List ResolveProperties(IEnumerable propLink, int SuperStructEnd) { List props = []; @@ -52,6 +54,7 @@ public List ResolveProperties(IEnumerable pro // PropertyLink contains a list of fields declared by object in ascending order, then from it's super // object and so on. if (prop.Offset_Internal < SuperStructEnd) break; + if (!AllowGenerationForProperty(prop)) continue; var newProp = ResolveProperty(prop); var numSameName = props.Count(x => x.Name.StartsWith(newProp.Name)); // Compare against sanitized name, since that's what props are using. if (numSameName > 0) newProp.Name = $"{newProp.Name}_{numSameName + 1}"; @@ -254,23 +257,23 @@ private Func GetPropAccessor(IFProperty prop, Func $"get => *({getTypeName()}*)(Inner.Ptr + FieldOffsets![\"{raw}\"]);"; + return (_, raw) => $"get => *({getTypeName()}*)(Inner.Ptr + GetFieldOffset(\"{raw}\"));"; // Pass by reference case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" or "WeakObjectProperty" or "SetProperty" or "DelegateProprety" or "MulticastInlineDelegateProperty" or "MulticastSparseDelegateProperty": - return (_, raw) => $"get => ({getTypeName()})(Inner.Ptr + FieldOffsets![\"{raw}\"]);"; + return (_, raw) => $"get => ({getTypeName()})(Inner.Ptr + GetFieldOffset(\"{raw}\"));"; case "BoolProperty": var BoolProp = context.Factory.CreateFBoolProperty(prop.Ptr); return (BoolProp.FieldMask == byte.MaxValue) switch { - true => (_, raw) => $"get => *(bool*)(Inner.Ptr + FieldOffsets![\"{raw}\"]);", - false => (_, raw) => $"get => (*(byte*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) & {BoolProp.FieldMask}) == 0;" + true => (_, raw) => $"get => *(bool*)(Inner.Ptr + GetFieldOffset(\"{raw}\"));", + false => (_, raw) => $"get => (*(byte*)(Inner.Ptr + GetFieldOffset(\"{raw}\")) & {BoolProp.FieldMask}) == 0;" }; case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": return (_, raw) => - $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + FieldOffsets![\"{raw}\"])));"; + $"get => new(Inner.GetFactory().CreateUObject(*(nint*)(Inner.Ptr + GetFieldOffset(\"{raw}\"))));"; default: return (_, _) => $"get => throw new NotSupportedException(\"!! GET TODO {className} !!\");"; } @@ -285,7 +288,7 @@ private Func GetPropAccessor(IFProperty prop, Func $"set => *({getTypeName()}*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value;"; + return (_, raw) => $"set => *({getTypeName()}*)(Inner.Ptr + GetFieldOffset(\"{raw}\")) = value;"; // Pass by reference, don't create mutator case "StructProperty" or "StrProperty" or "ArrayProperty" or "MapProperty" or "SoftObjectProperty" or "SoftClassProperty" or "TextProperty" @@ -296,11 +299,11 @@ private Func GetPropAccessor(IFProperty prop, Func (_, raw) => $"set => *(bool*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value;", - false => (param, raw) => $"set => *(byte*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) ^= (byte)(Convert.ToByte({param} != value) * {BoolProp.FieldMask});" + true => (_, raw) => $"set => *(bool*)(Inner.Ptr + GetFieldOffset(\"{raw}\")) = value;", + false => (param, raw) => $"set => *(byte*)(Inner.Ptr + GetFieldOffset(\"{raw}\")) ^= (byte)(Convert.ToByte({param} != value) * {BoolProp.FieldMask});" }; case "ObjectProperty" or "ClassProperty" or "ClassPtrProperty": - return (_, raw) => $"set => *(nint*)(Inner.Ptr + FieldOffsets![\"{raw}\"]) = value.Inner.Ptr;"; + return (_, raw) => $"set => *(nint*)(Inner.Ptr + GetFieldOffset(\"{raw}\")) = value.Inner.Ptr;"; default: return (_, _) => $"set => throw new NotSupportedException(\"!! SET TODO {className} !!\");"; } From b822d144d4f13853aa8ecb36c3470690f34a3523 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Sun, 26 Jul 2026 08:36:02 +0800 Subject: [PATCH 08/10] Handle duplicate function param and enum variant names --- UE.Toolkit.DumperMod/Definitions/Builtins.cs | 6 +++ UE.Toolkit.DumperMod/Definitions/Enum.cs | 13 ++++- UE.Toolkit.DumperMod/Definitions/Function.cs | 19 ++++++- UE.Toolkit.DumperMod/Definitions/Property.cs | 52 +++++++++++++++----- UE.Toolkit.DumperMod/Dumper.cs | 1 - UE.Toolkit.DumperMod/Mod.cs | 4 ++ 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/UE.Toolkit.DumperMod/Definitions/Builtins.cs b/UE.Toolkit.DumperMod/Definitions/Builtins.cs index 53ddc3b..1bed411 100644 --- a/UE.Toolkit.DumperMod/Definitions/Builtins.cs +++ b/UE.Toolkit.DumperMod/Definitions/Builtins.cs @@ -100,6 +100,8 @@ public static string SanitizeName(string name) if (parts.Last().Length == 32 && parts.Length > 2) { name = string.Join(string.Empty, parts[..^2]); + // SMT5V: Property that's a sequence of underscores becomes an empty string + if (name == string.Empty) name = "EMPTY"; } name = name.Replace(' ', '_'); @@ -134,8 +136,12 @@ public static string SanitizeForTypename(string name) "base" => "Base", "bool" => "Bool", "float" => "Float", + "double" => "Double", "ref" => "Ref", "int" => "Int", + "return" => "Return", + "string" => "String", + "Inner" => "Inner_", _ => name }; } diff --git a/UE.Toolkit.DumperMod/Definitions/Enum.cs b/UE.Toolkit.DumperMod/Definitions/Enum.cs index 06666ad..a707241 100644 --- a/UE.Toolkit.DumperMod/Definitions/Enum.cs +++ b/UE.Toolkit.DumperMod/Definitions/Enum.cs @@ -89,9 +89,20 @@ public string Serialize(Context context) } sb.AppendLine($"public enum {Builtins.SanitizeName(Name)} : {UnderlyingType}\n{{"); + Dictionary UsageCount = []; foreach (var entry in Entries) { - sb.AppendLine($" {SanitizeEntryName(entry.Key)} = {entry.Value},"); + var variantName = SanitizeEntryName(entry.Key); + if (UsageCount.TryGetValue(variantName, out var count)) + { + variantName += $"_{count}"; + UsageCount[variantName] = count + 1; + } + else + { + UsageCount[variantName] = 1; + } + sb.AppendLine($" {variantName} = {entry.Value},"); } sb.AppendLine("}"); diff --git a/UE.Toolkit.DumperMod/Definitions/Function.cs b/UE.Toolkit.DumperMod/Definitions/Function.cs index 94bed29..ba40e99 100644 --- a/UE.Toolkit.DumperMod/Definitions/Function.cs +++ b/UE.Toolkit.DumperMod/Definitions/Function.cs @@ -133,6 +133,7 @@ public List ResolveFunctions(IUClass uclass) List BodyPostfix = []; ReturnValueDefinition? ReturnValue = null; Func? returnTypeName = null; + Dictionary nameUse = []; foreach (var Field in x.ChildProperties) { var Param = context.Factory.CreateFProperty(Field.Ptr); @@ -144,7 +145,21 @@ public List ResolveFunctions(IUClass uclass) break; } var paramName = Builtins.SanitizeForTypename(Builtins.SanitizeName(Param.NamePrivate)); + if (nameUse.TryGetValue(paramName, out var useCount)) + { + paramName += $"_{useCount}"; + nameUse[paramName] = useCount + 1; + } + else + { + nameUse[paramName] = 1; + } var propTypeName = PropFactory.GetPropTypenameFunctionParam(Param); + if (propTypeName == null) + { + Log.Error($"{nameof(ResolveFunctions)} || Could not determine data types for all parameters in '{x.NamePrivate}'"); + return null; + } HeadParams.Add(new ParameterHeadDefinition(paramName, propTypeName, Param )); BodyParams.Add(new ParameterBodyDefinition(paramName, propTypeName, Param, false )); if (Param.ClassPrivate.Name is "ObjectProperty" or "ClassProperty" or "ClassPtrProperty") @@ -158,7 +173,9 @@ public List ResolveFunctions(IUClass uclass) var funcName = x.NamePrivate.ToString(); var funcNameSanitized = Builtins.SanitizeForFunctionName(Builtins.SanitizeName(funcName)); return new FunctionDefinition(funcNameSanitized, funcName, returnTypeName, HeadParams, BodyParams, ReturnValue, BodyPrefix, BodyPostfix); - }).ToList(); + }) + .Where(x => x != null) + .ToList(); } } diff --git a/UE.Toolkit.DumperMod/Definitions/Property.cs b/UE.Toolkit.DumperMod/Definitions/Property.cs index 5e60858..015d8e3 100644 --- a/UE.Toolkit.DumperMod/Definitions/Property.cs +++ b/UE.Toolkit.DumperMod/Definitions/Property.cs @@ -56,6 +56,7 @@ public List ResolveProperties(IEnumerable pro if (prop.Offset_Internal < SuperStructEnd) break; if (!AllowGenerationForProperty(prop)) continue; var newProp = ResolveProperty(prop); + if (newProp == null) continue; var numSameName = props.Count(x => x.Name.StartsWith(newProp.Name)); // Compare against sanitized name, since that's what props are using. if (numSameName > 0) newProp.Name = $"{newProp.Name}_{numSameName + 1}"; props.Add(newProp); @@ -64,7 +65,7 @@ public List ResolveProperties(IEnumerable pro return props; } - protected abstract BasePropertyDefintion ResolveProperty(IFProperty prop); + protected abstract BasePropertyDefintion? ResolveProperty(IFProperty prop); private static string SafelyCreateGenericType(string Type, bool makeGeneric) => makeGeneric ? Type : $"nint /* {Type} */"; @@ -79,7 +80,7 @@ private static string SafelyCreatePointerType(string Type, bool makeGeneric) /// /// /// C++/C# property type name. - public Func GetPropTypenameStruct(IFProperty prop, bool makeGenerics) + public Func? GetPropTypenameStruct(IFProperty prop, bool makeGenerics) { var className = prop.ClassPrivate.Name; switch (className) @@ -130,7 +131,13 @@ public Func GetPropTypenameStruct(IFProperty prop, bool makeGenerics) case "WeakObjectProperty": return () => "FWeakObjectPtr"; case "ObjectProperty": - var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + var propClass = context.Factory.Cast(prop).PropertyClass; + if (propClass.Ptr == nint.Zero) + { + Log.Error($"{nameof(GetPropTypenameStruct)} || {prop.NamePrivate}->PropertyClass (0x{prop.Ptr:x}) is null"); + return null; + } + var objPropType = propClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.GetUnmanagedTypeName()}*" : $"{objPropType}*"); case "SoftObjectProperty": var softObjPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); @@ -153,8 +160,14 @@ public Func GetPropTypenameStruct(IFProperty prop, bool makeGenerics) return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.GetUnmanagedTypeName()}*" : classPropType); case "EnumProperty": var enumProp = context.Factory.Cast(prop); - var enumPropName = enumProp.Enum.NamePrivate.ToString(); - new EnumFactory(context, ObjectType.Enum, enumProp.Enum, context.Classes.GetPropertyTypeName(enumProp.UnderlyingProp)).Register(); + var enumClass = enumProp.Enum; + if (enumClass.Ptr == nint.Zero) + { + Log.Error($"{nameof(GetPropTypenameStruct)} || {prop.NamePrivate}->Enum (0x{prop.Ptr:x}) is null"); + return null; + } + var enumPropName = enumClass.NamePrivate.ToString(); + new EnumFactory(context, ObjectType.Enum, enumClass, context.Classes.GetPropertyTypeName(enumProp.UnderlyingProp)).Register(); return () => Builtins.SanitizeName(enumPropName); case "MapProperty": var mapProp = context.Factory.Cast(prop); @@ -239,10 +252,11 @@ public Func GetPropTypenameStruct(IFProperty prop, bool makeGenerics) public class PropertyStructFactory(Context context) : BasePropertyFactory(context) { - protected override BasePropertyDefintion ResolveProperty(IFProperty prop) + protected override BasePropertyDefintion? ResolveProperty(IFProperty prop) { var (name, size, offset, _) = GetBaseInfo(prop); - return new PropertyStructDefinition(name, size, offset, GetPropTypenameStruct(prop, false)); + var typename = GetPropTypenameStruct(prop, false); + return typename != null ? new PropertyStructDefinition(name, size, offset, typename) : null; } } @@ -315,7 +329,7 @@ private Func GetClassPropTypenameManaged(IUClass classPropClass) return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(classPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : classPropType); } - private Func GetPropTypenameClass(IFProperty prop) + private Func? GetPropTypenameClass(IFProperty prop) { var className = prop.ClassPrivate.Name; switch (className) @@ -329,14 +343,20 @@ private Func GetPropTypenameClass(IFProperty prop) case "ClassProperty" or "ClassPtrProperty": return GetClassPropTypenameManaged(context.Factory.Cast(prop).MetaClass); case "ObjectProperty": - var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + var propClass = context.Factory.Cast(prop).PropertyClass; + if (propClass.Ptr == nint.Zero) + { + Log.Error($"{nameof(GetPropTypenameClass)} || {prop.NamePrivate}->PropertyClass (0x{prop.Ptr:x}) is null"); + return null; + } + var objPropType = propClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); default: return GetPropTypenameStruct(prop, true); } } - internal Func GetPropTypenameFunctionParam(IFProperty prop) + internal Func? GetPropTypenameFunctionParam(IFProperty prop) { var className = prop.ClassPrivate.Name; switch (className) @@ -344,17 +364,25 @@ internal Func GetPropTypenameFunctionParam(IFProperty prop) case "ClassProperty" or "ClassPtrProperty": return GetClassPropTypenameManaged(context.Factory.Cast(prop).MetaClass); case "ObjectProperty": - var objPropType = context.Factory.Cast(prop).PropertyClass.NamePrivate.ToString(); + var propClass = context.Factory.Cast(prop).PropertyClass; + if (propClass.Ptr == nint.Zero) + { + Log.Error($"{nameof(GetPropTypenameClass)} || {prop.NamePrivate}->PropertyClass (0x{prop.Ptr:x}) is null"); + return null; + } + var objPropType = propClass.NamePrivate.ToString(); return () => Builtins.SanitizeName(context.Registry.Structs.TryGetValue(objPropType, out var knownStruct) ? $"{knownStruct.DisplayName}" : $"{objPropType}"); default: return GetPropTypenameStruct(prop, true); } } - protected override BasePropertyDefintion ResolveProperty(IFProperty prop) + protected override BasePropertyDefintion? ResolveProperty(IFProperty prop) { var (name, size, offset, _) = GetBaseInfo(prop); var propTypename = GetPropTypenameClass(prop); + // Cannot continue + if (propTypename == null) return null; var getAccessor = GetPropAccessor(prop, propTypename); var getMutator = GetPropMutator(prop, propTypename); return new PropertyClassDefinition(name, prop.NamePrivate, size, offset, propTypename, getAccessor, getMutator); diff --git a/UE.Toolkit.DumperMod/Dumper.cs b/UE.Toolkit.DumperMod/Dumper.cs index 659adf1..3dc5bcf 100644 --- a/UE.Toolkit.DumperMod/Dumper.cs +++ b/UE.Toolkit.DumperMod/Dumper.cs @@ -50,7 +50,6 @@ public void DumpObjects() var singleFileOutput = string.IsNullOrEmpty(Mod.Config.SingleFileOutputName) ? Path.Join(Context.DumpDirectory, "Types.cs") : Path.Join(Context.DumpDirectory, $"{Mod.Config.SingleFileOutputName.Replace(".cs", string.Empty)}.cs"); - File.WriteAllText(singleFileOutput, sb!.ToString()); Log.Information($"{numDumped} objects dumped in {sw.ElapsedMilliseconds}ms.\nOutput File: {singleFileOutput}"); } diff --git a/UE.Toolkit.DumperMod/Mod.cs b/UE.Toolkit.DumperMod/Mod.cs index 765bf65..11ae6b6 100644 --- a/UE.Toolkit.DumperMod/Mod.cs +++ b/UE.Toolkit.DumperMod/Mod.cs @@ -48,6 +48,10 @@ public Mod(ModContext context) var dumpDir = Path.Join(_modLoader.GetDirectoryForModId(_modConfig.ModId), "dump", _modLoader.GetAppConfig().AppId); + if (!Directory.Exists(dumpDir)) + { + Directory.CreateDirectory(dumpDir); + } _dumper = new(factory!, objs!, strs!, classes!, dumpDir, _essentials); } From 23f7b38e0798f36501b1cc7fbc699f9102eda820 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Sun, 26 Jul 2026 10:25:57 +0800 Subject: [PATCH 09/10] Fixed UClass->FuncMap retrieval on UE5 --- README.md | 1 + .../Factories/UE4_27_2/UnrealFactory.cs | 1 - .../Unreal/Factories/UE5_0_3/UnrealFactory.cs | 214 ++++-------------- .../Unreal/Factories/UE5_2_1/UnrealFactory.cs | 6 +- .../Unreal/Factories/UE5_4_4/UnrealFactory.cs | 6 +- .../Unreal/Factories/UE5_6_1/UnrealFactory.cs | 6 +- .../Unreal/Factories/UE5_7_4/UnrealFactory.cs | 6 +- .../Types/Unreal/UE5_0_3/UClass.cs | 34 +++ .../Types/Unreal/UE5_2_1/UClass.cs | 34 +++ .../Common/GameConfigs/GameConfig.cs | 3 +- .../Common/GameConfigs/Games/UE5_0_3.cs | 2 +- .../Common/GameConfigs/Games/UE5_1_1.cs | 26 +++ 12 files changed, 150 insertions(+), 189 deletions(-) create mode 100644 UE.Toolkit.Core/Types/Unreal/UE5_0_3/UClass.cs create mode 100644 UE.Toolkit.Core/Types/Unreal/UE5_2_1/UClass.cs create mode 100644 UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_1_1.cs diff --git a/README.md b/README.md index 76832db..9566034 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ | Custom Constructor |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Add Properties |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Register Struct |✅|✅|✅|✅|✅|✅|✅|✅|✅ +| Call Blueprint Methods |✅|✅|✅|✅|✅|✅|✅|✅|✅ Features marked with ❔ are currently untested. diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs index 595d218..ea14f3e 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs @@ -468,7 +468,6 @@ public class IUFieldEnumerable(IUField? initial) { private IUField? _current = initial; private bool isInitial = true; - public bool MoveNext() { if (isInitial) diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs index e18ad5b..fc2ccdd 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs @@ -1,181 +1,47 @@ +using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Factories.UE5_2_1; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UClass = UE.Toolkit.Core.Types.Unreal.UE5_0_3.UClass; +using UFunction = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UFunction; namespace UE.Toolkit.Core.Types.Unreal.Factories.UE5_0_3; -public class UnrealFactory : BaseUnrealFactory +public class UnrealFactory : UE.Toolkit.Core.Types.Unreal.Factories.UE5_2_1.UnrealFactory { - public override IntPtr SizeOf() - { - throw new NotImplementedException(); - } - - public override IFProperty CreateFProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFBoolProperty CreateFBoolProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFByteProperty CreateFByteProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFEnumProperty CreateFEnumProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFObjectProperty CreateFObjectProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFSoftClassProperty CreateFSoftClassProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFClassProperty CreateFClassProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFStructProperty CreateFStructProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFMapProperty CreateFMapProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFInterfaceProperty CreateFInterfaceProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFArrayProperty CreateFArrayProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFSetProperty CreateFSetProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFOptionalProperty CreateFOptionalProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFDelegateProperty CreateFDelegateProperty(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUObjectArray CreateUObjectArray(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUObject CreateUObject(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUClass CreateUClass(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUScriptStruct CreateUScriptStruct(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUEnum CreateUEnum(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUField CreateUField(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUStruct CreateUStruct(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUUserDefinedEnum CreateUUserDefinedEnum(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUFunction CreateUFunction(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFFieldClass CreateFFieldClass(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFField CreateFField(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFFieldVariant CreateFFieldVariant(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFStructParams CreateFStructParams(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFPropertyParams CreateFPropertyParams(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFGenericPropertyParams CreateFGenericPropertyParams(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IFWorldContext CreateFWorldContext(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUEngine CreateUEngine(IntPtr ptr) - { - throw new NotImplementedException(); - } - - public override IUGameInstance CreateUGameInstance(IntPtr ptr) - { - throw new NotImplementedException(); - } + public override IUClass CreateUClass(nint ptr) => new UClass_UE5_0_3(ptr, this, Memory); +} - public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters() - { - throw new NotImplementedException(); - } - - public override IFActorSpawnParameters CreateFActorSpawnParameters() - { - throw new NotImplementedException(); - } +public unsafe class UClass_UE5_0_3(nint ptr, IUnrealFactory factory, IUnrealMemoryInternal memory) + : UStruct_UE5_2_1(ptr, factory, memory), IUClass +{ + private readonly UClass* _self = (UClass*)ptr; + + public IUClass? GetSuperClass() + => _self->_super.super_struct != null ? _factory.CreateUClass((nint)_self->_super.super_struct) : null; + + public IUFunction? GetFunction(string Name) + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->func_map), factory.Memory + ); + return FuncMapDict.TryGetValue(new(Name), out var Function) + ? factory.CreateUFunction((nint)Function.Value->Value) + : null; + } + + public IEnumerable GetFunctions() + { + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->func_map), _factory.Memory + ); + return FuncMapDict.Values.Select(x => _factory.CreateUFunction((nint)x.Value->Value)); + } + + public IUObject? ClassDefaultObject + => _self->class_default_obj != null ? factory.CreateUObject((nint)_self->class_default_obj) : null; + + public nint Constructor => _self->class_ctor; + public EClassFlags ClassFlags => _self->class_flags; + public EClassCastFlags ClassCastFlags => _self->class_cast_flags; } \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs index 02d35d2..60689ef 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_2_1/UnrealFactory.cs @@ -148,11 +148,11 @@ public unsafe class UClass_UE5_2_1(nint ptr, IUnrealFactory factory, IUnrealMemo public IUFunction? GetFunction(string Name) { - var FuncMapDict = new TMapDictionary( - (TMap*)(&_self->FuncMap), factory.Memory + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), factory.Memory ); return FuncMapDict.TryGetValue(new(Name), out var Function) - ? factory.CreateUFunction((nint)Function.Value) + ? factory.CreateUFunction((nint)Function.Value->Value) : null; } diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs index c863481..c0dc7b9 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_4_4/UnrealFactory.cs @@ -466,11 +466,11 @@ public unsafe class UClass_UE5_4_4(nint ptr, IUnrealFactory factory, IUnrealMemo public IUFunction? GetFunction(string Name) { - var FuncMapDict = new TMapDictionary( - (TMap*)(&_self->FuncMap), factory.Memory + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), factory.Memory ); return FuncMapDict.TryGetValue(new(Name), out var Function) - ? factory.CreateUFunction((nint)Function.Value) + ? factory.CreateUFunction((nint)Function.Value->Value) : null; } diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs index 2ceb828..8f79910 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_6_1/UnrealFactory.cs @@ -127,11 +127,11 @@ public unsafe class UClass_UE5_6_1(nint ptr, IUnrealFactory factory, IUnrealMemo public IUFunction? GetFunction(string Name) { - var FuncMapDict = new TMapDictionary( - (TMap*)(&_self->FuncMap), factory.Memory + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), factory.Memory ); return FuncMapDict.TryGetValue(new(Name), out var Function) - ? factory.CreateUFunction((nint)Function.Value) + ? factory.CreateUFunction((nint)Function.Value->Value) : null; } diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs index 63e7671..d3f218f 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/UE5_7_4/UnrealFactory.cs @@ -213,11 +213,11 @@ public unsafe class UClass_UE5_7_4(nint ptr, IUnrealFactory factory, IUnrealMemo public IUFunction? GetFunction(string Name) { - var FuncMapDict = new TMapDictionary( - (TMap*)(&_self->FuncMap), factory.Memory + var FuncMapDict = new TMapDictionary>( + (TMap>*)(&_self->FuncMap), factory.Memory ); return FuncMapDict.TryGetValue(new(Name), out var Function) - ? factory.CreateUFunction((nint)Function.Value) + ? factory.CreateUFunction((nint)Function.Value->Value) : null; } diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_0_3/UClass.cs b/UE.Toolkit.Core/Types/Unreal/UE5_0_3/UClass.cs new file mode 100644 index 0000000..95b8052 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE5_0_3/UClass.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Unreal.UE4_27_2; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UField = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UField; +using UObjectBase = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UObjectBase; +using UScriptStruct = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UScriptStruct; +using UStruct = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UStruct; + +namespace UE.Toolkit.Core.Types.Unreal.UE5_0_3; + +[StructLayout(LayoutKind.Explicit, Size = 0x230)] +public unsafe struct UClass +{ + [FieldOffset(0x0)] public UStruct _super; + [FieldOffset(0xb0)] public IntPtr class_ctor; // InternalConstructor => UClassName::UClassName + [FieldOffset(0xb8)] public IntPtr class_vtable_helper_ctor_caller; + [FieldOffset(0xc0)] public IntPtr class_add_ref_objects; + [FieldOffset(0xc8)] public uint class_status; // ClassUnique : 31, bCooked : 1 + [FieldOffset(0xcc)] public uint FirstOwnedClassRep; + [FieldOffset(0xd0)] public bool bCooked; + [FieldOffset(0xd1)] public bool bLayoutChanging; + [FieldOffset(0xd4)] public EClassFlags class_flags; + [FieldOffset(0xd8)] public EClassCastFlags class_cast_flags; + [FieldOffset(0xe0)] public UClass* class_within; // type of object containing the current object + [FieldOffset(0xe8)] public FName class_conf_name; + [FieldOffset(0x100)] public TArray net_fields; + [FieldOffset(0x110)] public UObjectBase* class_default_obj; // Default object of type described in UClass instance + [FieldOffset(0x118)] public nint sparse_class_data; + [FieldOffset(0x120)] public UScriptStruct* sparse_class_data_struct; + [FieldOffset(0x128)] public TMap func_map; + [FieldOffset(0x178)] public TMap super_func_map; + [FieldOffset(0x1d0)] public TArray interfaces; + [FieldOffset(0x220)] public TArray native_func_lookup; +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_2_1/UClass.cs b/UE.Toolkit.Core/Types/Unreal/UE5_2_1/UClass.cs new file mode 100644 index 0000000..d0f89d0 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE5_2_1/UClass.cs @@ -0,0 +1,34 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Unreal.UE4_27_2; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UField = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UField; +using UObjectBase = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UObjectBase; +using UScriptStruct = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UScriptStruct; +using UStruct = UE.Toolkit.Core.Types.Unreal.UE4_27_2.UStruct; + +namespace UE.Toolkit.Core.Types.Unreal.UE5_2_1; + +[StructLayout(LayoutKind.Explicit, Size = 0x220)] +public unsafe struct UClass +{ + [FieldOffset(0x0)] public UStruct _super; + [FieldOffset(0xb0)] public IntPtr class_ctor; // InternalConstructor => UClassName::UClassName + [FieldOffset(0xb8)] public IntPtr class_vtable_helper_ctor_caller; + [FieldOffset(0xc0)] public IntPtr class_add_ref_objects; + [FieldOffset(0xc8)] public uint class_status; // ClassUnique : 31, bCooked : 1 + [FieldOffset(0xcc)] public uint FirstOwnedClassRep; + [FieldOffset(0xd0)] public bool bCooked; + [FieldOffset(0xd1)] public bool bLayoutChanging; + [FieldOffset(0xd4)] public EClassFlags class_flags; + [FieldOffset(0xd8)] public EClassCastFlags class_cast_flags; + [FieldOffset(0xe0)] public UClass* class_within; // type of object containing the current object + [FieldOffset(0xe8)] public FName class_conf_name; + [FieldOffset(0x100)] public TArray net_fields; + [FieldOffset(0x110)] public UObjectBase* class_default_obj; // Default object of type described in UClass instance + [FieldOffset(0x118)] public nint sparse_class_data; + [FieldOffset(0x120)] public UScriptStruct* sparse_class_data_struct; + [FieldOffset(0x128)] public TMap func_map; + [FieldOffset(0x180)] public TMap super_func_map; + [FieldOffset(0x1d8)] public TArray interfaces; + [FieldOffset(0x210)] public TArray native_func_lookup; +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs index 64f7729..8d056a0 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs @@ -36,7 +36,8 @@ public static void SetGame(string appId, IUnrealEssentials essentials) { "++UE4+Release-4.27" => new UE4_27_2_P3R(), "++UE5+Release-5.0" => new UE5_0_3(), - "++UE5+Release-5.1" or "++UE5+Release-5.2" => new UE5_2_1(), + "++UE5+Release-5.1" => new UE5_1_1(), + "++UE5+Release-5.2" => new UE5_2_1(), "++UE5+Release-5.3" => new UE5_3_2(), "++UE5+Release-5.6" => new UE5_6_1(), "++UE5+Release-5.7" => new UE5_7_4(), diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs index 58b7a22..daf302f 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs @@ -1,6 +1,6 @@ using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories; -using UE.Toolkit.Core.Types.Unreal.Factories.UE5_2_1; +using UE.Toolkit.Core.Types.Unreal.Factories.UE5_0_3; using UE.Toolkit.Interfaces; using UE.Toolkit.Reloaded.Reflection; using UE.Toolkit.Reloaded.Reflection.UE5_2_1; diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_1_1.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_1_1.cs new file mode 100644 index 0000000..167b41f --- /dev/null +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_1_1.cs @@ -0,0 +1,26 @@ +using UE.Toolkit.Core.Types.Unreal.Factories; +using UE.Toolkit.Core.Types.Unreal.Factories.UE5_0_3; +using UE.Toolkit.Interfaces; +using UE.Toolkit.Reloaded.Reflection; +using UE.Toolkit.Reloaded.Reflection.UE5_2_1; +using UE.Toolkit.Reloaded.Unreal; + +namespace UE.Toolkit.Reloaded.Common.GameConfigs.Games; + +public class UE5_1_1 : UE5_4_4_ClairObscur +{ + public override string Id => "UE5_1_1"; + public override IUnrealFactory Factory { get; } = new UnrealFactory(); + public override IUnrealMemory Memory { get; } = new UnrealMemory(); + public override IPropertyFlagsBuilder FlagsBuilder { get; } = new PropertyFlagsBuilder(); + + public override BasePropertyFactory PropertyFactory(IUnrealClasses classes) + => new PropertyFactory(Factory, Memory, classes, FlagsBuilder); + + public override BaseTypeFactory TypeFactory(IUnrealClasses classes) + => new TypeFactory(Factory, Memory, classes, FlagsBuilder); + + public override Type GetFText() => typeof(UE.Toolkit.Core.Types.Unreal.UE4_27_2.FText); + + public override unsafe int GetFTextSize() => sizeof(UE.Toolkit.Core.Types.Unreal.UE4_27_2.FText); +} \ No newline at end of file From afc1b7b87048e6e54df7beda315f388424984383 Mon Sep 17 00:00:00 2001 From: Rirurin Date: Mon, 27 Jul 2026 08:48:12 +0800 Subject: [PATCH 10/10] Updated README for 1.10.0 --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9566034..e40484c 100644 --- a/README.md +++ b/README.md @@ -168,10 +168,10 @@ First, we add an `Item` element with an `id` attribute. The ID is which item in Inside the `Item` element, we're back to what's already been covered. In this example, items have an `EquipID` property and we're setting the `EquipID` of the __2nd__ item to `100`. #### Editing Items in a DataTable -Finally, _DataTables_! There's only two notable differences: the `row-struct` attribute and `id`s are now row names. This example is from _Clair Obscur_'s `DT_jRPG_CharacterDefinitions.uasset`. +Finally, _DataTables_! There's only one notable difference: the `id`s are now row names. This example is from _Clair Obscur_'s `DT_jRPG_CharacterDefinitions.uasset`. ```xml - + @@ -186,10 +186,10 @@ Finally, _DataTables_! There's only two notable differences: the `row-struct` a A DataTable's __Class__ is... `DataTable`! Who could've guessed? As such, the __Root Element__ in our XML has to be `DataTable`. -But, we also need to specify the __Class (Struct)__ for the __Items (Rows)__ in the DataTable so we can edit them. You do that by adding a `row-struct` attribute with the DataTable's __RowStruct__ name, `S_jRPG_CharacterDefinition` in this example. - For our `Item` element's ID, instead of a number we use the __Row's name__ that we want to edit. Inside the `Item` element, it's no different than lists from earlier. +You can optionally override the data type for a DataTable by specifying a struct name for the `row-struct` attribute on the root element. This used to be required in previous versions of UE Toolkit, but the data type is automatically retrived now. + That covers everything, congrats on finishing this 🎉🎉🎉! #### Misc. Stuff (Enums, Type Hints) @@ -206,6 +206,8 @@ Some Unreal types may not be generated and need to be supplied. `UE.Toolkit.Core that were missing in my testing. Add it to your project using **NuGet** and add `UE.Toolkit.Core.Types.Unreal;` in the `File Usings` config before dumping. +The dumper supports two different schemas: a `Structs Only` schema that only generates the types as unmanaged structs and `Structs and Classes`, which generates C# classes for types that inherit from UObject. Classes will also generate methods that are exposed to blueprints. + ## Code Only Features ### Using Unreal's Memory Allocator `FMemory`