diff --git a/README.md b/README.md index cfa245e..f8c7412 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,15 @@ - Add new properties to a class/struct's type information. - Register a new struct into the type information system. -## Supported Engine Version and Games - -Object XML requires game-specific support with an extension mod. Supported games [are listed here](#installing-the-ue-toolkit-extension-mod). +## Supported Engine Versions | Feature | 4.27 | 5.0 | 5.1 | 5.2 | 5.3 | 5.4 | 5.5 | 5.6 | 5.7 | - | - | - | - | - | - | - | - | - | - | | Object Logging |✅|✅|✅|✅|✅|✅|✅|✅|✅ -| Object Editing |✅|❔|❔|❔|❔|✅|❔|❔|❔ +| Object Editing |✅|❔|❔|✅|❔|✅|❔|❔|❔ | `FMemory` Functions |✅|✅|✅|✅|✅|✅|✅|✅|✅ | Dumper |✅|✅|✅|✅|️️️️️️✅|✅|✅|✅️|️️️️✅️ -| Property Editing (Object XML) |✅|❔|❔|❔|❔|✅|❔|❔|❔ +| Property Editing (Object XML) |✅|❔|❔|✅|❔|✅|❔|❔|❔ | Add List Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ | Add Map Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|❔ | Type Information |✅|✅|✅|✅|✅|✅|✅|✅|✅ @@ -50,11 +48,6 @@ I recommend the P3R guides, since they're the newest and cover GamePass. __You o 1. Go to [Releases](https://github.com/RyoTune/UE.Toolkit/releases) and download the latest version of `UE.Toolkit.Reloaded.7z` 2. Drag and drop the `7z` file into Reloaded to install. -### Installing the UE Toolkit Extension Mod -Install the extension mod for your game to be able to use all features (Object XML Editing). -- __Clair Obscur__ - https://github.com/RyoTune/E33.UEToolkit/releases -- __Persona 3 Reload__ - https://github.com/RyoTune/P3R.UEToolkit/releases - ## Editing Objects with XML In supported games, you're able to edit any object with just a simple text file (XML). No Unreal Engine, no file unpacking/repacking/cooking, and no hex 🤮 editing. Just _Notepad++_ and a dream ✨! ~~Or Notepad if you hate yourself...~~ diff --git a/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Base.cs b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Base.cs new file mode 100644 index 0000000..288e68e --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Base.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +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.Unreal.Common.DynamicMap; + +public interface IDynamicMapSizedType +{ + int DynSizeOf(); +} + +public interface IDynamicMapValueType : IDynamicMapSizedType; + +public class DynamicMapValueSystemType(Type type) : IDynamicMapValueType +{ + private Type Type => type; + + public int DynSizeOf() => Marshal.SizeOf(type); +} + +public class DynamicMapValueUnrealProperty(IFProperty type) : IDynamicMapValueType +{ + private IFProperty Type => type; + + public int DynSizeOf() => Type.ElementSize; +} + +public interface IDynamicMapKeyType : IDynamicMapSizedType +{ + bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key); + IDynamicMapKey FromPtr(nint ptr); +} + +public abstract class BaseDynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) : IDynamicMapKeyType +{ + protected IUnrealFactory Factory => factory; + protected IFMapProperty Property => property; + + public abstract bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key); + public abstract IDynamicMapKey FromPtr(nint ptr); + public abstract int DynSizeOf(); +} + +public interface IDynamicMapKey +{ + void Write(nint ptr); + uint GetTypeHash(); +} + +public abstract class BaseDynamicMapKey(TInner value) + : IDynamicMapKey, IMapHashable where TInner : IMapHashable +{ + protected TInner Value { get; set; } = value; + public uint GetTypeHash() => Value!.GetTypeHash(); + public abstract void Write(nint ptr); + public override string ToString() => Value.ToString(); + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Value.Equals(((BaseDynamicMapKey)obj).Value); + } +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Integer.cs b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Integer.cs new file mode 100644 index 0000000..4388493 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Integer.cs @@ -0,0 +1,130 @@ +using System.Diagnostics.CodeAnalysis; +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.Unreal.Common.DynamicMap; + +public struct HashableByte(byte value) : IMapHashable, IEquatable +{ + public byte Value = value; + public uint GetTypeHash() => Value; + public bool Equals(HashableByte other) => other.Value == Value; + public override string ToString() => Value.ToString(); +} + +public struct HashableShort(short value) : IMapHashable, IEquatable +{ + public short Value = value; + public uint GetTypeHash() => (uint)Value; + public bool Equals(HashableShort other) => other.Value == Value; + public override string ToString() => Value.ToString(); +} + +public struct HashableLong(long value) : IMapHashable, IEquatable +{ + public long Value = value; + public uint GetTypeHash() => (uint)(Value + (Value >> 32 * 23)); + public bool Equals(HashableLong other) => other.Value == Value; + public override string ToString() => Value.ToString(); +} + +public class Int8DynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) + : BaseDynamicMapKeyType(property, factory) +{ + public override int DynSizeOf() => (int)Factory.GetAlignment(Property.ValueProp); + + public override bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = null; + if (!byte.TryParse(text, out var value)) + { + return false; + } + key = new Int8DynamicMapKey(new(value)); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new Int8DynamicMapKey(new(*(byte*)ptr)); +} + +public class Int8DynamicMapKey(HashableByte value) + : BaseDynamicMapKey(value) +{ + public override unsafe void Write(nint ptr) => *(byte*)ptr = Value.Value; +} + +public class Int16DynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) + : BaseDynamicMapKeyType(property, factory) +{ + public override int DynSizeOf() => int.Max((short)Factory.GetAlignment(Property.ValueProp), 2); + + public override bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = null; + if (!short.TryParse(text, out var value)) + { + return false; + } + key = new Int16DynamicMapKey(new(value)); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new Int16DynamicMapKey(new(*(short*)ptr)); +} + +public class Int16DynamicMapKey(HashableShort value) + : BaseDynamicMapKey(value) +{ + public override unsafe void Write(nint ptr) => *(short*)ptr = Value.Value; +} + +public class IntDynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) + : BaseDynamicMapKeyType(property, factory) +{ + public override int DynSizeOf() => int.Max((int)Factory.GetAlignment(Property.ValueProp), 4); + + public override bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = null; + if (!int.TryParse(text, out var value)) + { + return false; + } + key = new IntDynamicMapKey(new(value)); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new IntDynamicMapKey(new(*(int*)ptr)); +} + +public class IntDynamicMapKey(HashableInt value) + : BaseDynamicMapKey(value) +{ + public override unsafe void Write(nint ptr) => *(int*)ptr = Value.Value; +} + +public class Int64DynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) + : BaseDynamicMapKeyType(property, factory) +{ + public override int DynSizeOf() => 8; + + public override bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = null; + if (!long.TryParse(text, out var value)) + { + return false; + } + key = new Int64DynamicMapKey(new(value)); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new Int64DynamicMapKey(new(*(long*)ptr)); +} + +public class Int64DynamicMapKey(HashableLong value) + : BaseDynamicMapKey(value) +{ + public override unsafe void Write(nint ptr) => *(long*)ptr = Value.Value; +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Name.cs b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Name.cs new file mode 100644 index 0000000..63894fd --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/DynamicMap/Name.cs @@ -0,0 +1,26 @@ +using System.Diagnostics.CodeAnalysis; +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.Unreal.Common.DynamicMap; + +public class NameDynamicMapKeyType(IFMapProperty property, IUnrealFactory factory) + : BaseDynamicMapKeyType(property, factory) +{ + public override bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = new NameDynamicMapKey(new(text)); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new NameDynamicMapKey(*(FName*)ptr); + + public override int DynSizeOf() => 8; +} + +public class NameDynamicMapKey(FName value) + : BaseDynamicMapKey(value) +{ + public override unsafe void Write(nint ptr) => *(FName*)ptr = Value; +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/Common/SoftObjectPath.cs b/UE.Toolkit.Core/Types/Unreal/Common/SoftObjectPath.cs new file mode 100644 index 0000000..6645567 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/Common/SoftObjectPath.cs @@ -0,0 +1,9 @@ +namespace UE.Toolkit.Core.Types.Unreal.Common; + +public interface ISoftObjectPath +{ + nint Ptr { get; } + + void SetAssetPath(string Value); + int GetSizeOf(); +} \ 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 16b490e..61192ee 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/BaseUnrealFactory.cs @@ -56,6 +56,20 @@ public T Cast(IPtr obj) } } public abstract nint SizeOf(); + // Type alignment when placed inline, such as the value type in a TMap + public nint GetAlignment(IFProperty prop) + { + return prop.ClassPrivate.Name switch + { + "Int8Property" or "BoolProperty" => 1, + "Int16Property" or "UInt16Property" => 2, + "IntProperty" or "Int32Property" or "UInt32Property" or "FloatProperty" or "NameProperty" => 4, + "Int64Property" or "UInt64Property" or "DoubleProperty" or "StrProperty" or "TextProperty" or "ObjectProperty" + or "SoftObjectProperty" or "SoftClassProperty" or "ArrayProperty" => 8, + "StructProperty" => CreateFStructProperty(prop.Ptr).Struct.MinAlignment, + _ => throw new NotSupportedException(prop.ClassPrivate.Name) + }; + } public abstract IFProperty CreateFProperty(nint ptr); public abstract IFBoolProperty CreateFBoolProperty(nint ptr); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs index c25930d..e9a5d67 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/IUnrealFactory.cs @@ -10,6 +10,8 @@ public interface IUnrealFactory { T Cast(IPtr obj); nint SizeOf(); + nint GetAlignment(IFProperty prop); + IUnrealMemoryInternal? Memory { get; set; } IFProperty CreateFProperty(nint ptr); diff --git a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUEnum.cs b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUEnum.cs index b5f5de8..53ec9ec 100644 --- a/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUEnum.cs +++ b/UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUEnum.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; namespace UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; @@ -7,4 +8,6 @@ public interface IUEnum : IUField string CppType { get; } TArray> Names { get; } + + bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? value); } \ 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 dd60c68..a1463ae 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 @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Common; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; @@ -339,6 +340,29 @@ public unsafe class UEnumUE4_27_2(nint ptr, IUnrealFactory factory) private readonly UEnum* _self = (UEnum*)ptr; public string CppType => _self->cpp_type.ToString(); public UE.Toolkit.Core.Types.Unreal.UE5_4_4.TArray> Names => _self->entries; + + public bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? value) + { + value = null; + if (ignoreCase) + { + name = name.ToLower(); + } + var NamesArray = new TArrayList>(&_self->entries, _factory.Memory); + foreach (var pDiscriminant in NamesArray) + { + var Discriminant = pDiscriminant.Value; + var CheckName = Discriminant->Key.ToString(); + if (ignoreCase) + CheckName = CheckName.ToLower(); + if (CheckName == name) + { + value = Discriminant->Value; + return true; + } + } + return false; + } } public unsafe class UScriptStructUE4_27_2(nint ptr, IUnrealFactory factory) 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 6cdde5f..c517688 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 @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Common; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; @@ -276,6 +277,29 @@ public unsafe class UEnum_UE5_4_4(nint ptr, IUnrealFactory factory) private readonly UEnum* _self = (UEnum*)ptr; public string CppType => _self->CppType.ToString(); public TArray> Names => _self->Names; + + public bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? value) + { + value = null; + if (ignoreCase) + { + name = name.ToLower(); + } + var NamesArray = new TArrayList>(&_self->Names, _factory.Memory); + foreach (var pDiscriminant in NamesArray) + { + var Discriminant = pDiscriminant.Value; + var CheckName = Discriminant->Key.ToString(); + if (ignoreCase) + CheckName = CheckName.ToLower(); + if (CheckName == name) + { + value = Discriminant->Value; + return true; + } + } + return false; + } } public unsafe class UScriptStruct_UE5_4_4(nint ptr, IUnrealFactory factory) 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 e61a6b2..266f10f 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 @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Types.Interfaces; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; @@ -162,6 +163,28 @@ public TArray> Names return CachedNames.Value; } } + + public bool TryParse(string name, bool ignoreCase, [NotNullWhen(true)] out long? value) + { + value = null; + if (ignoreCase) + { + name = name.ToLower(); + } + for (var i = 0; i < Names.ArrayNum; i++) + { + var Discriminant = &Names.AllocatorInstance[i]; + var CheckName = Discriminant->Key.ToString(); + if (ignoreCase) + CheckName = CheckName.ToLower(); + if (CheckName == name) + { + value = Discriminant->Value; + return true; + } + } + return false; + } ~UEnum_UE5_7_4() => Dispose(false); diff --git a/UE.Toolkit.Core/Types/Unreal/UE4_27_2/FSoftObjectPath.cs b/UE.Toolkit.Core/Types/Unreal/UE4_27_2/FSoftObjectPath.cs new file mode 100644 index 0000000..ffd9730 --- /dev/null +++ b/UE.Toolkit.Core/Types/Unreal/UE4_27_2/FSoftObjectPath.cs @@ -0,0 +1,26 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Unreal.Common; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Core.Types.Unreal.UE4_27_2; + +[StructLayout(LayoutKind.Sequential)] +public struct FSoftObjectPath +{ + /** Asset path, patch to a top level object in a package. This is /package/path.assetname */ + public FName AssetPathName; + + /** Optional FString for subobject within an asset. This is the sub path after the : */ + public FString SubPathString; +} + +public class SoftObjectPath(Ptr inner) : ISoftObjectPath +{ + private Ptr Inner { get; } = inner; + + public unsafe nint Ptr => (nint)Inner.Value; + + public unsafe void SetAssetPath(string Value) => Inner.Value->AssetPathName = new(Value); + + public unsafe int GetSizeOf() => sizeof(FSoftObjectPath); +} \ No newline at end of file diff --git a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FSoftObjectPath.cs b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FSoftObjectPath.cs index c2b273b..8745e39 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FSoftObjectPath.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FSoftObjectPath.cs @@ -1,4 +1,5 @@ using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Unreal.Common; namespace UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -10,4 +11,23 @@ public struct FSoftObjectPath /** Optional FString for subobject within an asset. This is the sub path after the : */ public FString SubPathString; +} + +public class SoftObjectPath(Ptr inner) : ISoftObjectPath +{ + private Ptr Inner { get; } = inner; + + public unsafe nint Ptr => (nint)Inner.Value; + + public void SetAssetPath(string Value) + { + var Parts = Value.Split("."); + unsafe + { + Inner.Value->AssetPath.PackageName = new(Parts[0]); + Inner.Value->AssetPath.AssetName = new(Parts[1]); + } + } + + public unsafe int GetSizeOf() => sizeof(FSoftObjectPath); } \ No newline at end of file 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 6285c15..5bed2e0 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/FString.cs @@ -3,10 +3,45 @@ namespace UE.Toolkit.Core.Types.Unreal.UE5_4_4; [StructLayout(LayoutKind.Sequential)] -public unsafe struct FString +public unsafe struct FString : IMapHashable { + + private 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, + 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, + 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, + 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, + 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, + 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, + 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, + 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, + 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + ]; + public TArray Data; public override string ToString() => Data.ArrayNum > 0 ? new(Data.AllocatorInstance, 0, Data.ArrayNum - 1) : string.Empty; + + public uint GetTypeHash() + { + var Chars = ToString().ToCharArray(); + uint Hash = 0; + foreach (var Char in Chars) + { + var C = (short)Char; + Hash = ((Hash >> 8) & 0xFFFFFF) ^ CRC_HASH[(Hash ^ C) & 0xFF]; + C >>= 8; + Hash = ((Hash >> 8) & 0xFFFFFF) ^ CRC_HASH[(Hash ^ C) & 0xFF]; + } + return Hash; + } } \ No newline at end of file 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 536d9c2..6ae1f71 100644 --- a/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs +++ b/UE.Toolkit.Core/Types/Unreal/UE5_4_4/TMap.cs @@ -4,6 +4,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common.DynamicMap; +using UE.Toolkit.Core.Types.Unreal.Factories; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; namespace UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -711,18 +714,22 @@ public void Dispose() { } /// INTERNAL USE /// Used in cases where the value type is not known at compile time, such as when processing Object XML. /// The only operations supported are ones required to work with Object XML. -public class TMapDynamicElementAccessor : IDisposable - where TElemKey : unmanaged, IEquatable, IMapHashable +// public class TMapDynamicElementAccessor : IDisposable +// where TElemKey : unmanaged, IEquatable, IMapHashable +public class TMapDynamicElementAccessor : IDisposable { protected unsafe TArray* Elements; - private Type ValueType; + internal IDynamicMapKeyType KeyType; + internal IDynamicMapValueType ValueType; protected IUnrealMemoryInternal Allocator; protected bool OwnsInstance = false; private bool Disposed = false; - public unsafe TMapDynamicElementAccessor(TArray* _Self, Type _ValueType, IUnrealMemoryInternal _Allocator, bool _OwnsInstance = false) + public unsafe TMapDynamicElementAccessor(TArray* _Self, IDynamicMapKeyType _KeyType, + IDynamicMapValueType _ValueType, IUnrealMemoryInternal _Allocator, bool _OwnsInstance = false) { Self = _Self; + KeyType = _KeyType; ValueType = _ValueType; Allocator = _Allocator; OwnsInstance = _OwnsInstance; @@ -752,12 +759,7 @@ internal unsafe int Capacity set => Elements->ArrayMax = value; } - internal int ValueSize => Marshal.SizeOf(ValueType); - - internal unsafe nint this[int Index] - { - get => (nint)Allocation + (Index * ValueSize); - } + internal unsafe nint this[int Index] => (nint)Allocation + Index * ValueType.DynSizeOf(); // TMapElement layout: // public KeyType Key; @@ -765,14 +767,15 @@ internal unsafe nint this[int Index] // public int HashNextId; // public int HashIndex; - internal unsafe int SizeOf() => sizeof(TElemKey) + ValueSize + 2 * sizeof(int); + internal int SizeOf() => (KeyType.DynSizeOf() + ValueType.DynSizeOf() + 3 & ~3) + 2 * sizeof(int); private int GetKeyOffset(int Index) => Index * SizeOf(); - private unsafe int GetValueOffset(int Index) => GetKeyOffset(Index) + sizeof(TElemKey); + private int GetValueOffset(int Index) => GetKeyOffset(Index) + KeyType.DynSizeOf(); private int GetNextHashIdOffset(int Index) => GetKeyOffset(Index + 1) - 2 * sizeof(int); private int GetHashIndexOffset(int Index) => GetKeyOffset(Index + 1) - sizeof(int); - internal unsafe TElemKey GetKey(int Index) => *(TElemKey*)(Allocation + GetKeyOffset(Index)); - internal unsafe TElemKey SetKey(int Index, TElemKey Key) => *(TElemKey*)(Allocation + GetKeyOffset(Index)) = Key; + internal unsafe IDynamicMapKey GetKey(int Index) => KeyType.FromPtr((nint)(Allocation + GetKeyOffset(Index))); + internal unsafe void SetKey(int Index, IDynamicMapKey Key) => Key.Write((nint)(Allocation + GetKeyOffset(Index))); + internal unsafe int GetNextHashId(int Index) => *(int*)(Allocation + GetNextHashIdOffset(Index)); internal unsafe int SetNextHashId(int Index, int Value) => *(int*)(Allocation + GetNextHashIdOffset(Index)) = Value; internal unsafe int GetHashIndex(int Index) => *(int*)(Allocation + GetHashIndexOffset(Index)); @@ -817,14 +820,14 @@ protected virtual void Dispose(bool disposing) /// INTERNAL USE /// Used in cases where the value type is not known at compile time, such as when processing Object XML. /// The only operations supported are ones required to work with Object XML. -public unsafe class TMapDynamicDictionary : IDisposable - where TElemKey : unmanaged, IEquatable, IMapHashable +// public unsafe class TMapDynamicDictionary : IDisposable +// where TElemKey : unmanaged, IEquatable, IMapHashable +public unsafe class TMapDynamicDictionary : IDisposable { public nint Self { get; private set; } protected IUnrealMemoryInternal Allocator; - protected TMapDynamicElementAccessor Elements; - private Type ValueType; + protected TMapDynamicElementAccessor Elements; protected TBitArrayList BitAllocator; protected bool OwnsInstance; protected bool Disposed = false; @@ -883,18 +886,18 @@ private int HashSize /// /// Pointer to an existing TMap /// The Unreal allocator, used for methods that modify the TMap - public TMapDynamicDictionary(TMap* _Self, Type _ValueType, IUnrealMemoryInternal _Allocator) + public TMapDynamicDictionary(nint _Self, IDynamicMapKeyType _KeyType, + IDynamicMapValueType _ValueType, IUnrealMemoryInternal _Allocator) { - Self = (nint)_Self; - ValueType = _ValueType; + Self = _Self; Allocator = _Allocator; - Elements = new(ElementsRaw, ValueType, Allocator); + Elements = new(ElementsRaw, _KeyType, _ValueType, Allocator); OwnsInstance = false; BitAllocator = new(BitAllocatorRaw, Allocator); } // ValueType* - private nint TryGetLinear(TElemKey key) + private nint TryGetLinear(IDynamicMapKey key) { if (Elements.Size == 0) return nint.Zero; for (var i = 0; i < Elements.Size; i++) @@ -903,7 +906,7 @@ private nint TryGetLinear(TElemKey key) return nint.Zero; } - private nint TryGetByHash(TElemKey key) + private nint TryGetByHash(IDynamicMapKey key) { var value = nint.Zero; // Hash alloc doesn't exist for single element maps, @@ -932,13 +935,13 @@ private int GetBucketListTail(int HashIndex) return currentIndex; } - public bool TryGetValue(TElemKey key, out nint pValue) + public bool TryGetValue(IDynamicMapKey key, out nint pValue) { pValue = new(TryGetByHash(key)); return pValue != nint.Zero; } - public void AddIndirect(TElemKey key, nint pNewItem) + public void AddIndirect(IDynamicMapKey key, nint pNewItem) { if (ContainsKey(key)) return; // Don't allow duplicate keys if (Hashes == null && Elements.Size + 1 >= MapConstants.MIN_SIZE_FOR_HASH_LIST) Rehash(MapConstants.HASH_INITIAL_SIZE); @@ -962,17 +965,17 @@ public void AddIndirect(TElemKey key, nint pNewItem) } // Add a new element to the array Elements.SetKey(Elements.Size, key); - NativeMemory.Copy((void*)pNewItem, (void*)Elements.GetAddress(Elements.Size), (nuint)Elements.ValueSize); + NativeMemory.Copy((void*)pNewItem, (void*)Elements.GetAddress(Elements.Size), (nuint)Elements.ValueType.DynSizeOf()); // Update the bit allocator BitAllocator.Add(true); Elements.Size++; } - public ICollection Keys + public ICollection Keys { get { - ICollection Keys = new List(); + var Keys = new List(); for (var i = 0; i < Elements.Size; i++) { Keys.Add(Elements.GetKey(i)); @@ -981,7 +984,7 @@ public ICollection Keys } } - public bool ContainsKey(TElemKey key) => Keys.Contains(key); + public bool ContainsKey(IDynamicMapKey key) => Keys.Contains(key); public int Count => Elements.Size; @@ -1016,7 +1019,7 @@ private void ResizeTo(int NewSize) Elements.Capacity = NewSize; } - int CalculateNewArraySize() => (Elements.Allocation != null) ? Elements.Capacity * 2 : MapConstants.DEFAULT_ARRAY_SIZE; + int CalculateNewArraySize() => Elements.Allocation != null ? Elements.Capacity * 2 : MapConstants.DEFAULT_ARRAY_SIZE; /// /// Relinquishes ownership of the TMap so that C#'s garbage collector doesn't delete this. /// diff --git a/UE.Toolkit.Core/UE.Toolkit.Core.csproj b/UE.Toolkit.Core/UE.Toolkit.Core.csproj index f3a7522..0d00bf1 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.8.0 + 1.9.0 diff --git a/UE.Toolkit.DumperMod/Dumper.cs b/UE.Toolkit.DumperMod/Dumper.cs index fc2451c..3d3039f 100644 --- a/UE.Toolkit.DumperMod/Dumper.cs +++ b/UE.Toolkit.DumperMod/Dumper.cs @@ -86,13 +86,15 @@ public string GetCsharpText() private readonly IUnrealObjects _uobjs; private readonly IUnrealStrings _strs; private readonly IUnrealFactory _factory; + private readonly IUnrealClasses _classes; - public Dumper(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, string dumpDir) + public Dumper(IUnrealFactory factory, IUnrealObjects uobjs, IUnrealStrings strs, IUnrealClasses classes, string dumpDir) { _uobjs = uobjs; _strs = strs; _dumpDir = dumpDir; _factory = factory; + _classes = classes; if (Directory.Exists(dumpDir)) Directory.Delete(dumpDir, true); Directory.CreateDirectory(dumpDir); @@ -251,7 +253,7 @@ private static string GetHeaderNameForObject(IUObject obj) private static void AddHeader(StringBuilder sb) { - sb.AppendLine("/* Generated with UE Toolkit: Dumper (1.8.0) */"); + 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 */"); @@ -510,12 +512,12 @@ private Func GetPropertyTypeNameLazy(IFProperty prop) case "EnumProperty": var enumProp = _factory.Cast(prop); var enumPropName = enumProp.Enum.NamePrivate.ToString(); - GenerateEnumDefinition(enumProp.Enum, GetPropertyTypeName(enumProp.UnderlyingProp)); + GenerateEnumDefinition(enumProp.Enum, _classes.GetPropertyTypeName(enumProp.UnderlyingProp)); return () => SanitizeName(enumPropName); case "MapProperty": var mapProp = _factory.Cast(prop); - var mapPropKeyType = GetPropertyTypeName(mapProp.KeyProp); - var mapPropValueType = GetPropertyTypeName(mapProp.ValueProp); + 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. @@ -536,7 +538,7 @@ private Func GetPropertyTypeNameLazy(IFProperty prop) 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 = GetPropertyTypeName(_factory.Cast(prop).Inner); + var arrayPropType = _classes.GetPropertyTypeName(_factory.Cast(prop).Inner); return () => { var isPtrType = arrayPropType.EndsWith('*') || arrayPropType.Contains('<'); // Use nint for pointers and generic types. @@ -547,7 +549,7 @@ private Func GetPropertyTypeNameLazy(IFProperty prop) $"TArray<{arrTypeSanitized}>"; }; case "SetProperty": - var setPropType = GetPropertyTypeName(_factory.Cast(prop).ElementProp); + var setPropType = _classes.GetPropertyTypeName(_factory.Cast(prop).ElementProp); return () => { var isPtrType = setPropType.EndsWith('*') || setPropType.Contains('<'); // Use nint for pointers and generic types.; @@ -557,7 +559,7 @@ private Func GetPropertyTypeNameLazy(IFProperty prop) : $"TSet<{SanitizeName(knownStruct?.DisplayName ?? setPropType)}>"; }; case "OptionalProperty": - var optionalType = GetPropertyTypeName(_factory.Cast(prop).ValueProperty); + var optionalType = _classes.GetPropertyTypeName(_factory.Cast(prop).ValueProperty); return () => { if (_UStructDefinitions.TryGetValue(optionalType, out var knownOptType)) @@ -586,87 +588,6 @@ private Func GetPropertyTypeNameLazy(IFProperty prop) } } - /// - /// Gets the property type name, such as byte for ByteProperty. - /// - /// - /// - private string GetPropertyTypeName(IFProperty prop) - { - var className = prop.ClassPrivate.Name; - switch (className) - { - case "BoolProperty": - return "bool"; - case "ByteProperty" or "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 "ObjectProperty": - return $"{_factory.Cast(prop).PropertyClass.NamePrivate}*"; - case "SoftObjectProperty": - return $"TSoftObjectPtr<{_factory.Cast(prop).PropertyClass.NamePrivate}>"; - case "SoftClassProperty": - return $"TSoftClassPtr<{_factory.Cast(prop).MetaClass.NamePrivate}>"; - case "StructProperty": - return _factory.Cast(prop).Struct.NamePrivate.ToString(); - case "ClassProperty": - case "ClassPtrProperty": - return _factory.Cast(prop).MetaClass!.NamePrivate.ToString(); - case "EnumProperty": - return _factory.Cast(prop).Enum.NamePrivate.ToString(); - case "MapProperty": - var mapProp = _factory.Cast(prop); - var mapPropKeyType = GetPropertyTypeName(mapProp.KeyProp); - var mapPropValueType = GetPropertyTypeName(mapProp.ValueProp); - return $"TMap<{mapPropKeyType}, {mapPropValueType}>"; - case "InterfaceProperty": - return $"TScriptInterface<{_factory.Cast(prop).NamePrivate}>"; - case "ArrayProperty": - return $"TArray<{GetPropertyTypeName(_factory.Cast(prop).Inner)}>"; - case "SetProperty": - return $"TSet<{GetPropertyTypeName(_factory.Cast(prop).ElementProp)}>"; - case "DelegateProperty": - return "FScriptDelegate"; - case "MulticastInlineDelegateProperty": - case "MulticastSparseDelegateProperty": - return "FMulticastScriptDelegate"; - case "WeakObjectProperty": - return "FWeakObjectPtr"; - case "FieldPathProperty": - return "FFieldPath"; - 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); diff --git a/UE.Toolkit.DumperMod/Mod.cs b/UE.Toolkit.DumperMod/Mod.cs index 033888f..926b6d9 100644 --- a/UE.Toolkit.DumperMod/Mod.cs +++ b/UE.Toolkit.DumperMod/Mod.cs @@ -37,10 +37,11 @@ public Mod(ModContext context) _modLoader.GetController().TryGetTarget(out var objs); _modLoader.GetController().TryGetTarget(out var strs); _modLoader.GetController().TryGetTarget(out var factory); + _modLoader.GetController().TryGetTarget(out var classes); var dumpDir = Path.Join(_modLoader.GetDirectoryForModId(_modConfig.ModId), "dump", _modLoader.GetAppConfig().AppId); - _dumper = new(factory!, objs!, strs!, dumpDir); + _dumper = new(factory!, objs!, strs!, classes!, dumpDir); } #region Standard Overrides diff --git a/UE.Toolkit.DumperMod/ModConfig.json b/UE.Toolkit.DumperMod/ModConfig.json index 89f46da..ab9294e 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.8.0", + "ModVersion": "1.9.0", "ModDescription": "Unreal Engine object dumper to C# types.", "ModDll": "UE.Toolkit.DumperMod.dll", "ModIcon": "Preview.png", diff --git a/UE.Toolkit.Interfaces/ITypeReflection.cs b/UE.Toolkit.Interfaces/ITypeReflection.cs new file mode 100644 index 0000000..6119f96 --- /dev/null +++ b/UE.Toolkit.Interfaces/ITypeReflection.cs @@ -0,0 +1,50 @@ +using UE.Toolkit.Core.Types.Unreal.Common; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Interfaces; + +/// +/// API for obtaining special type reflection info which cannot be obtained entirely through the runtime reflection system. +/// +public interface ITypeReflection +{ + + #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 + + /// + /// Converts an untyped pointer into a managed SoftObjectPath for the currently running version of the engine. + /// UE 5.1 and later use a FTopLevelAssetPath for the AssetPath instead of an FName + /// + /// Size of FText. + ISoftObjectPath IntoSoftObjectPath(nint ptr); + + #endregion + + #region Type Names + + /// + /// Gets the property type name, such as byte for ByteProperty. + /// + /// Property to get type name from. + /// C++/C# property type name. + string GetPropertyTypeName(IFProperty prop); + + #endregion +} \ No newline at end of file diff --git a/UE.Toolkit.Interfaces/IUnrealClasses.cs b/UE.Toolkit.Interfaces/IUnrealClasses.cs index 6cf5077..cfcfc49 100644 --- a/UE.Toolkit.Interfaces/IUnrealClasses.cs +++ b/UE.Toolkit.Interfaces/IUnrealClasses.cs @@ -6,7 +6,7 @@ namespace UE.Toolkit.Interfaces; /// /// API for functionality related to Unreal classes. /// -public interface IUnrealClasses : IUnrealClassesInternal +public interface IUnrealClasses : IUnrealClassesInternal, ITypeReflection { #region Class/Struct Getters @@ -47,6 +47,24 @@ public interface IUnrealClasses : IUnrealClassesInternal /// If the type information exists. public bool GetScriptStructInfoFromName(string Name, out IUScriptStruct? Value); + /// + /// Get the type information for a specified object. If this enum has no type info, this will return null. + /// This method works on types that are prefixed with E. + /// + /// Type information for the specified object type. + /// Object type. + /// If the type information exists. + public bool GetEnumInfoFromType(out IUEnum? Value) where TObject : unmanaged; + + /// + /// Get the type information for a specified object. If this enum has no type info, this will return null. + /// This method works on types that are prefixed with E. + /// + /// Name of the object type. + /// Type information for the object type with the given name. + /// If the type information exists. + public bool GetEnumInfoFromName(string Name, out IUEnum? Value); + #endregion #region Struct Field Extension Methods diff --git a/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj b/UE.Toolkit.Interfaces/UE.Toolkit.Interfaces.csproj index e1feca4..b1e6b90 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.8.0 + 1.9.0 diff --git a/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs b/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs new file mode 100644 index 0000000..48351db --- /dev/null +++ b/UE.Toolkit.Reloaded/Common/DynamicMap/String.cs @@ -0,0 +1,43 @@ +using System.Diagnostics.CodeAnalysis; +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common.DynamicMap; +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.Interfaces; + +namespace UE.Toolkit.Reloaded.Common.DynamicMap; + +public class StringDynamicMapKeyType(IFMapProperty property, IUnrealFactory factory, + IUnrealObjects objects, IUnrealMemoryInternal memory) + : BaseDynamicMapKeyType(property, factory) +{ + private IUnrealObjects Objects => objects; + private IUnrealMemoryInternal Memory => memory; + + public override unsafe bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = new StringDynamicMapKey(*Objects.CreateFString(text), Memory); + return true; + } + + public override unsafe IDynamicMapKey FromPtr(nint ptr) => new StringDynamicMapKey(*(FString*)ptr, Memory); + + public override int DynSizeOf() => 8; +} + +public class StringDynamicMapKey(FString value, IUnrealMemoryInternal memory) + : BaseDynamicMapKey(value) +{ + private IUnrealMemoryInternal Memory => memory; + + public override unsafe void Write(nint ptr) + { + var str = (FString*)ptr; + if (str->Data.AllocatorInstance != null) + { + Memory.Free((nint)str->Data.AllocatorInstance); + } + *(FString*)ptr = Value; + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/DynamicMap/Struct.cs b/UE.Toolkit.Reloaded/Common/DynamicMap/Struct.cs new file mode 100644 index 0000000..c15ab98 --- /dev/null +++ b/UE.Toolkit.Reloaded/Common/DynamicMap/Struct.cs @@ -0,0 +1,180 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common.DynamicMap; +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.Interfaces; + +namespace UE.Toolkit.Reloaded.Common.DynamicMap; + +public interface IStructMapKeyImpl +{ + bool FromString(string text, IStructMapKeyImpl impl, [NotNullWhen(true)] out IDynamicMapKey? key); + uint GetTypeHash(nint ptr); + string DisplayString(nint ptr); + bool PointerEqual(nint a, nint b); +} + +[StructLayout(LayoutKind.Explicit, Size = 0x18)] +struct FKey_Private +{ + [FieldOffset(0x0)] public FName KeyName; // Size: 0x8 +} + +public class FKeyMapKeyImpl : IStructMapKeyImpl +{ + public bool FromString(string text, IStructMapKeyImpl impl, [NotNullWhen(true)] out IDynamicMapKey? key) + { + var Bytes = new FKey_Private + { + KeyName = new(text) + }; + unsafe + { + var AsBytes = new byte[sizeof(FKey_Private)]; + Marshal.Copy((nint)(&Bytes), AsBytes, 0, sizeof(FKey_Private)); + key = new StructDynamicMapKey(AsBytes, impl); + return true; + } + } + + public unsafe uint GetTypeHash(nint ptr) => ((FKey_Private*)ptr)->KeyName.GetTypeHash(); + + public unsafe string DisplayString(nint ptr) => ((FKey_Private*)ptr)->KeyName.ToString(); + + public unsafe bool PointerEqual(nint a, nint b) => ((FKey_Private*)a)->KeyName.Equals(((FKey_Private*)b)->KeyName); +} + +[StructLayout(LayoutKind.Explicit, Size = 0x10)] +struct FGuid_Private +{ + [FieldOffset(0x0)] public int A; // Size: 0x4 + [FieldOffset(0x4)] public int B; // Size: 0x4 + [FieldOffset(0x8)] public int C; // Size: 0x4 + [FieldOffset(0xC)] public int D; // Size: 0x4 + + public override string ToString() => $"{A:X} {B:X} {C:X} {D:X}"; +} + +public class FGuidMapKeyImpl : IStructMapKeyImpl +{ + public bool FromString(string text, IStructMapKeyImpl impl, [NotNullWhen(true)] out IDynamicMapKey? key) + { + key = null; + if (!Guid.TryParse(text, out var guid)) + { + return false; + } + key = new StructDynamicMapKey(guid.ToByteArray(), impl); + return true; + } + + public unsafe uint GetTypeHash(nint ptr) + { + var AsBytes = new byte[sizeof(FGuid_Private)]; + Marshal.Copy(ptr, AsBytes, 0, AsBytes.Length); + return (uint)HashAlgorithms.CityHash.ComputeHash(AsBytes).AsInt32(); + } + + public unsafe string DisplayString(nint ptr) => ((FGuid_Private*)ptr)->ToString(); + + public unsafe bool PointerEqual(nint a, nint b) => ((FGuid_Private*)a)->Equals(*(FGuid_Private*)b); +} + +public static class StructMapKeyRegistry +{ + public static readonly Dictionary Implementations = new() + { + { "Key", new FKeyMapKeyImpl() }, + { "Guid", new FGuidMapKeyImpl() }, + }; +} + +public class StructDynamicMapKeyType : IDynamicMapKeyType +{ + private IFMapProperty Property; + private IStructMapKeyImpl Impl; + private IFStructProperty TypeProp; + private IUnrealFactory Factory; + private IUnrealObjects Objects; + private IUnrealMemoryInternal Memory; + + private StructDynamicMapKeyType(IFMapProperty property, IStructMapKeyImpl impl, + IFStructProperty typeProp, IUnrealFactory factory, IUnrealObjects objects, IUnrealMemoryInternal memory) + { + Property = property; + Impl = impl; + TypeProp = typeProp; + Factory = factory; + Objects = objects; + Memory = memory; + } + + public static StructDynamicMapKeyType? Create(IFMapProperty property, IFStructProperty typeProp, + IUnrealFactory factory, IUnrealObjects objects, IUnrealMemoryInternal memory) + { + if (!StructMapKeyRegistry.Implementations.TryGetValue( + typeProp.Struct.NamePrivate.ToString(), + out var impl)) + { + return null; + } + return new StructDynamicMapKeyType(property, impl, typeProp, factory, objects, memory); + } + + public int DynSizeOf() => TypeProp.ElementSize; + + public bool FromString(string text, [NotNullWhen(true)] out IDynamicMapKey? key) + => Impl.FromString(text, Impl, out key); + + public IDynamicMapKey FromPtr(nint ptr) + { + var Value = new byte[DynSizeOf()]; + Marshal.Copy(ptr, Value, 0, Value.Length); + return new StructDynamicMapKey(Value, Impl); + } +} + +public class StructDynamicMapKey(byte[] value, IStructMapKeyImpl impl) : IDynamicMapKey +{ + private byte[] Value => value; + private IStructMapKeyImpl Impl => impl; + + public void Write(nint ptr) => Marshal.Copy(Value, 0, ptr, Value.Length); + + public unsafe uint GetTypeHash() + { + fixed (byte* pValue = Value) + { + return Impl.GetTypeHash((nint)pValue); + } + } + + public override unsafe string ToString() + { + fixed (byte* pValue = Value) + { + return impl.DisplayString((nint)pValue); + } + } + + public override bool Equals(object? obj) + { + if (obj is null) return false; + if (ReferenceEquals(this, obj)) return true; + Log.Debug($"{this} == {obj}"); + if (obj.GetType() != GetType()) return false; + unsafe + { + fixed (byte* pThis = Value) + { + fixed (byte* pOther = ((StructDynamicMapKey)obj).Value) + { + return Impl.PointerEqual((nint)pThis, (nint)pOther); + } + } + } + } +} \ 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 53d92b4..64f7729 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfig.cs @@ -1,4 +1,5 @@ using UE.Toolkit.Reloaded.Common.GameConfigs.Games; +using UnrealEssentials.Interfaces; // ReSharper disable InconsistentNaming // ReSharper disable FieldCanBeMadeReadOnly.Global @@ -12,27 +13,33 @@ public static class GameConfig { [GameConfigVersion.UE5_4_4_ClairObscur] = () => new UE5_4_4_ClairObscur(), [GameConfigVersion.UE4_27_2_P3R] = () => new UE4_27_2_P3R(), + [GameConfigVersion.UE_5_0] = () => new UE5_0_3(), + [GameConfigVersion.UE_5_1] = () => new UE5_2_1(), + [GameConfigVersion.UE_5_2] = () => new UE5_2_1(), + [GameConfigVersion.UE_5_3] = () => new UE5_3_2(), + [GameConfigVersion.UE_5_5] = () => new UE5_4_4_ClairObscur(), + [GameConfigVersion.UE_5_6] = () => new UE5_6_1(), + [GameConfigVersion.UE_5_7] = () => new UE5_7_4(), }; public static IGameConfig Instance { get; private set; } = null!; - public static void SetGame(string appId) + public static void SetGame(string appId, IUnrealEssentials essentials) { if (Mod.Config.GameConfig != GameConfigVersion.Auto) { Instance = _gameConfigs[Mod.Config.GameConfig](); return; } - - Instance = appId switch + + Instance = essentials.GetEngineVersion() switch { - "p3r.exe" or "smt5v-win64-shipping.exe" => new UE4_27_2_P3R(), - "iostoretest_50-win64-shipping.exe" => new UE5_0_3(), - "iostoretest_51-win64-shipping.exe" or "iostoretest_52-win64-shipping.exe" - or "chronos-win64-shipping.exe" => new UE5_2_1(), - "iostoretest_53-win64-shipping.exe" => new UE5_3_2(), - "iostoretest_56-win64-shipping.exe" => new UE5_6_1(), - "iostoretest_57-win64-shipping.exe" => new UE5_7_4(), + "++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.3" => new UE5_3_2(), + "++UE5+Release-5.6" => new UE5_6_1(), + "++UE5+Release-5.7" => new UE5_7_4(), _ => new UE5_4_4_ClairObscur() }; diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfigVersion.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfigVersion.cs index 6f247e8..3e7ebfa 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfigVersion.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/GameConfigVersion.cs @@ -13,4 +13,25 @@ public enum GameConfigVersion [Display(Name = "UE 4.27.2 (Persona 3 Reload)")] UE4_27_2_P3R, + + [Display(Name = "UE 5.0")] + UE_5_0, + + [Display(Name = "UE 5.1")] + UE_5_1, + + [Display(Name = "UE 5.2")] + UE_5_2, + + [Display(Name = "UE 5.3")] + UE_5_3, + + [Display(Name = "UE 5.5")] + UE_5_5, + + [Display(Name = "UE 5.6")] + UE_5_6, + + [Display(Name = "UE 5.7")] + UE_5_7, } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE4_27_2_P3R.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE4_27_2_P3R.cs index 04ba7e1..a80b4c1 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE4_27_2_P3R.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE4_27_2_P3R.cs @@ -1,3 +1,4 @@ +using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.Factories.UE4_27_2; using UE.Toolkit.Interfaces; @@ -25,4 +26,8 @@ public override BaseTypeFactory TypeFactory(IUnrealClasses classes) 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); + + public override unsafe ISoftObjectPath IntoSoftObjectPath(nint ptr) + => new UE.Toolkit.Core.Types.Unreal.UE4_27_2.SoftObjectPath( + new((UE.Toolkit.Core.Types.Unreal.UE4_27_2.FSoftObjectPath*)ptr)); } \ No newline at end of file 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 1c672d8..58b7a22 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_0_3.cs @@ -1,4 +1,5 @@ -using UE.Toolkit.Core.Types.Unreal.Factories; +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.Interfaces; using UE.Toolkit.Reloaded.Reflection; @@ -9,7 +10,7 @@ namespace UE.Toolkit.Reloaded.Common.GameConfigs.Games; public class UE5_0_3 : UE5_4_4_ClairObscur { - public override string Id => "P3R"; + public override string Id => "UE5_0_3"; public override IUnrealFactory Factory { get; } = new UnrealFactory(); public override IUnrealMemory Memory { get; } = new UnrealMemory(); public override IPropertyFlagsBuilder FlagsBuilder { get; } = new PropertyFlagsBuilder(); @@ -23,4 +24,8 @@ public override BaseTypeFactory TypeFactory(IUnrealClasses classes) 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); + + public override unsafe ISoftObjectPath IntoSoftObjectPath(nint ptr) + => new UE.Toolkit.Core.Types.Unreal.UE4_27_2.SoftObjectPath( + new((UE.Toolkit.Core.Types.Unreal.UE4_27_2.FSoftObjectPath*)ptr)); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_4_4_ClairObscur.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_4_4_ClairObscur.cs index e4d62e4..862a3b0 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_4_4_ClairObscur.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_4_4_ClairObscur.cs @@ -1,3 +1,4 @@ +using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.Factories.UE5_4_4; using UE.Toolkit.Interfaces; @@ -24,4 +25,8 @@ public virtual BaseTypeFactory TypeFactory(IUnrealClasses classes) public virtual Type GetFText() => typeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); public virtual unsafe int GetFTextSize() => sizeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); + + public virtual unsafe ISoftObjectPath IntoSoftObjectPath(nint ptr) + => new UE.Toolkit.Core.Types.Unreal.UE5_4_4.SoftObjectPath( + new((UE.Toolkit.Core.Types.Unreal.UE5_4_4.FSoftObjectPath*)ptr)); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_6_1.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_6_1.cs index f8c5ecb..03dc8ca 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_6_1.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_6_1.cs @@ -19,8 +19,4 @@ public override BasePropertyFactory PropertyFactory(IUnrealClasses classes) public override BaseTypeFactory TypeFactory(IUnrealClasses classes) => new TypeFactory(Factory, Memory, classes, FlagsBuilder); - - public override Type GetFText() => typeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); - - public override unsafe int GetFTextSize() => sizeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_7_4.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_7_4.cs index 5c5fbfe..d981328 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_7_4.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/Games/UE5_7_4.cs @@ -19,8 +19,4 @@ public override BasePropertyFactory PropertyFactory(IUnrealClasses classes) public override BaseTypeFactory TypeFactory(IUnrealClasses classes) => new TypeFactory(Factory, Memory, classes, FlagsBuilder); - - public override Type GetFText() => typeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); - - public override unsafe int GetFTextSize() => sizeof(UE.Toolkit.Core.Types.Unreal.UE5_4_4.FText); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/GameConfigs/IGameConfig.cs b/UE.Toolkit.Reloaded/Common/GameConfigs/IGameConfig.cs index 1003112..69dc9c4 100644 --- a/UE.Toolkit.Reloaded/Common/GameConfigs/IGameConfig.cs +++ b/UE.Toolkit.Reloaded/Common/GameConfigs/IGameConfig.cs @@ -1,5 +1,6 @@ // ReSharper disable InconsistentNaming +using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Interfaces; using UE.Toolkit.Reloaded.Reflection; @@ -16,4 +17,5 @@ public interface IGameConfig BaseTypeFactory TypeFactory(IUnrealClasses classes); Type GetFText(); int GetFTextSize(); + ISoftObjectPath IntoSoftObjectPath(nint ptr); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Common/HashAlgorithms.cs b/UE.Toolkit.Reloaded/Common/HashAlgorithms.cs new file mode 100644 index 0000000..ffe9776 --- /dev/null +++ b/UE.Toolkit.Reloaded/Common/HashAlgorithms.cs @@ -0,0 +1,9 @@ +using HashifyNet; +using HashifyNet.Algorithms.CityHash; + +namespace UE.Toolkit.Reloaded.Common; + +public static class HashAlgorithms +{ + public static ICityHash CityHash = HashFactory.Create(); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Mod.cs b/UE.Toolkit.Reloaded/Mod.cs index 78ff78c..d57ecda 100644 --- a/UE.Toolkit.Reloaded/Mod.cs +++ b/UE.Toolkit.Reloaded/Mod.cs @@ -14,6 +14,8 @@ using UE.Toolkit.Reloaded.ObjectWriters; using UE.Toolkit.Reloaded.Toolkit; using UE.Toolkit.Reloaded.Unreal; +using UnrealEssentials.Interfaces; +using IUnrealMemory = UE.Toolkit.Interfaces.IUnrealMemory; namespace UE.Toolkit.Reloaded; @@ -30,6 +32,9 @@ public class Mod : ModBase, IExports private readonly IMod _owner; private readonly IModConfig _modConfig; + + // Unreal Essentials API + private readonly IUnrealEssentials _essentials; // Unreal Toolkit API private readonly IUnrealFactory _factory; @@ -46,6 +51,7 @@ public class Mod : ModBase, IExports private readonly UnrealMethods _methods; private readonly UnrealState _state; private readonly UnrealSpawning _spawning; + public Mod(ModContext context) { @@ -61,8 +67,14 @@ public Mod(ModContext context) Project.Initialize(_modConfig, _modLoader, _log, true); Log.LogLevel = Config.LogLevel; + if (!_modLoader.GetController().TryGetTarget(out _essentials)) + { + throw new Exception( + "Unreal Essentials is missing! Download the latest version from https://github.com/AnimatedSwine37/UnrealEssentials/releases"); + } + // Setup game patterns and Unreal factory. - GameConfig.SetGame(_modLoader.GetAppConfig().AppId); + GameConfig.SetGame(_modLoader.GetAppConfig().AppId, _essentials); _factory = GameConfig.Instance.Factory; _memory = GameConfig.Instance.Memory; @@ -77,8 +89,8 @@ public Mod(ModContext context) _methods = new(_factory, _memory, _classes, _objects, _hooks); _state = new(_factory, _classes); _spawning = new(_classes, _factory, _state); - _writer = new(_typeRegistry, _objects, _tables, _memory); - _toolkit = new(_writer); + _writer = new(_objects, _tables, _memory, _classes, _factory); + _toolkit = new(_writer, _essentials); _modLoader.AddOrReplaceController(_owner, _memory); _modLoader.AddOrReplaceController(_owner, _tables); diff --git a/UE.Toolkit.Reloaded/ModConfig.json b/UE.Toolkit.Reloaded/ModConfig.json index 2df488a..6c141eb 100644 --- a/UE.Toolkit.Reloaded/ModConfig.json +++ b/UE.Toolkit.Reloaded/ModConfig.json @@ -2,8 +2,8 @@ "ModId": "UE.Toolkit.Reloaded", "ModName": "Unreal Toolkit", "ModAuthor": "RyoTune, Rirurin", - "ModVersion": "1.8.0", - "ModDescription": "Modding toolkit for Unreal Engine games.\r\n\r\nSupported:\r\n- UE 5.4.4 (Clair Obscur)\r\n- UE 4.27.2 (P3R/SMTVV)", + "ModVersion": "1.9.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", "ModR2RManagedDll32": "x86/UE.Toolkit.Reloaded.dll", @@ -35,6 +35,15 @@ "AssetFileName": "Mod.zip" }, "ReleaseMetadataName": "Reloaded.Memory.SigScan.ReloadedII.ReleaseMetadata.json" + }, + "UnrealEssentials": { + "Config": { + "UserName": "AnimatedSwine37", + "RepositoryName": "UnrealEssentials", + "UseReleaseTag": false, + "AssetFileName": "Mod.zip" + }, + "ReleaseMetadataName": "UnrealEssentials.ReleaseMetadata.json" } } }, @@ -48,7 +57,8 @@ "IsUniversalMod": true, "ModDependencies": [ "reloaded.sharedlib.hooks", - "Reloaded.Memory.SigScan.ReloadedII" + "Reloaded.Memory.SigScan.ReloadedII", + "UnrealEssentials" ], "OptionalDependencies": [], "SupportedAppId": [ diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Documents/DataTableDocument.cs b/UE.Toolkit.Reloaded/ObjectWriters/Documents/DataTableDocument.cs new file mode 100644 index 0000000..8a7959c --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Documents/DataTableDocument.cs @@ -0,0 +1,73 @@ +using System.Diagnostics.CodeAnalysis; +using System.Xml; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class DataTableDocument(string tableName, nint baseAddress, NodeFactory factory) : IFieldNode +{ + + private string TableName => tableName; + private nint BaseAddress => baseAddress; + private NodeFactory Factory => factory; + + public unsafe void ConsumeNode(XmlReader reader) + { + // DataTable item type. + if (!TryGetRowType(reader, out var rowStruct)) return; + + // DataTable map type is always FName + Pointer, can just use UObject. + // var tempTable = new ToolkitDataTable((UDataTable*)fieldPtr); + var tempTable = new UDataTableManaged>((UDataTable>*)BaseAddress, Factory.Memory); + + // Get any item nodes. + using var subReader = reader.ReadSubtree(); + subReader.MoveToContent(); + + while (subReader.Read()) + { + if (subReader.NodeType != XmlNodeType.Element) continue; + if (subReader.Name != WriterConstants.ItemTag) throw new($"Only '{WriterConstants.ItemTag}' elements can be directly inside a UDataTable. Found: {subReader.Name}"); + + var id = subReader.GetAttribute(WriterConstants.ItemIdAttr); + if (id == null) + { + Log.Error($"{nameof(DataTableDocument)} || '{WriterConstants.ItemTag}' in field '{TableName}' is missing an ID."); + break; + } + + var Key = new FName(id); + if (!tempTable.ContainsKey(Key)) + { + tempTable.AddRow(Key, new Ptr((byte*)Factory.Memory.MallocZeroed(rowStruct.PropertiesSize))); + Log.Debug($"{nameof(DataTableDocument)} || Added row with ID '{id}' into '{TableName}'."); + } + var itemTree = subReader.ReadSubtree(); + itemTree.MoveToContent(); + new StructDocument($"{TableName} (ID: {id})", rowStruct, (nint)tempTable[Key].Value->Value, Factory) + .ConsumeNode(itemTree); + } + + Log.Verbose($"{nameof(DataTableDocument)} || Field '{TableName}' node consumed."); + } + + private bool TryGetRowType(XmlReader reader, [NotNullWhen(true)] out IUStruct? rowStruct) + { + // DataTable root object, type needs to be specified as attribute. + var rowStructName = reader.GetAttribute(WriterConstants.RowStructAttr); + if (rowStructName == null) + { + rowStruct = null; + Log.Error($"{nameof(DataTableDocument)} || Data Table declaration is missing '{WriterConstants.RowStructAttr}' attribute."); + return false; + } + rowStruct = Factory.Classes.GetScriptStructInfoFromName($"F{rowStructName}", out var rowStructNew) ? rowStructNew : null; + if (rowStruct == null) + { + Log.Error($"{nameof(DataTableDocument)} || Data Table row type '{rowStructName}' does not exist."); + } + return rowStruct != null; + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Documents/StructDocument.cs b/UE.Toolkit.Reloaded/ObjectWriters/Documents/StructDocument.cs new file mode 100644 index 0000000..7d90334 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Documents/StructDocument.cs @@ -0,0 +1,69 @@ +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class StructDocument : IFieldNode +{ + private static readonly Dictionary> CachedStructsFields = []; + + private string StructName { get; } + private IUStruct Property { get; } + // private string StructName => Property.NamePrivate.ToString(); + private nint BaseAddress { get; } + private NodeFactory Factory { get; } + private readonly Dictionary _fields; + + public StructDocument(string structName, IUStruct property, nint baseAddress, NodeFactory factory) + { + StructName = structName; + Property = property; + BaseAddress = baseAddress; + Factory = factory; + + // Cache struct fields data cause reflection slow... + if (CachedStructsFields.TryGetValue(property.NamePrivate.ToString(), out var cachedFields)) + { + _fields = cachedFields; + } + else + { + _fields = CachedStructsFields[property.NamePrivate.ToString()] = + property.PropertyLink.Select(x => (x.NamePrivate, x)).ToDictionary(); + CachedStructsFields[property.NamePrivate.ToString()] = _fields; + } + } + + public unsafe void ConsumeNode(XmlReader reader) + { + var anyElementFound = false; + while (reader.Read()) + { + if (reader.Name == StructName || reader.NodeType == XmlNodeType.EndElement) break; + if (reader.NodeType != XmlNodeType.Element) continue; + if (AtItemElement(reader)) Log.Warning($"{nameof(StructDocument)} || Unexpected '{WriterConstants.ItemTag}' element found. Error?"); + + anyElementFound = true; + + var fieldName = reader.Name; + if (_fields.TryGetValue(fieldName, out var fieldData)) + { + var fieldPtr = BaseAddress + fieldData.Offset_Internal; + Log.Debug($"StructDocument->{fieldData.NamePrivate} @ 0x{fieldPtr:x}"); + Factory.Create(fieldData, fieldPtr).ConsumeNode(reader); + } + else + { + Log.Warning($"{nameof(StructDocument)} || Field '{fieldName}' not found in struct '{StructName}'."); + break; + } + } + + if (!anyElementFound) Log.Warning($"{nameof(StructDocument)} || No elements found in '{StructName}'. Error?"); + Log.Verbose($"{nameof(StructDocument)} || Field '{StructName}' node consumed."); + } + + private static bool AtItemElement(XmlReader reader) + => reader.Name == WriterConstants.ItemTag + && reader.GetAttribute(WriterConstants.ItemIdAttr) != null; +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TArrayFieldNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ArrayNode.cs similarity index 54% rename from UE.Toolkit.Reloaded/ObjectWriters/Nodes/TArrayFieldNode.cs rename to UE.Toolkit.Reloaded/ObjectWriters/Nodes/ArrayNode.cs index 1ec2a74..7dc9592 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TArrayFieldNode.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ArrayNode.cs @@ -1,19 +1,20 @@ -using System.Runtime.InteropServices; -using System.Xml; +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; -// ReSharper disable InconsistentNaming - namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; -public class TArrayFieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) : IFieldNode +public class ArrayNode(IFArrayProperty property, nint value, NodeFactory factory) : IFieldNode { + private IFArrayProperty Property => property; + private nint Value => value; + private NodeFactory Factory => factory; + public unsafe void ConsumeNode(XmlReader reader) { - // TArray item type. - var itemType = fieldType.GetGenericArguments().First(); - var itemSize = Marshal.SizeOf(itemType); - var tempArray = (TArray*)fieldPtr; + var itemType = Property.Inner; + var itemSize = itemType.ElementSize; + var tempArray = (TArray*)Value; // Get any item nodes. using var subReader = reader.ReadSubtree(); @@ -27,40 +28,43 @@ public unsafe void ConsumeNode(XmlReader reader) var id = subReader.GetAttribute(WriterConstants.ItemIdAttr); if (id == null) { - Log.Error($"{nameof(TArrayFieldNode)} || '{WriterConstants.ItemTag}' is missing an ID."); + Log.Error($"{nameof(ArrayNode)} || '{WriterConstants.ItemTag}' is missing an ID."); break; } - - if (!int.TryParse(id, out var itemIdx)) + + int itemIdx; + if (id == "+") { - Log.Warning($"{nameof(TArrayFieldNode)} || Invalid ID: {id}"); + itemIdx = -1; + } + else if (!int.TryParse(id, out itemIdx)) + { + Log.Warning($"{nameof(ArrayNode)} || Invalid ID: {id}"); break; } if (itemIdx != -1) { itemIdx -= 1; // We're doing 1 indexing because normal people can't handle 0... + // (What did Ryo mean by this...) if (itemIdx < 0 || itemIdx > tempArray->ArrayNum) { - Log.Warning($"{nameof(TArrayFieldNode)} || ID is either less than 1 or more than item count: {id} || Total: {tempArray->ArrayNum}"); + Log.Warning($"{nameof(ArrayNode)} || ID is either less than 1 or more than item count: {id} || Total: {tempArray->ArrayNum}"); break; } } else { - Log.Verbose($"{nameof(TArrayFieldNode)} @ 0x{(nint)tempArray:x} || Old Size : {tempArray->ArrayNum} || Old Capacity: {tempArray->ArrayMax}"); + Log.Verbose($"{nameof(ArrayNode)} @ 0x{(nint)tempArray:x} || Old Size : {tempArray->ArrayNum} || Old Capacity: {tempArray->ArrayMax}"); if (tempArray->ArrayNum == tempArray->ArrayMax) - TArrayListStatic.ResizeToStatic(tempArray, TArrayListStatic.CalculateNewArraySizeStatic(tempArray), itemSize, nodeFactory.Memory); + TArrayListStatic.ResizeToStatic(tempArray, TArrayListStatic.CalculateNewArraySizeStatic(tempArray), itemSize, Factory.Memory); itemIdx = tempArray->ArrayNum; tempArray->ArrayNum++; } var itemPtr = itemIdx * itemSize + (nint)tempArray->AllocatorInstance; - if (nodeFactory.TryCreate($"{fieldName} (ID: {id})", itemPtr, 0, itemType, out var itemNode)) - { - itemNode.ConsumeNode(subReader); - } + Factory.Create(itemType, itemPtr).ConsumeNode(subReader); } - Log.Verbose($"{nameof(TArrayFieldNode)} || Field '{fieldName}' node consumed."); + Log.Verbose($"{nameof(ArrayNode)} || Field '{property.NamePrivate}' node consumed."); } } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/BoolNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/BoolNode.cs new file mode 100644 index 0000000..d6edcaf --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/BoolNode.cs @@ -0,0 +1,13 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class BoolNode(IFBoolProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) + { + var Bool = Convert.ToBoolean(text); + *Value.Value |= (byte)(property.FieldMask * *(byte*)&Bool); + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ByteNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ByteNode.cs new file mode 100644 index 0000000..6d537df --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ByteNode.cs @@ -0,0 +1,19 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class ByteNode(IFByteProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) + { + if (Property.Enum != null) + { + EnumWriter.WriteEnum(property.Enum, new((nint*)Value.Value), text); + } + else + { + *Value.Value = Convert.ToByte(text); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DataTableFieldNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DataTableFieldNode.cs deleted file mode 100644 index ec6d3dc..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DataTableFieldNode.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Xml; -using UE.Toolkit.Core.Types; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; - -public class DataTableFieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) : IFieldNode -{ - public unsafe void ConsumeNode(XmlReader reader) - { - // DataTable item type. - if (!TryGetRowType(reader, out var itemType)) return; - var itemSize = Marshal.SizeOf(itemType); - - // DataTable map type is always FName + Pointer, can just use UObject. - // var tempTable = new ToolkitDataTable((UDataTable*)fieldPtr); - var tempTable = new UDataTableManaged>((UDataTable>*)fieldPtr, nodeFactory.Memory); - - // Get any item nodes. - using var subReader = reader.ReadSubtree(); - subReader.MoveToContent(); - - while (subReader.Read()) - { - if (subReader.NodeType != XmlNodeType.Element) continue; - if (subReader.Name != WriterConstants.ItemTag) throw new($"Only '{WriterConstants.ItemTag}' elements can be directly inside a UDataTable. Found: {subReader.Name}"); - - var id = subReader.GetAttribute(WriterConstants.ItemIdAttr); - if (id == null) - { - Log.Error($"{nameof(DataTableFieldNode)} || '{WriterConstants.ItemTag}' in field '{fieldName}' is missing an ID."); - break; - } - - var Key = new FName(id); - if (!tempTable.ContainsKey(Key)) - { - tempTable.AddRow(Key, new Ptr((byte*)nodeFactory.Memory.MallocZeroed(itemSize))); - Log.Debug($"{nameof(DataTableFieldNode)} || Added row with ID '{id}' into '{fieldName}'."); - } - - if (tempTable.TryGetValue(Key, out var row) - && nodeFactory.TryCreate($"{fieldName} (ID: {id})", (nint)row.Value->Value, 0, itemType, out var itemNode)) - { - var itemTree = subReader.ReadSubtree(); - itemTree.MoveToContent(); - itemNode.ConsumeNode(itemTree); - } - } - - Log.Verbose($"{nameof(DataTableFieldNode)} || Field '{fieldName}' node consumed."); - } - - private bool TryGetRowType(XmlReader reader, [NotNullWhen(true)] out Type? rowType) - { - // DataTable is the property of an object with generic type. - if (fieldType.IsGenericType) - { - rowType = fieldType.GetGenericArguments().FirstOrDefault(); - } - - // DataTable root object, type needs to be specified as attribute. - else - { - var rowStruct = reader.GetAttribute(WriterConstants.RowStructAttr); - if (rowStruct == null) - { - rowType = null; - Log.Error($"{nameof(DataTableFieldNode)} || Root '{nameof(UDataTable)}' missing '{WriterConstants.RowStructAttr}' attribute."); - return false; - } - - var rowStructHint = reader.GetAttribute(WriterConstants.RowStructHintAttr); - var rowStructProvider = reader.GetAttribute(WriterConstants.RowStructProviderAttr); - nodeFactory.TypeRegistry.TryGetType(rowStruct, rowStructHint, rowStructProvider, out rowType); - } - - return rowType != null; - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DoubleNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DoubleNode.cs new file mode 100644 index 0000000..b8f0f38 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DoubleNode.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class DoubleNode(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToDouble(text, ObjectXMLFormatProvider.FloatProvider); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DummyNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DummyNode.cs new file mode 100644 index 0000000..aaff8b9 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/DummyNode.cs @@ -0,0 +1,17 @@ +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class DummyNode(IFProperty property) : IFieldNode +{ + private IFProperty Property => property; + + public void ConsumeNode(XmlReader reader) + { + Log.Warning($"Unimplemented XML Node for property type {Property.ClassPrivate.Name}!"); + using var subReader = reader.ReadSubtree(); + subReader.MoveToContent(); + while (subReader.Read()) {} + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/EnumNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/EnumNode.cs new file mode 100644 index 0000000..00bd226 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/EnumNode.cs @@ -0,0 +1,58 @@ +using System.Xml; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public static class EnumWriter +{ + public static void WriteEnum(IUEnum Enum, Ptr Value, string text) + { + // Value is enum member name. + long enumValue; + if (Enum.TryParse(text, true, out var value)) + { + enumValue = value.Value; + } else if (double.TryParse(text, ObjectXMLFormatProvider.FloatProvider, out var intValue)) + { + enumValue = (long)intValue; + } + else + { + Log.Error($"{nameof(EnumNode)} || Enum {Enum.NamePrivate} does not contain the key '{text}'"); + return; + } + // Value is enum integer value. + long maxValue = 0; + unsafe + { + for (var i = 0; i < Enum.Names.ArrayNum; i++) + { + var currentValue = Enum.Names.AllocatorInstance[i].Value; + if (currentValue > maxValue) + maxValue = currentValue; + } + if (maxValue <= byte.MaxValue) + { + *(byte*)Value.Value = (byte)enumValue; + } + else if (maxValue <= ushort.MaxValue) + { + *(ushort*)Value.Value = (ushort)enumValue; + } + else if (maxValue <= uint.MaxValue) + { + *(uint*)Value.Value = (uint)enumValue; + } + else + { + *(ulong*)Value.Value = (ulong)enumValue; + } + } + } +} + +public class EnumNode(IFEnumProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override void SetField(string text) => EnumWriter.WriteEnum(property.Enum, Value, text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FieldNodeFactory.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FieldNodeFactory.cs deleted file mode 100644 index 486c9d6..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FieldNodeFactory.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; -using UE.Toolkit.Interfaces.ObjectWriters; -using UE.Toolkit.Reloaded.Common.GameConfigs; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; - -public class FieldNodeFactory(ITypeRegistry typeReg, IObjectCreator objCreator, IUnrealMemoryInternal memory) -{ - public ITypeRegistry TypeRegistry { get; } = typeReg; - public IUnrealMemoryInternal Memory { get; } = memory; - - public bool TryCreate(string fieldName, nint fieldPtr, int fieldBit, Type fieldType, [NotNullWhen(true)]out IFieldNode? node) - { - node = Create(fieldName, fieldPtr, fieldBit, fieldType); - return node != null; - } - - private IFieldNode? Create(string fieldName, nint fieldPtr, int fieldBit, Type fieldType) - { - - Log.Debug($"{nameof(FieldNodeFactory)} || Create Node '{fieldName}' with type '{fieldType.Name}'."); - - if (fieldType.IsPrimitive - || fieldType.IsEnum - || fieldType == typeof(string) - || fieldType == GameConfig.Instance.GetFText() - || fieldType == typeof(FString) - || fieldType == typeof(FName) - || fieldType.Name.StartsWith("TSoftObjectPtr") - || fieldType.Name.StartsWith("TSoftClassPtr") - ) - { - return new PrimitiveFieldNode(fieldName, fieldPtr, fieldBit, fieldType, objCreator); - } - - if (fieldType.Name.StartsWith("TArray")) - { - return new TArrayFieldNode(fieldName, fieldPtr, fieldType, this); - } - - if (fieldType.Name.StartsWith("UDataTable")) - { - return new DataTableFieldNode(fieldName, fieldPtr, fieldType, this); - } - - if (fieldType.Name.StartsWith("TMap")) - { - var keyType = fieldType.GetGenericArguments()[0]; - if (keyType == typeof(int)) - { - var valueType = fieldType.GetGenericArguments()[1]; - // Check if it has a StructLayout with an explicit alignment value, this lets us avoid iterating - // through each field to determine alignment - // Every type defined in the extension mod has an alignment value in it's StructLayout - if (valueType.StructLayoutAttribute != null) - { - return valueType.StructLayoutAttribute!.Pack switch - { - <= 4 => new TMapIntFieldNode(fieldName, fieldPtr, fieldType, this), - _ => new TMapInt8FieldNode(fieldName, fieldPtr, fieldType, this) - }; - } - // Fallback: Iterate through fields (this should not be necessary) - var LastOffset = 0; - var bUseInt8 = false; - foreach (var Field in valueType.GetFields()) - { - // Check that the size for each field doesn't exceed 4 bytes - if (Marshal.SizeOf(Field.GetType()) > 4) - { - bUseInt8 = true; - break; - } - } - return bUseInt8 switch - { - true => new TMapInt8FieldNode(fieldName, fieldPtr, fieldType, this), - false => new TMapIntFieldNode(fieldName, fieldPtr, fieldType, this), - }; - } - - if (keyType == typeof(FName)) - { - return new TMapNameFieldNode(fieldName, fieldPtr, fieldType, this); - } - - /* - if (keyType.Name.StartsWith("Ptr")) // TODO - { - - } - */ - Log.Warning($"{nameof(FieldNodeFactory)} || Field '{fieldName}' with type '{fieldType.Name}' is not currently supported for map editing operations."); - return null; - } - - if (fieldType.IsValueType) - { - return new StructFieldNode(fieldName, fieldPtr, fieldType, this); - } - - Log.Error($"{nameof(FieldNodeFactory)} || Unsupported field '{fieldName}' with type '{fieldType.Name}'."); - return null; - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FloatNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FloatNode.cs new file mode 100644 index 0000000..6a92777 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/FloatNode.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class FloatNode(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToSingle(text, ObjectXMLFormatProvider.FloatProvider); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/GuidNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/GuidNode.cs new file mode 100644 index 0000000..16372b5 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/GuidNode.cs @@ -0,0 +1,42 @@ +using System.Runtime.InteropServices; +using System.Xml; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Reloaded.Common.DynamicMap; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class GuidNode(IFProperty property, Ptr value) : IFieldNode +{ + private Ptr Value => value; + + private byte[]? InitialValue { get; set; } + + private string GetFieldValue(XmlReader reader) + => reader.GetAttribute("value") ?? reader.ReadElementContentAsString(); + + public void ConsumeNode(XmlReader reader) + { + var fieldValue = GetFieldValue(reader); + if (InitialValue != null) + { + unsafe + { + InitialValue = new byte[sizeof(Guid)]; + Marshal.Copy((nint)Value.Value, InitialValue, 0, sizeof(Guid)); + } + } + + if (!Guid.TryParse(fieldValue, out var guid)) + { + Log.Warning($"{nameof(GuidNode)} || Field '{property.NamePrivate}' has an incorrectly formatted GUID! '{fieldValue}'"); + return; + } + + unsafe + { + *Value.Value = guid; + Log.Verbose($"{nameof(GuidNode)} || Field '{property.NamePrivate}' at 0x{(nint)Value.Value:X} set to '{fieldValue}'"); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int16Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int16Node.cs new file mode 100644 index 0000000..9acf874 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int16Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class Int16Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToInt16(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int32Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int32Node.cs new file mode 100644 index 0000000..31f7b0d --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int32Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class Int32Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToInt32(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int64Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int64Node.cs new file mode 100644 index 0000000..ccbabdb --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int64Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class Int64Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToInt64(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int8Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int8Node.cs new file mode 100644 index 0000000..427d7fa --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/Int8Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class Int8Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToByte(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/MapNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/MapNode.cs new file mode 100644 index 0000000..0b98464 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/MapNode.cs @@ -0,0 +1,109 @@ +using System.Diagnostics.CodeAnalysis; +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Common.DynamicMap; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UE.Toolkit.Reloaded.Common.DynamicMap; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public static class MapNodeFactory +{ + private static bool CreateMapKey(IFMapProperty property, NodeFactory factory, [NotNullWhen(true)] out IDynamicMapKeyType? MapKey) + { + var Key = property.KeyProp; + MapKey = Key.ClassPrivate.Name switch + { + "Int8Property" => new Int8DynamicMapKeyType(property, factory.Factory), + "Int16Property" or "UInt16Property" => new Int16DynamicMapKeyType(property, factory.Factory), + "IntProperty" or "UInt32Property" => new IntDynamicMapKeyType(property, factory.Factory), + "Int64Property" or "UInt64Property" => new Int64DynamicMapKeyType(property, factory.Factory), + "NameProperty" => new NameDynamicMapKeyType(property, factory.Factory), + "StrProperty" => new StringDynamicMapKeyType(property, factory.Factory, factory.Objects, factory.Memory), + "StructProperty" => StructDynamicMapKeyType.Create( + property, factory.Factory.CreateFStructProperty(Key.Ptr), + factory.Factory, factory.Objects, factory.Memory), + _ => null + }; + return MapKey != null; + } + + public static IFieldNode CreateMapNode(IFMapProperty property, nint value, NodeFactory factory) + { + var Key = property.KeyProp; + if (!CreateMapKey(property, factory, out var mapKey)) + { + Log.Warning($"{nameof(MapNodeFactory)} || Field '{property.NamePrivate}' with type '{factory.Classes.GetPropertyTypeName(property.KeyProp)}' is not currently supported for map editing operations."); + return new DummyNode(property); + } + return new MapNode(property, mapKey, value, factory); + } +} + +public class MapNode(IFMapProperty property, IDynamicMapKeyType mapKey, nint value, NodeFactory factory) + : IFieldNode +{ + protected IFMapProperty Property => property; + protected IDynamicMapKeyType MapKey => mapKey; + protected nint Value => value; + protected NodeFactory Factory => factory; + + public void ConsumeNode(XmlReader reader) + { + var tempMap = CreateTempMap(Value, Property.ValueProp); + + // Get any item nodes. + using var subReader = reader.ReadSubtree(); + subReader.MoveToContent(); + + while (subReader.Read()) + { + if (subReader.NodeType != XmlNodeType.Element) continue; + if (subReader.Name != WriterConstants.ItemTag) + throw new( + $"Only '{WriterConstants.ItemTag}' elements can be directly inside a map. Found: {subReader.Name}"); + + var id = subReader.GetAttribute(WriterConstants.ItemIdAttr); + if (id == null) + { + Log.Error($"{nameof(MapNode)} || '{WriterConstants.ItemTag}' is missing an ID."); + break; + } + + if (!MapKey.FromString(id, out var Key)) + { + Log.Error($"{nameof(MapNode)} || Could not process map key {id}"); + break; + } + unsafe + { + var tempMapBitAlloc = (TArray*)(tempMap.Self + 0x20); // MapConstants.SIZE_OF_ARRAY + 0x10 + // Ensure that TMap is properly initialized for newly allocated items - BitAllocator's capacity should + // be 0x80 (128) to account for inline bits (equivalent to Reset()). + if (tempMapBitAlloc->ArrayMax < 128) + tempMapBitAlloc->ArrayMax = 128; + } + + // Get existing value + if (!ContainsKey(tempMap, Key)) + { + var newEntry = Factory.Memory.MallocZeroed(Property.ValueProp.ElementSize); + tempMap.AddIndirect(Key, newEntry); + Factory.Memory.Free(newEntry); + Log.Debug($"{nameof(MapNode)} || Added entry at 0x{newEntry:x} with key '{Key}' into '{Property.NamePrivate}'"); + } + + if (tempMap.TryGetValue(Key, out var valuePtr)) + { + var itemTree = subReader.ReadSubtree(); + itemTree.MoveToContent(); + Factory.Create(Property.ValueProp, valuePtr).ConsumeNode(itemTree); + } + } + } + + public TMapDynamicDictionary CreateTempMap(nint fieldPtr, IFProperty valueType) + => new(fieldPtr, MapKey, new DynamicMapValueUnrealProperty(valueType), Factory.Memory); + + public virtual bool ContainsKey(TMapDynamicDictionary dict, IDynamicMapKey key) => dict.ContainsKey(key); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NameNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NameNode.cs new file mode 100644 index 0000000..7409381 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NameNode.cs @@ -0,0 +1,10 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class NameNode(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = new(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs new file mode 100644 index 0000000..3b3ff5b --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/NodeFactory.cs @@ -0,0 +1,59 @@ +using UE.Toolkit.Core.Types.Interfaces; +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.Interfaces; +using UE.Toolkit.Reloaded.Common.GameConfigs; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class NodeFactory(IUnrealObjects objects, IUnrealMemoryInternal memory, + IUnrealClasses classes, IUnrealFactory factory) +{ + public IUnrealMemoryInternal Memory => memory; + public IUnrealObjects Objects => objects; + public IUnrealClasses Classes => classes; + public IUnrealFactory Factory => factory; + + private unsafe IFieldNode StructNodeHandleSpecial(IFStructProperty property, nint fieldPtr) + { + var structName = property.Struct.NamePrivate.ToString(); + Log.Debug($"Handle StructProperty {structName}"); + return structName switch + { + "Guid" => new GuidNode(property, new((Guid*)fieldPtr)), + "SoftObjectPath" => new SoftPathNode(property, GameConfig.Instance.IntoSoftObjectPath(fieldPtr)), + _ => new StructNode(property, fieldPtr, this) + }; + } + + public unsafe IFieldNode Create(IFProperty property, nint fieldPtr) + { + var className = property.ClassPrivate.Name; + return className switch + { + "BoolProperty" => new BoolNode(Factory.CreateFBoolProperty(property.Ptr), new((byte*)fieldPtr)), + "ByteProperty" => new ByteNode(Factory.CreateFByteProperty(property.Ptr), new((byte*)fieldPtr)), + "Int8Property" => new Int8Node(property, new((byte*)fieldPtr)), + "Int16Property" => new Int16Node(property, new((short*)fieldPtr)), + "UInt16Property" => new UInt16Node(property, new((ushort*)fieldPtr)), + "IntProperty" or "Int32Property" => new Int32Node(property, new((int*)fieldPtr)), + "UInt32Property" => new UInt32Node(property, new((uint*)fieldPtr)), + "Int64Property" => new Int64Node(property, new((long*)fieldPtr)), + "UInt64Property" => new UInt64Node(property, new((ulong*)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), + "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), + "SoftClassProperty" => new SoftClassNode(property, new((FSoftObjectPtr*)fieldPtr), Classes), + "StructProperty" => StructNodeHandleSpecial(Factory.CreateFStructProperty(property.Ptr), fieldPtr), + "EnumProperty" => new EnumNode(Factory.CreateFEnumProperty(property.Ptr), new((nint*)fieldPtr)), + "MapProperty" => MapNodeFactory.CreateMapNode(Factory.CreateFMapProperty(property.Ptr), fieldPtr, this), + "ArrayProperty" => new ArrayNode(Factory.CreateFArrayProperty(property.Ptr), fieldPtr, this), + _ => new DummyNode(property) + }; + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ObjectNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ObjectNode.cs new file mode 100644 index 0000000..90391b5 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/ObjectNode.cs @@ -0,0 +1,17 @@ +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class ObjectNode(IFObjectProperty property, nint value, NodeFactory factory) : IFieldNode +{ + private IFObjectProperty Property => property; + private nint Value => value; + private NodeFactory Factory => factory; + + public void ConsumeNode(XmlReader reader) + { + new StructDocument(Property.NamePrivate, Property.PropertyClass, Value, Factory) + .ConsumeNode(reader); + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveFieldNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveFieldNode.cs deleted file mode 100644 index 6cdd19b..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveFieldNode.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Xml; -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Reloaded.ObjectWriters.Writers; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; - -public class PrimitiveFieldNode(string fieldName, nint fieldPtr, int fieldBit, Type fieldType, - IObjectCreator objCreator, string? baseObjsDir = null) : IFieldNode -{ - private readonly IFieldWriter? _writer = FieldWriterFactory.Create(fieldName, fieldPtr, fieldBit, fieldType, objCreator); - - public void ConsumeNode(XmlReader reader) - { - var fieldValue = GetFieldValue(reader); - _writer?.SetField(fieldValue); - - reader.Read(); // Consume node. - Log.Verbose($"{nameof(PrimitiveFieldNode)} || Field '{fieldName}' node consumed."); - } - - private string GetFieldValue(XmlReader reader) - { - // Get content from external text file. - // Attribute path is relative to the objects' folder. - if (baseObjsDir != null && reader.GetAttribute("ext-file") is { } extFileRelative) - { - var extFile = Path.Join(baseObjsDir, extFileRelative); - return File.ReadAllText(extFile); - } - - return reader.GetAttribute("value") ?? reader.ReadElementContentAsString(); - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveNode.cs new file mode 100644 index 0000000..319bd95 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/PrimitiveNode.cs @@ -0,0 +1,68 @@ +using System.Globalization; +using System.Xml; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public static class ObjectXMLFormatProvider +{ + public static IFormatProvider FloatProvider = new CultureInfo("en-US"); +} + +public abstract class BasePrimitiveNode(TProperty property, Ptr value) : IFieldNode + where TProperty : IFProperty + where TValue : unmanaged +{ + protected TProperty Property => property; + protected Ptr Value => value; + // protected TValue? InitialValue { get; set; } + + public void ConsumeNode(XmlReader reader) + { + var fieldValue = GetFieldValue(reader); + SetInitialValue(); + SetField(fieldValue); + + reader.Read(); // Consume node. + Log.Verbose($"{nameof(BasePrimitiveNode)} || Field '{property.NamePrivate}' node consumed."); + } + + protected string GetFieldValue(XmlReader reader) + { + string? baseObjsDir = null; // TODO: Remake this + // Get content from external text file. + // Attribute path is relative to the objects' folder. + if (baseObjsDir != null && reader.GetAttribute("ext-file") is { } extFileRelative) + { + var extFile = Path.Join(baseObjsDir, extFileRelative); + return File.ReadAllText(extFile); + } + + return reader.GetAttribute("value") ?? reader.ReadElementContentAsString(); + } + + protected abstract void SetField(string text); + protected abstract void SetInitialValue(); +} + +public abstract class ValuePrimitiveNode(TProperty property, Ptr value) + : BasePrimitiveNode(property, value) + where TProperty : IFProperty + where TValue: unmanaged +{ + protected TValue? InitialValue { get; set; } + + protected override void SetInitialValue() + { + unsafe { InitialValue ??= *Value.Value; } + } +} + +public abstract class TextPrimitiveNode(TProperty property, Ptr value) + : BasePrimitiveNode(property, value) + where TProperty : IFProperty + where TValue: unmanaged +{ + protected byte[]? InitialValue { get; set; } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftClassNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftClassNode.cs new file mode 100644 index 0000000..61e1a8d --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftClassNode.cs @@ -0,0 +1,29 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types; +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 SoftClassNode(IFProperty property, Ptr value, IUnrealClasses classes) + : TextPrimitiveNode(property, value) +{ + private IUnrealClasses Classes => classes; + + protected override unsafe void SetField(string text) + { + var SoftObjectPath = Classes.IntoSoftObjectPath((nint)(&Value.Value->Super.ObjectId)); + SoftObjectPath.SetAssetPath(text); + Log.Debug($"{nameof(SoftClassNode)} || Field '{Property.NamePrivate}' at 0x{(nint)Value.Value:X} set to '{text}'"); + } + + protected override unsafe void SetInitialValue() + { + if (InitialValue == null) + { + InitialValue = new byte[sizeof(FSoftObjectPtr)]; + Marshal.Copy((nint)Value.Value, InitialValue, 0, sizeof(FSoftObjectPtr)); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftObjectNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftObjectNode.cs new file mode 100644 index 0000000..0d2fdbc --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftObjectNode.cs @@ -0,0 +1,29 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types; +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 SoftObjectNode(IFProperty property, Ptr value, IUnrealClasses classes) + : TextPrimitiveNode(property, value) +{ + private IUnrealClasses Classes => classes; + + protected override unsafe void SetField(string text) + { + var SoftObjectPath = Classes.IntoSoftObjectPath((nint)(&Value.Value->Super.ObjectId)); + SoftObjectPath.SetAssetPath(text); + Log.Debug($"{nameof(SoftObjectNode)} || Field '{Property.NamePrivate}' at 0x{(nint)Value.Value:X} set to '{text}'"); + } + + protected override unsafe void SetInitialValue() + { + if (InitialValue == null) + { + InitialValue = new byte[sizeof(FSoftObjectPtr)]; + Marshal.Copy((nint)Value.Value, InitialValue, 0, sizeof(FSoftObjectPtr)); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftPathNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftPathNode.cs new file mode 100644 index 0000000..8359e23 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/SoftPathNode.cs @@ -0,0 +1,30 @@ +using System.Runtime.InteropServices; +using System.Xml; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Common; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class SoftPathNode(IFProperty property, ISoftObjectPath value) : IFieldNode +{ + private ISoftObjectPath Value => value; + + private byte[]? InitialValue { get; set; } + + private string GetFieldValue(XmlReader reader) + => reader.GetAttribute("value") ?? reader.ReadElementContentAsString(); + + public void ConsumeNode(XmlReader reader) + { + var fieldValue = GetFieldValue(reader); + if (InitialValue != null) + { + InitialValue = new byte[Value.GetSizeOf()]; + Marshal.Copy(Value.Ptr, InitialValue, 0, Value.GetSizeOf()); + } + Value.SetAssetPath(fieldValue); + Log.Verbose($"{nameof(SoftPathNode)}<{Value.GetType().FullName}> || Field '{property.NamePrivate}' at 0x{Value.Ptr:X} set to '{fieldValue}'"); + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs new file mode 100644 index 0000000..8153dd7 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StrNode.cs @@ -0,0 +1,28 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types; +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) + : TextPrimitiveNode(property, value) +{ + private IUnrealObjects UnrealObjects => unrealObjects; + + protected override unsafe void SetField(string text) + { + var fstring = UnrealObjects.CreateFString(text); + *Value.Value = *fstring; + } + + protected override unsafe void SetInitialValue() + { + if (InitialValue == null) + { + InitialValue = new byte[sizeof(FString)]; + Marshal.Copy((nint)Value.Value, InitialValue, 0, sizeof(FString)); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructFieldNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructFieldNode.cs deleted file mode 100644 index 052eb2e..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructFieldNode.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Runtime.InteropServices; -using System.Xml; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; - -internal record FieldData(Type type, nint offset, int bit); - -public class StructFieldNode : IFieldNode -{ - private static readonly Dictionary> CachedStructsFields = []; - - private readonly Dictionary _fields; - private readonly string _structName; - private readonly nint _structPtr; - private readonly Type _structType; - private readonly FieldNodeFactory _nodeFactory; - - private static Dictionary GetFieldsFromStruct(Type structType) - { - var FieldList = new Dictionary(); - int? LastOffset = null; - int CurrentBit = 0; - foreach (var Field in structType.GetFields().SelectMany((x, i) => - { - if (i == 0 && x.Name == "Super") return GetFieldsFromStruct(x.FieldType).AsEnumerable(); - // Handle cases where sequences of booleans are in bitflag form (1 bit) instead of C form (1 byte) - // We'll need to save the current bit as well to correctly write values for Object XML - var CurrentOffset = (int)Marshal.OffsetOf(structType, x.Name); - CurrentBit = x.FieldType == typeof(bool) && LastOffset == CurrentOffset ? CurrentBit + 1 : 0; - LastOffset = CurrentOffset; - return Enumerable.Repeat>(new( - x.Name, new(x.FieldType, CurrentOffset, CurrentBit)), 1); - })) - { - // ToDictionary() implementation will crash if there are duplicate field names - // While this shouldn't be the case with new struct dumps, make sure that the old - // format still works - FieldList.TryAdd(Field.Key, Field.Value); - } - return FieldList; - } - - public StructFieldNode(string structName, nint structPtr, Type structType, FieldNodeFactory nodeFactory) - { - _structName = structName; - _structPtr = structPtr; - _structType = structType; - _nodeFactory = nodeFactory; - - // Cache struct fields data cause reflection slow... - if (CachedStructsFields.TryGetValue(structType.Name, out var cachedFields)) - { - _fields = cachedFields; - } - else - { - _fields = GetFieldsFromStruct(structType); - CachedStructsFields[structType.Name] = _fields; - } - } - - public void ConsumeNode(XmlReader reader) - { - var anyElementFound = false; - while (reader.Read()) - { - if (reader.Name == _structName || reader.NodeType == XmlNodeType.EndElement) break; - if (reader.NodeType != XmlNodeType.Element) continue; - if (AtItemElement(reader)) Log.Warning($"{nameof(StructFieldNode)} || Unexpected '{WriterConstants.ItemTag}' element found. Error?"); - - anyElementFound = true; - - var fieldName = reader.Name; - if (_fields.TryGetValue(fieldName, out var fieldData)) - { - var fieldType = fieldData.type; - var fieldPtr = _structPtr + fieldData.offset; - if (_nodeFactory.TryCreate(fieldName, fieldPtr, fieldData.bit, fieldType, out var fieldNode)) - { - fieldNode.ConsumeNode(reader); - } - else - { - Log.Warning($"{nameof(StructFieldNode)} || Unsupported node type: {fieldType.Name}"); - break; - } - } - else - { - Log.Warning($"{nameof(StructFieldNode)} || Field '{fieldName}' not found in struct '{_structType.Name}'."); - break; - } - } - - if (!anyElementFound) Log.Warning($"{nameof(StructFieldNode)} || No elements found in '{_structName}'. Error?"); - Log.Verbose($"{nameof(StructFieldNode)} || Field '{_structName}' node consumed."); - } - - private static bool AtItemElement(XmlReader reader) - => reader.Name == WriterConstants.ItemTag - && reader.GetAttribute(WriterConstants.ItemIdAttr) != null; -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructNode.cs new file mode 100644 index 0000000..7c3db21 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/StructNode.cs @@ -0,0 +1,17 @@ +using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class StructNode(IFStructProperty property, nint value, NodeFactory factory) : IFieldNode +{ + private IFStructProperty Property => property; + private nint Value => value; + private NodeFactory Factory => factory; + + public void ConsumeNode(XmlReader reader) + { + new StructDocument(Property.NamePrivate, Property.Struct, Value, Factory) + .ConsumeNode(reader); + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TMapFieldNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TMapFieldNode.cs deleted file mode 100644 index 5ccdd2d..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TMapFieldNode.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System.Runtime.InteropServices; -using System.Xml; -using UE.Toolkit.Core.Types; -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; -using UE.Toolkit.Interfaces; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; - -public abstract class TMapBaseFieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) - : IFieldNode where TKeyValue : unmanaged, IMapHashable, IEquatable -{ - public void ConsumeNode(XmlReader reader) - { - var valueType = fieldType.GetGenericArguments()[1]; - var valueSize = Marshal.SizeOf(valueType); - - var tempMap = CreateTempMap(fieldPtr, valueType, nodeFactory.Memory); - - // Get any item nodes. - using var subReader = reader.ReadSubtree(); - subReader.MoveToContent(); - - while (subReader.Read()) - { - if (subReader.NodeType != XmlNodeType.Element) continue; - if (subReader.Name != WriterConstants.ItemTag) - throw new( - $"Only '{WriterConstants.ItemTag}' elements can be directly inside a map. Found: {subReader.Name}"); - - var id = subReader.GetAttribute(WriterConstants.ItemIdAttr); - if (id == null) - { - Log.Error($"{nameof(TMapBaseFieldNode)} || '{WriterConstants.ItemTag}' is missing an ID."); - break; - } - - if (!CreateKeyValue(id, out var KeyMaybe)) - { - Log.Error($"{nameof(TMapBaseFieldNode)} || Could not process map key {id}"); - break; - } - var Key = KeyMaybe!.Value; - unsafe - { - var tempMapBitAlloc = (TArray*)(tempMap.Self + 0x20); - // Ensure that TMap is properly initialized for newly allocated items - BitAllocator's capacity should - // be 0x80 (128) to account for inline bits (equivalent to Reset()). - if (tempMapBitAlloc->ArrayMax < 128) - tempMapBitAlloc->ArrayMax = 128; - } - - // Get existing value - if (!ContainsKey(tempMap, Key)) - { - var newEntry = nodeFactory.Memory.MallocZeroed(valueSize); - tempMap.AddIndirect(Key, newEntry); - nodeFactory.Memory.Free(newEntry); - Log.Debug($"{nameof(TMapBaseFieldNode)} || Added entry at 0x{newEntry:x} with key '{Key}' into '{fieldName}'"); - } - - if (tempMap.TryGetValue(Key, out var valuePtr) && - nodeFactory.TryCreate($"{fieldName} (Key: {Key})", valuePtr, 0, valueType, out var itemNode)) - { - var itemTree = subReader.ReadSubtree(); - itemTree.MoveToContent(); - itemNode.ConsumeNode(itemTree); - } - } - } - - public abstract TMapDynamicDictionary CreateTempMap( - nint fieldPtr, Type valueType, IUnrealMemoryInternal memory); - - public abstract bool CreateKeyValue(string id, out TKeyValue? Value); - - public virtual bool ContainsKey(TMapDynamicDictionary dict, TKeyValue key) - => dict.ContainsKey(key); -} - -public class TMapIntFieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) - : TMapBaseFieldNode(fieldName, fieldPtr, fieldType, nodeFactory) -{ - public override unsafe TMapDynamicDictionary CreateTempMap(nint fieldPtr, Type valueType, IUnrealMemoryInternal memory) - => new((TMap*)fieldPtr, valueType, nodeFactory.Memory); - - public override bool CreateKeyValue(string id, out HashableInt? Value) - { - Value = null; - if (!int.TryParse(id, out var itemIdx)) - { - Log.Warning($"{nameof(TMapIntFieldNode)} || Invalid ID: {id}"); - return false; - } - Value = new HashableInt(itemIdx); - return true; - } -} - -public class TMapInt8FieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) - : TMapBaseFieldNode(fieldName, fieldPtr, fieldType, nodeFactory) -{ - public override unsafe TMapDynamicDictionary CreateTempMap(nint fieldPtr, Type valueType, IUnrealMemoryInternal memory) - => new((TMap*)fieldPtr, valueType, nodeFactory.Memory); - - public override bool CreateKeyValue(string id, out HashableInt8? Value) - { - Value = null; - if (!int.TryParse(id, out var itemIdx)) - { - Log.Warning($"{nameof(TMapInt8FieldNode)} || Invalid ID: {id}"); - return false; - } - Value = new HashableInt8(itemIdx); - return true; - } -} - -public class TMapNameFieldNode(string fieldName, nint fieldPtr, Type fieldType, FieldNodeFactory nodeFactory) - : TMapBaseFieldNode(fieldName, fieldPtr, fieldType, nodeFactory) -{ - public override unsafe TMapDynamicDictionary CreateTempMap(nint fieldPtr, Type valueType, IUnrealMemoryInternal memory) - => new((TMap*)fieldPtr, valueType, nodeFactory.Memory); - - public override bool CreateKeyValue(string id, out FName? Value) - { - Value = new FName(id); - return true; - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TextNode.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TextNode.cs new file mode 100644 index 0000000..91b74d9 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/TextNode.cs @@ -0,0 +1,30 @@ +using System.Runtime.InteropServices; +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; +using UE.Toolkit.Core.Types.Unreal.UE5_4_4; +using UE.Toolkit.Interfaces; +using UE.Toolkit.Reloaded.Common.GameConfigs; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class TextNode(IFProperty property, Ptr value, IUnrealObjects unrealObjects) + : TextPrimitiveNode(property, value) +{ + private IUnrealObjects UnrealObjects => unrealObjects; + + protected override unsafe void SetField(string text) + { + var ftext = UnrealObjects.CreateFText(text); + *Value.Value = *ftext; + } + + protected override unsafe void SetInitialValue() + { + if (InitialValue == null) + { + var allocSize = GameConfig.Instance.GetFTextSize(); + InitialValue = new byte[allocSize]; + Marshal.Copy((nint)Value.Value, InitialValue, 0, allocSize); + } + } +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt16Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt16Node.cs new file mode 100644 index 0000000..c49f36e --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt16Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class UInt16Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToUInt16(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt32Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt32Node.cs new file mode 100644 index 0000000..d695eb8 --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt32Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class UInt32Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToUInt32(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt64Node.cs b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt64Node.cs new file mode 100644 index 0000000..531f6bc --- /dev/null +++ b/UE.Toolkit.Reloaded/ObjectWriters/Nodes/UInt64Node.cs @@ -0,0 +1,9 @@ +using UE.Toolkit.Core.Types; +using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; + +namespace UE.Toolkit.Reloaded.ObjectWriters.Nodes; + +public class UInt64Node(IFProperty property, Ptr value) : ValuePrimitiveNode(property, value) +{ + protected override unsafe void SetField(string text) => *Value.Value = Convert.ToUInt64(text); +} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriter.cs b/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriter.cs index 57d8b87..f50e210 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriter.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriter.cs @@ -3,7 +3,10 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Xml; +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; using UE.Toolkit.Reloaded.ObjectWriters.Nodes; namespace UE.Toolkit.Reloaded.ObjectWriters; @@ -12,9 +15,9 @@ public unsafe class ObjectWriter { private static readonly EventLoopScheduler WriterScheduler = new(); - private readonly Type _objType; + private readonly string _objType; private readonly string _objFile; - private readonly FieldNodeFactory _nodeFactory; + private readonly NodeFactory _factory; private readonly FileSystemWatcher _xmlFileWatcher; private readonly Subject _xmlChanged = new(); private readonly IDisposable _xmlSub; @@ -22,11 +25,13 @@ public unsafe class ObjectWriter private byte[] _xmlContent; private UObjectBase* _currObj; - public ObjectWriter(string objName, Type objType, string objFile, FieldNodeFactory nodeFactory, string? objectPath) + private IUnrealClasses? _unrealClasses; + + public ObjectWriter(string objName, string objType, string objFile, NodeFactory factory, string? objectPath) { _objType = objType; _objFile = objFile; - _nodeFactory = nodeFactory; + _factory = factory; _xmlContent = File.ReadAllBytes(objFile); _xmlFileWatcher = new(Path.GetDirectoryName(objFile)!, Path.GetFileName(objFile)) { @@ -55,15 +60,24 @@ public void WriteToObject(nint objPtr) using var reader = XmlReader.Create(new MemoryStream(_xmlContent)); reader.MoveToContent(); - // TODO: Possibly rework XML node tree creation to return - // a collection of generated writers to allow resetting values on rewrites. - if (_nodeFactory.TryCreate(ObjectName, objPtr, 0, _objType, out var rootNode)) + IFieldNode? rootNode = null; + // The root object *has* to be a class since UObjects contain the serialization methods needed to convert + // to and from a file-based representation (UAssets are just binary files representing UObjects). + if (_factory.Classes.GetClassInfoFromName($"U{_objType}", out var rootClass) || // If the prefix is not included + _factory.Classes.GetClassInfoFromName($"U{_objType[1..]}", out rootClass)) // If the prefix is included (Theo!!!!) + { + rootNode = rootClass.NamePrivate.ToString() == "DataTable" + ? new DataTableDocument(ObjectName, objPtr, _factory) + : new StructDocument(ObjectName, rootClass, objPtr, _factory); + } + + if (rootNode != null) { rootNode.ConsumeNode(reader); } else { - Log.Error($"Failed to create root node from Object XML file.\nFile: {_objFile}"); + Log.Error($"{nameof(ObjectWriter)} || Failed to create root node with type '{_objType}' from Object XML file.\nFile: {_objFile}"); } } diff --git a/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriterService.cs b/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriterService.cs index cfcfeb4..f851e44 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriterService.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/ObjectWriterService.cs @@ -1,4 +1,5 @@ using System.Xml; +using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; using UE.Toolkit.Interfaces; using UE.Toolkit.Interfaces.ObjectWriters; @@ -8,11 +9,11 @@ namespace UE.Toolkit.Reloaded.ObjectWriters; -public class ObjectWriterService(ITypeRegistry typeReg, IUnrealObjects uobjs, IDataTables dt, IUnrealMemory memory) +public class ObjectWriterService(IUnrealObjects uobjs, IDataTables dt, IUnrealMemory memory, + IUnrealClasses unrealClasses, IUnrealFactory unrealFactory) { - private readonly Dictionary _types = []; private readonly List _objWriters = []; - private readonly FieldNodeFactory _nodeFactory = new(typeReg, uobjs, memory); + private readonly NodeFactory _nodeFactory = new(uobjs, memory, unrealClasses, unrealFactory); public void AddPath(string path) { @@ -38,53 +39,30 @@ private void RegisterFolder(string folder) private unsafe void RegisterFile(string objFile) { - const string any = "any"; var objName = Path.GetFileName(objFile).Replace(".obj.xml", string.Empty); using var reader = XmlReader.Create(File.OpenRead(objFile)); reader.MoveToContent(); var rootTypeName = reader.Name; - var rootTypeProvider = reader.GetAttribute("provider"); var rootTypePath = reader.GetAttribute("path"); - - var typeKey = new TypeKey(rootTypeName, rootTypeProvider); - if (!_types.TryGetValue(typeKey, out var objType)) - { - // Find type if not previously registered. - var rootTypeHint = reader.GetAttribute(WriterConstants.HintAttr); - if (typeReg.TryGetType(rootTypeName, rootTypeHint, rootTypeProvider, out objType)) - { - _types[typeKey] = objType; - Log.Debug($"{nameof(ObjectWriterService)} || Registered Type: {rootTypeName} || Provider: {rootTypeProvider ?? any}"); - } - else - { - Log.Error($"{nameof(ObjectWriterService)} || Failed to find type '{rootTypeName}' with provider '{rootTypeProvider ?? any}'.\nFile: {objFile}"); - return; - } - } - var objWriter = new ObjectWriter(objName, objType, objFile, _nodeFactory, rootTypePath); + var objWriter = new ObjectWriter(objName, rootTypeName, objFile, _nodeFactory, rootTypePath); _objWriters.Add(objWriter); - if (objType.Name.StartsWith(nameof(UDataTable))) + if (rootTypeName == "DataTable") { - if (objWriter.ObjectPath != null) - dt.OnDataTableChangedByPath(objWriter.ObjectPath, table => objWriter.WriteToObject((nint)table.Self)); - else - dt.OnDataTableChanged(objWriter.ObjectName, table => objWriter.WriteToObject((nint)table.Self)); + Action>> Callback = + objWriter.ObjectPath != null ? dt.OnDataTableChangedByPath : dt.OnDataTableChanged; + Callback(objWriter.ObjectPath ?? objWriter.ObjectName, x => objWriter.WriteToObject((nint)x.Self)); } else { - if (objWriter.ObjectPath != null) - uobjs.OnObjectLoadedByPath(objWriter.ObjectPath, obj => objWriter.WriteToObject((nint)obj.Self)); - else - uobjs.OnObjectLoadedByName(objWriter.ObjectName, obj => objWriter.WriteToObject((nint)obj.Self)); + Action>> Callback = + objWriter.ObjectPath != null ? uobjs.OnObjectLoadedByPath : uobjs.OnObjectLoadedByName; + Callback(objWriter.ObjectPath ?? objWriter.ObjectName, x => objWriter.WriteToObject((nint)x.Self)); } Log.Information($"{nameof(ObjectWriterService)} || Object XML registered: {objWriter.GetObjectNameOrPath()}\nFile: {objFile}"); } - - private readonly record struct TypeKey(string TypeName, string? TypeProvider); } \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/TypeRegistry.cs b/UE.Toolkit.Reloaded/ObjectWriters/TypeRegistry.cs index 57bf954..36f8fc4 100644 --- a/UE.Toolkit.Reloaded/ObjectWriters/TypeRegistry.cs +++ b/UE.Toolkit.Reloaded/ObjectWriters/TypeRegistry.cs @@ -3,63 +3,19 @@ namespace UE.Toolkit.Reloaded.ObjectWriters; +// !!!! WARNING +// Type providers are no longer required for Object XML to work as of 1.9.0 since the object information is +// retrieved at runtime. This class is now a no-op! +// !!!! WARNING public class TypeRegistry : ITypeRegistry { private readonly Dictionary _providers = []; - public void RegisterProvider(ITypeProvider provider) - { - if (_providers.TryAdd(provider.Id, provider)) - { - Log.Information($"{nameof(ObjectWriterService)} || Type provider registered: {provider.Id}"); - } - else - { - Log.Warning($"{nameof(ObjectWriterService)} || Failed to register type provider with duplicate ID: {provider.Id}"); - } - } + public void RegisterProvider(ITypeProvider provider) {} public bool TryGetType(string typeName, string? typeHint, string? providerId, [NotNullWhen(true)] out Type? type) - { - const string typeObj = WriterConstants.HintAttrObject; - const string typeActor = WriterConstants.HintAttrActor; - const string typeStruct = WriterConstants.HintAttrStruct; - - // Try resolving with type hint, if provided. - if (!string.IsNullOrEmpty(typeHint)) - { - switch (typeHint) - { - case typeStruct when TryGetType($"F{typeName}", providerId, out type): - case typeActor when TryGetType($"A{typeName}", providerId, out type): - case typeObj when TryGetType($"U{typeName}", providerId, out type): - return true; - } - } - - // Try resolving by name directly. - if (TryGetType(typeName, providerId, out type)) return true; - - // Fallback to trying every prefix... - if (TryGetType($"F{typeName}", providerId, out type)) return true; - if (TryGetType($"A{typeName}", providerId, out type)) return true; - if (TryGetType($"U{typeName}", providerId, out type)) return true; - - return false; - } + => throw new Exception("TypeRegistry is no longer used"); public bool TryGetType(string typeName, string? providerId, [NotNullWhen(true)] out Type? type) - { - type = null; - if (providerId == null) - { - type = _providers.Values.FirstOrDefault(x => x.CanProvide(typeName))?.GetType(typeName); - } - else if (_providers.TryGetValue(providerId, out var provider) && provider.CanProvide(typeName)) - { - type = provider.GetType(typeName); - } - - return type != null; - } + => throw new Exception("TypeRegistry is no longer used"); } diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Writers/FieldWriterFactory.cs b/UE.Toolkit.Reloaded/ObjectWriters/Writers/FieldWriterFactory.cs deleted file mode 100644 index 42d27f0..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Writers/FieldWriterFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; -using UE.Toolkit.Reloaded.Common.GameConfigs; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Writers; - -public static class FieldWriterFactory -{ - - public static IFieldWriter? Create(string fieldName, nint fieldPtr, int fieldBit, Type fieldType, IObjectCreator objCreator) - { - if (fieldType.IsPrimitive || fieldType.IsEnum || fieldType.Name == nameof(String)) - return new PrimitiveFieldWriter(fieldName, fieldPtr, fieldBit, fieldType); - - if (fieldType == GameConfig.Instance.GetFText() || fieldType == typeof(FString) || fieldType == typeof(FName)) - return new TextFieldWriter(fieldName, fieldPtr, fieldType, objCreator); - - if (fieldType.Name.StartsWith("TSoftObjectPtr") || fieldType.Name.StartsWith("TSoftClassPtr")) - return new PtrFieldWriter(fieldName, fieldPtr, fieldType, objCreator); - - Log.Error($"{nameof(FieldWriterFactory)} || No writer found for field '{fieldName}' of type '{fieldType.Name}'."); - return null; - } - - private class DummyFieldWriter : IFieldWriter - { - public void Reset() - { - } - - public void SetField(string value) - { - } - - public void SetField(TValue value) where TValue : unmanaged - { - } - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Writers/IFieldWriter.cs b/UE.Toolkit.Reloaded/ObjectWriters/Writers/IFieldWriter.cs deleted file mode 100644 index 80099a6..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Writers/IFieldWriter.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace UE.Toolkit.Reloaded.ObjectWriters.Writers; - -public interface IFieldWriter -{ - void Reset(); - - void SetField(string value); - - void SetField(TValue value) - where TValue : unmanaged; -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Writers/PrimitiveFieldWriter.cs b/UE.Toolkit.Reloaded/ObjectWriters/Writers/PrimitiveFieldWriter.cs deleted file mode 100644 index 1235d50..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Writers/PrimitiveFieldWriter.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Globalization; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Writers; - -public unsafe class PrimitiveFieldWriter(string fieldName, nint fieldPtr, int fieldBit, Type fieldType) : IFieldWriter -{ - private static IFormatProvider FloatProvider = new CultureInfo("en-US"); - - private nint? _ogValue; - private Type _fieldType = fieldType; - private int _fieldBit = fieldBit; - - public void Reset() - { - if (_ogValue != null) SetField((nint)_ogValue); - } - - public void SetField(string value) - { - // Handle enum values either by name or integer. - if (_fieldType.IsEnum) - { - var enumType = _fieldType; - _fieldType = Enum.GetUnderlyingType(enumType); - - // Value is enum member name. - if (Enum.TryParse(enumType, value, true, out var nameValue)) - { - SetField(Convert.ToDouble(nameValue, FloatProvider)); - } - - // Value is enum integer value. - else if (double.TryParse(value, FloatProvider, out var intValue)) - { - SetField(intValue); - } - } - else if (bool.TryParse(value, out var boolValue)) - { - SetField(boolValue); - } - else if (double.TryParse(value, FloatProvider, out var numValue)) - { - SetField(numValue); - } - else - { - Log.Error($"{nameof(PrimitiveFieldWriter)} || Invalid value '{value}' for field '{fieldName}'."); - } - } - - public void SetField(TValue value) - where TValue : unmanaged - { - _ogValue ??= *(nint*)fieldPtr; - - switch (_fieldType.Name) - { - case "Int64": - *(nint*)fieldPtr = (nint)Convert.ToInt64(value); - break; - case "UInt64": - *(nint*)fieldPtr = (nint)Convert.ToUInt64(value); - break; - case "Int32": - *(int*)fieldPtr = Convert.ToInt32(value); - break; - case "UInt32": - *(uint*)fieldPtr = Convert.ToUInt32(value); - break; - case "Int16": - *(short*)fieldPtr = Convert.ToInt16(value); - break; - case "UInt16": - *(ushort*)fieldPtr = Convert.ToUInt16(value); - break; - case "Byte": - *(byte*)fieldPtr = Convert.ToByte(value); - break; - case "SByte": - *(sbyte*)fieldPtr = Convert.ToSByte(value); - break; - case "Boolean": - var Value = Convert.ToBoolean(value); - *(byte*)fieldPtr |= (byte)(*(byte*)&Value << _fieldBit); - break; - case "Single": - *(float*)fieldPtr = Convert.ToSingle(value, FloatProvider); - break; - case "Double": - *(double*)fieldPtr = Convert.ToDouble(value, FloatProvider); - break; - default: - Log.Error($"{nameof(PrimitiveFieldWriter)} || Invalid type '{_fieldType.Name}' for field '{fieldName}'."); - return; - } - - Log.Debug($"{nameof(PrimitiveFieldWriter)} || Field '{fieldName}' at 0x{fieldPtr:X} set to: {value}"); - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Writers/PtrFieldWriter.cs b/UE.Toolkit.Reloaded/ObjectWriters/Writers/PtrFieldWriter.cs deleted file mode 100644 index 4494a51..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Writers/PtrFieldWriter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Runtime.InteropServices; -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Writers; - -public unsafe class PtrFieldWriter(string fieldName, nint fieldPtr, Type fieldType, IObjectCreator objCreator) : IFieldWriter -{ - - private byte[]? _ogData; - - public void Reset() - { - if (_ogData != null) Marshal.Copy(_ogData, 0, fieldPtr, _ogData.Length); - } - - public void SetField(string value) - { - var strPtr = Marshal.StringToHGlobalUni(value); - SetField(strPtr); - Marshal.FreeHGlobal(strPtr); - } - - public void SetField(TValue value) where TValue : unmanaged - { - var strValue = Marshal.PtrToStringUni(*(nint*)&value)!; - if (fieldType.Name.StartsWith("TSoftObjectPtr") || fieldType.Name.StartsWith("TSoftClassPtr")) - { - var SizeOf = Marshal.SizeOf(); - _ogData = new byte[SizeOf]; - Marshal.Copy(fieldPtr, _ogData, 0, SizeOf); - var ObjectPtr = (TSoftObjectPtr*)fieldPtr; - ObjectPtr->SoftObjectPtr.Super.ObjectId.AssetPath.PackageName = new FName(strValue); - Log.Debug($"{nameof(PtrFieldWriter)} || Field '{fieldName}' at 0x{fieldPtr:X} set to: {strValue}"); - } - else - { - Log.Error($"{nameof(PtrFieldWriter)} || Invalid type '{fieldType.Name}' for field '{fieldName}'."); - } - } -} \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/ObjectWriters/Writers/TextFieldWriter.cs b/UE.Toolkit.Reloaded/ObjectWriters/Writers/TextFieldWriter.cs deleted file mode 100644 index 0512678..0000000 --- a/UE.Toolkit.Reloaded/ObjectWriters/Writers/TextFieldWriter.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Runtime.InteropServices; -using UE.Toolkit.Core.Types.Interfaces; -using UE.Toolkit.Core.Types.Unreal.UE5_4_4; -using UE.Toolkit.Reloaded.Common.GameConfigs; - -namespace UE.Toolkit.Reloaded.ObjectWriters.Writers; - -public unsafe class TextFieldWriter(string fieldName, nint fieldPtr, Type fieldType, IObjectCreator objCreator) : IFieldWriter -{ - private byte[]? _ogData; - - public void Reset() - { - if (_ogData != null) Marshal.Copy(_ogData, 0, fieldPtr, _ogData.Length); - } - - public void SetField(string value) - { - var strPtr = Marshal.StringToHGlobalUni(value); - SetField(strPtr); - Marshal.FreeHGlobal(strPtr); - } - - public void SetField(TValue value) where TValue : unmanaged - { - var strValue = Marshal.PtrToStringUni(*(nint*)&value)!; - switch (fieldType.Name) - { - case nameof(FText): - var allocSize = GameConfig.Instance.GetFTextSize(); - _ogData = new byte[allocSize]; - Marshal.Copy(fieldPtr, _ogData, 0, allocSize); - var ftext = objCreator.CreateFText(strValue); - *(FText*)fieldPtr = *ftext; - break; - case nameof(FString): - _ogData = new byte[sizeof(FString)]; - Marshal.Copy(fieldPtr, _ogData, 0, sizeof(FString)); - var fstring = objCreator.CreateFString(strValue); - *(FString*)fieldPtr = *fstring; - break; - case nameof(FName): - _ogData = new byte[sizeof(FName)]; - Marshal.Copy(fieldPtr, _ogData, 0, sizeof(FName)); - *(FName*)fieldPtr = new(strValue); - break; - default: - Log.Error($"{nameof(TextFieldWriter)} || Invalid type '{fieldType.Name}' for field '{fieldName}'."); - return; - } - - Log.Debug($"{nameof(TextFieldWriter)} || Field '{fieldName}' at 0x{fieldPtr:X} set to: {strValue}"); - } -} diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/SMT5V-Win64-Shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/SMT5V-Win64-Shipping/scans.ini index 8429952..0c5feec 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/SMT5V-Win64-Shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/SMT5V-Win64-Shipping/scans.ini @@ -48,8 +48,11 @@ GetPrivateStaticClassBody_UE5=DISABLED StaticConstructObject_Internal=48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 54 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC B0 01 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 48 8B 39 ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 4D 8B F0 48 8B DA 48 8B F1 48 8B C2 66 90 48 83 78 ?? 00 48 8D 78 ?? 74 ?? 33 C9 F7 40 ?? 00 00 00 10 74 ?? 48 8B C8 E8 ?? ?? ?? ?? 48 8B C8 48 8B C1 48 85 C0 75 ?? 48 8B 07 EB ?? 48 8B 48 ?? 48 8D 54 24 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 2D ?? ?? ?? ?? 41 B9 01 00 00 00 C6 44 24 ?? 00 48 8B CD 48 C7 44 24 ?? 00 00 00 00 48 0F 45 4C 24 ?? 49 8B D6 45 8D 41 ?? E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? FF D6 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 +GetStaticStruct_RESULT=result - 219 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 49 8B D6 +GetStaticEnum_RESULT=result - 219 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC 08 03 00 00 ;ConstructUScriptStruct_RESULT= diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/scans.ini index eba4497..d6fa4bd 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/chronos-win64-shipping/scans.ini @@ -45,14 +45,17 @@ FTextInspector_GetTableIdAndKey=48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 GetPrivateStaticClassBody_UE4=DISABLED ;GetPrivateStaticClassBody_RESULT= -GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 60 45 33 ED +GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 30 4D 8B F1 ;GetPrivateStaticClassBody_RESULT= StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 75 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 75 ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC C8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/scans.ini index 499f404..7d99b9c 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_50-win64-shipping/scans.ini @@ -53,8 +53,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 4D 8B F0 48 8B DA 48 8B F1 48 8B C2 66 90 48 83 78 ?? 00 48 8D 78 ?? 74 ?? 33 C9 F7 40 ?? 00 00 00 10 74 ?? 48 8B C8 E8 ?? ?? ?? ?? 48 8B C8 48 8B C1 48 85 C0 75 ?? 48 8B 07 EB ?? 48 8B 48 ?? 48 8D 54 24 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 2D ?? ?? ?? ?? 41 B9 01 00 00 00 C6 44 24 ?? 00 48 8B CD 48 C7 44 24 ?? 00 00 00 00 48 0F 45 4C 24 ?? 49 8B D6 45 8D 41 ?? E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? FF D6 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 +GetStaticStruct_RESULT=result - 219 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 49 8B D6 +GetStaticEnum_RESULT=result - 219 ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC C8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/scans.ini index dddc343..2bc7718 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_51-win64-shipping/scans.ini @@ -52,8 +52,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 75 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 75 ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC C8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/scans.ini index 01f3672..c692b9b 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_52-win64-shipping/scans.ini @@ -51,8 +51,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 75 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 75 ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC C8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_53-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_53-win64-shipping/scans.ini index 04669a9..6e46725 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_53-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_53-win64-shipping/scans.ini @@ -51,8 +51,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B F8 48 8B DA FF D1 48 8B CB 48 8B F0 E8 ?? ?? ?? ?? 48 8D 54 24 ?? 48 8B 48 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8D 54 24 ?? 48 8B 48 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 34 + +GetStaticEnum=48 8D 54 24 ?? 48 8B 48 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D7 +GetStaticEnum_RESULT=result - 34 ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC A8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_54-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_54-win64-shipping/scans.ini index a4527d5..85d7760 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_54-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_54-win64-shipping/scans.ini @@ -48,8 +48,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 8B 43 ?? 33 C9 0F BA E0 1C 73 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B C8 48 8B D9 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 78 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 78 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC 98 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/scans.ini index 463e21c..aee0cf1 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_55-win64-shipping/scans.ini @@ -51,8 +51,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 8B 43 ?? 33 C9 0F BA E0 1C 73 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B C8 48 8B D9 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 78 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 78 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC A8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/scans.ini index b390c11..7e88b43 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_56-win64-shipping/scans.ini @@ -52,8 +52,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 8B 43 ?? 33 C9 0F BA E0 1C 73 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B C8 48 8B D9 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 78 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 78 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC B8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/scans.ini index 82bbc1c..9af1280 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/iostoretest_57-win64-shipping/scans.ini @@ -53,8 +53,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 55 53 41 56 49 8D AB ?? ?? ?? ?? 48 81 EC B0 02 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 8B 41 ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 55 56 57 48 83 EC 50 49 8B D8 48 8B FA FF D1 48 8B D3 48 8D 4C 24 ?? 48 8B E8 E8 ?? ?? ?? ?? 48 8B 18 66 0F 1F 84 ?? 00 00 00 00 48 83 7F ?? 00 48 8D 77 ?? 74 ?? 8B 47 ?? 33 C9 0F BA E0 1C 73 ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C8 48 8B F9 48 85 FF 75 ?? 48 8B 3E EB ?? 48 8B 47 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? 48 0F 45 54 24 ?? E8 ?? ?? ?? ?? 41 B9 02 00 00 00 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 47 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? 48 0F 45 54 24 ?? E8 ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 6C 24 ?? C6 44 24 ?? 00 45 8B C1 48 8B D3 48 C7 44 24 ?? 00 00 00 00 48 8B 08 E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 85 C9 +GetStaticStruct_RESULT=result - 94 + +GetStaticEnum=48 8B 47 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 15 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? 48 0F 45 54 24 ?? E8 ?? ?? ?? ?? 48 89 6C 24 ?? +GetStaticEnum_RESULT=result - 94 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC B8 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/scans.ini new file mode 100644 index 0000000..4f98910 --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/scans.ini @@ -0,0 +1,68 @@ +;Jujutsu Kaisen Cursed Clash (5.1) + +[Scans] +GFNamePool=48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 8B C0 C6 05 ?? ?? ?? ?? 01 8B D3 0F B7 C3 89 44 24 +GFNamePool_RESULT=GetGlobalAddress(result + 3) + +;FNameHelper_FindOrStoreString=48 89 74 24 ?? 57 48 83 EC 60 81 79 ?? 00 04 00 00 +;FNameHelper_FindOrStoreString_RESULT= + +;??0FName@@QEAA@PEB_WW4EFindName@@@Z +FName_Ctor_Wide=48 89 5C 24 ?? 57 48 83 EC 30 48 8B D9 48 89 54 24 ?? 33 C9 +;FName_Ctor_Wide_RESULT= + +;?FromString@FText@@SA?AV1@$$QEAVFString@@@Z +FText_FromString=48 89 6C 24 ?? 57 48 83 EC 50 83 7A ?? 01 48 8B F9 48 89 74 24 ?? C7 44 24 ?? 00 00 00 00 7F ?? E8 ?? ?? ?? ?? 48 8B 48 ?? 48 8B 10 48 89 54 24 ?? 48 89 4C 24 ?? 48 85 C9 74 ?? F0 FF 41 ?? 8B 40 ?? BE 01 00 00 00 89 44 24 ?? 48 8D 44 24 ?? EB ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? BE 02 00 00 00 48 8B 10 48 89 17 48 8B 48 ?? 48 89 4F ?? 48 85 C9 74 ?? F0 FF 41 ?? 8B 40 ?? BD FF FF FF FF 89 47 ?? 48 89 5C 24 ?? 40 F6 C6 02 74 ?? 48 8B 5C 24 ?? 83 E6 FD 48 85 DB 74 ?? 8B C5 F0 0F C1 43 ?? 83 F8 01 75 ?? 48 8B 03 48 8B CB FF 10 8B C5 F0 0F C1 43 ?? 83 F8 01 75 ?? 48 8B 03 8D 55 ?? 48 8B CB FF 50 ?? 40 F6 C6 01 48 8B 74 24 ?? 74 ?? 48 8B 4C 24 ?? 48 85 C9 74 ?? 8B C5 F0 0F C1 41 ?? 83 F8 01 75 ?? 48 8B 5C 24 ?? 48 8B CB 48 8B 03 FF 10 F0 0F C1 6B ?? 83 FD 01 75 ?? 48 8B 4C 24 ?? 8B D5 48 8B 01 FF 50 ?? 83 4F ?? 12 +;FText_FromString_RESULT= + +FText_ToString=E8 ?? ?? ?? ?? 48 8D 1D ?? ?? ?? ?? 83 78 ?? 00 74 ?? 48 8B 30 EB ?? 48 8B F3 48 8D 4D ?? E8 ?? ?? ?? ?? 83 78 ?? 00 74 ?? 48 8B 18 4C 8B C6 48 8B D3 33 C9 E8 ?? ?? ?? ?? 48 8B 5D ?? 48 8B B4 24 ?? ?? ?? ?? +FText_ToString_RESULT=GetGlobalAddress(result + 1) + +UObject_PostLoadSubobjects=40 53 48 83 EC 20 48 8B 41 ?? 48 8B D9 8B 90 ?? ?? ?? ?? +;UObject_PostLoadSubobjects_RESULT= + +UObject_BeginDestroy=40 53 48 83 EC 30 8B 41 ?? 48 8B D9 C1 E8 0F +;UObject_BeginDestroy_RESULT= + +;From StaticExit +GUObjectArray=48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 74 ?? F2 0F 10 0D ?? ?? ?? ?? +GUObjectArray_RESULT=GetGlobalAddress(result + 3) + +UStruct_IsChildOf=48 85 D2 74 ?? 48 63 42 ?? 4C 8D 42 +;UStruct_IsChildOf_RESULT= + +UUserDefinedEnum_GetDisplayNameTextByIndex=48 89 5C 24 ?? 55 56 57 48 83 EC 30 48 8B FA +;UUserDefinedEnum_GetDisplayNameTextByIndex_RESULT= + +UDataTable_HandleDataTableChanged=48 89 54 24 ?? 55 53 56 48 8D 6C 24 ?? 48 81 EC A0 00 00 00 +;UDataTable_HandleDataTableChanged_RESULT= + +GMalloc=48 8B 0D ?? ?? ?? ?? 48 85 C9 75 ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 48 8B D3 FF 50 ?? 48 83 C4 20 +GMalloc_RESULT=GetGlobalAddress(result + 3) + +FTextInspector_GetTableIdAndKey=E8 ?? ?? ?? ?? 4C 8D 44 24 ?? 48 89 5C 24 ?? 48 8D 54 24 ?? 48 89 5C 24 ?? +FTextInspector_GetTableIdAndKey_RESULT=GetGlobalAddress(result + 1) + +GetPrivateStaticClassBody_UE4=DISABLED +;GetPrivateStaticClassBody_RESULT= + +GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 30 4D 8B F1 +;GetPrivateStaticClassBody_RESULT= + +StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? +;StaticConstructObject_Internal_RESULT= + +GetStaticStruct=83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 58 + +GetStaticEnum=83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D7 +GetStaticEnum_RESULT=result - 58 + +ConstructUScriptStruct=40 53 56 57 41 55 41 56 41 57 48 81 EC C8 03 00 00 +;ConstructUScriptStruct + +GEngine=48 89 05 ?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? 48 8D 4D ?? +GEngine_RESULT=GetGlobalAddress(result + 3) + +UWorld_SpawnActor=E8 ?? ?? ?? ?? 48 8B D0 48 89 44 24 ?? 48 8B CD E8 ?? ?? ?? ?? 8B 45 ?? +UWorld_SpawnActor_RESULT=GetGlobalAddress(result + 1) \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/unreal.ini new file mode 100644 index 0000000..26bfdbb --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/jujutsu kaisen cc/unreal.ini @@ -0,0 +1,20 @@ +;Jujutsu Kaisen Cursed Clash (5.1) + +[FMallocBinned2] +Free=0x30 +GetAllocSize=0x40 +Malloc=0x10 +Realloc=0x20 +QuantizeSize=0x38 + +[UObject] +ProcessEvent=0x268 + +[UScriptStruct] +GetCustomGuid=0x378 + +[UPackage] +GamePackage=/Script/IOStoreTest_51 + +[UStruct] +Link=0x2c8 \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/p3r/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/p3r/scans.ini index 730277f..06859be 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/p3r/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/p3r/scans.ini @@ -47,8 +47,11 @@ GetPrivateStaticClassBody_UE5=DISABLED StaticConstructObject_Internal=48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 54 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC B0 01 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 48 8B 39 ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 4D 8B F0 48 8B DA 48 8B F1 48 8B C2 66 90 48 83 78 ?? 00 48 8D 78 ?? 74 ?? 33 C9 F7 40 ?? 00 00 00 10 74 ?? 48 8B C8 E8 ?? ?? ?? ?? 48 8B C8 48 8B C1 48 85 C0 75 ?? 48 8B 07 EB ?? 48 8B 48 ?? 48 8D 54 24 ?? 48 89 4C 24 ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 2D ?? ?? ?? ?? 41 B9 01 00 00 00 C6 44 24 ?? 00 48 8B CD 48 C7 44 24 ?? 00 00 00 00 48 0F 45 4C 24 ?? 49 8B D6 45 8D 41 ?? E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? FF D6 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 33 C0 F7 43 ?? 00 00 00 10 74 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B D8 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 +GetStaticStruct_RESULT=result - 219 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 49 8B D6 +GetStaticEnum_RESULT=result - 219 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC 08 03 00 00 ;ConstructUScriptStruct_RESULT= diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/scans.ini new file mode 100644 index 0000000..881c8cf --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/scans.ini @@ -0,0 +1,66 @@ +;Master Detective Archives RAIN CODE (4.27) +;Tested by https://github.com/raycopper + +[Scans] +GFNamePool=48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? C6 05 ?? ?? ?? ?? 01 0F 10 03 +GFNamePool_RESULT=GetGlobalAddress(result + 3) + +FNameHelper_FindOrStoreString=DISABLED +;FNameHelper_FindOrStoreString_RESULT= + +FName_Ctor_Wide=48 89 5C 24 ?? 57 48 83 EC 30 48 8B D9 48 89 54 24 ?? 33 C9 41 8B F8 4C 8B DA +;FName_Ctor_Wide_RESULT= + +FText_FromString=48 89 74 24 ?? 57 48 83 EC 50 83 7A ?? 01 48 8B F9 48 89 6C 24 ?? C7 44 24 ?? 00 00 00 00 7F ?? E8 ?? ?? ?? ?? 48 8B 48 ?? 48 8B 10 48 89 54 24 ?? 48 89 4C 24 ?? 48 85 C9 74 ?? F0 FF 41 ?? 8B 40 ?? BD 01 00 00 00 89 44 24 ?? 48 8D 44 24 ?? EB ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? BD 02 00 00 00 48 8B 10 48 89 17 48 8B 48 ?? 48 89 4F ?? 48 85 C9 74 ?? F0 FF 41 ?? 8B 40 ?? BE FF FF FF FF 89 47 ?? 48 89 5C 24 ?? 40 F6 C5 02 74 ?? 48 8B 5C 24 ?? 83 E5 FD 48 85 DB 74 ?? 8B C6 F0 0F C1 43 ?? 83 F8 01 75 ?? 48 8B 03 48 8B CB FF 10 8B C6 F0 0F C1 43 ?? 83 F8 01 75 ?? 48 8B 03 8D 56 ?? 48 8B CB FF 50 ?? 40 F6 C5 01 48 8B 6C 24 ?? 74 ?? 48 8B 4C 24 ?? 48 85 C9 74 ?? 8B C6 F0 0F C1 41 ?? 83 F8 01 75 ?? 48 8B 5C 24 ?? 48 8B CB 48 8B 03 FF 10 F0 0F C1 73 ?? 83 FE 01 75 ?? 48 8B 4C 24 ?? 8B D6 48 8B 01 FF 50 ?? 83 4F ?? 12 +;FText_FromString_RESULT= + +FText_ToString=40 53 48 83 EC 20 48 8B D9 E8 ?? ?? ?? ?? 48 8B 0B 48 8B 01 +;FText_ToString_RESULT= + +UObject_PostLoadSubobjects=40 53 48 83 EC 20 48 8B 41 ?? 48 8B D9 8B 90 ?? ?? ?? ?? C1 EA 17 +;UObject_PostLoadSubobjects_RESULT= + +UObject_BeginDestroy=40 53 48 83 EC 30 8B 41 ?? 48 8B D9 C1 E8 0F +;UObject_BeginDestroy_RESULT= + +GUObjectArray=48 8B 05 ?? ?? ?? ?? 48 8B 0C ?? 48 8D 04 ?? 48 85 C0 74 ?? 44 39 40 ?? 75 ?? F7 40 ?? 00 00 00 30 75 ?? 48 8B 00 +GUObjectArray_RESULT=GetGlobalAddress(result + 3) - 0x10 + +;Inlined +UStruct_IsChildOf=DISABLED +;UStruct_IsChildOf_RESULT= + +UUserDefinedEnum_GetDisplayNameTextByIndex=48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 30 48 8B FA 41 8B E8 +;UUserDefinedEnum_GetDisplayNameTextByIndex_RESULT= + +UDataTable_HandleDataTableChanged=48 89 54 24 ?? 55 53 56 57 48 8D 6C 24 ?? 48 81 EC 98 00 00 00 +;UDataTable_HandleDataTableChanged_RESULT= + +;GMalloc= +;GMalloc_RESULT= + +FTextInspector_GetTableIdAndKey=48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B D9 49 8B F8 48 8B 09 48 8B F2 48 8B 01 + +GetPrivateStaticClassBody_UE4=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 60 45 33 ED +;GetPrivateStaticClassBody_RESULT= + +GetPrivateStaticClassBody_UE5=DISABLED +;GetPrivateStaticClassBody_RESULT= + +StaticConstructObject_Internal=48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 54 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC B0 01 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 48 8B 39 +;StaticConstructObject_Internal_RESULT= + +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 45 8B C1 +GetStaticStruct_RESULT=result - 219 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 41 B9 02 00 00 00 C6 44 24 ?? 00 49 8B D6 +GetStaticEnum_RESULT=result - 219 + +ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC 08 03 00 00 +;ConstructUScriptStruct_RESULT= + +GEngine=48 89 05 ?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? 48 8D 4D ?? +GEngine_RESULT=GetGlobalAddress(result + 3) + +UWorld_SpawnActor=40 55 53 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC F8 01 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 ?? +;UWorld_SpawnActor_RESULT= \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/unreal.ini new file mode 100644 index 0000000..cd4a7ec --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/raincodeplus-win64-shipping/unreal.ini @@ -0,0 +1,18 @@ +[FMallocBinned2] +Free=0x30 +GetAllocSize=0x40 +Malloc=0x10 +Realloc=0x20 +QuantizeSize=0x38 + +[UObject] +ProcessEvent=0x220 + +[UScriptStruct] +GetCustomGuid=0x330 + +[UPackage] +GamePackage=/Script/xrd777 + +[UStruct] +Link=0x288 \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/scans.ini index d688a28..2614491 100644 --- a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/scans.ini +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/scans.ini @@ -48,8 +48,11 @@ GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? ;StaticConstructObject_Internal_RESULT= -GetStaticStruct=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 49 8B E8 48 8B DA FF D1 48 8B F0 90 48 83 7B ?? 00 48 8D 7B ?? 74 ?? 8B 43 ?? 33 C9 0F BA E0 1C 73 ?? 48 8B CB E8 ?? ?? ?? ?? 48 8B C8 48 8B D9 48 85 DB 75 ?? 48 8B 1F EB ?? 48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 -;GetStaticStruct_RESULT= +GetStaticStruct=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 78 + +GetStaticEnum=48 8B 43 ?? 48 8D 54 24 ?? 48 8D 4C 24 ?? 48 89 44 24 ?? E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D5 +GetStaticEnum_RESULT=result - 78 ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC 98 03 00 00 ;ConstructUScriptStruct diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/scans.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/scans.ini new file mode 100644 index 0000000..5f394c7 --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/scans.ini @@ -0,0 +1,71 @@ +;ROMEO IS A DEAD MAN (5.6) +;Tested by https://github.com/raycopper + +[Scans] +;DebugDumpInternal +GFNamePool=48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? C6 05 ?? ?? ?? ?? 01 48 8D 54 24 ?? +GFNamePool_RESULT=GetGlobalAddress(result + 3) + +;FNameHelper_FindOrStoreString=48 89 74 24 ?? 57 48 83 EC 60 81 79 ?? 00 04 00 00 +;FNameHelper_FindOrStoreString_RESULT= + +;??0FName@@QEAA@PEB_WW4EFindName@@@Z +FName_Ctor_Wide=48 89 5C 24 ?? 57 48 83 EC 30 48 8B D9 41 8B F8 +;FName_Ctor_Wide_RESULT= + +;LaunchFixGameNameCase +FText_FromString=E8 ?? ?? ?? ?? 83 7C 24 ?? 00 48 8D 4D ?? 49 8B D6 +FText_FromString_RESULT=GetGlobalAddress(result + 1) + +FText_ToString=E8 ?? ?? ?? ?? 83 78 ?? 00 74 ?? 4C 8B 30 4D 8B CE +FText_ToString_RESULT=GetGlobalAddress(result + 1) + +UObject_PostLoadSubobjects=40 55 53 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC 10 02 00 00 +;UObject_PostLoadSubobjects_RESULT= + +UObject_BeginDestroy=40 53 48 83 EC 30 8B 41 ?? 48 8B D9 C1 E8 0F +;UObject_BeginDestroy_RESULT= + +;From UObjectBaseInit +GUObjectArray=48 8D 0D ?? ?? ?? ?? 8B 54 24 ?? E8 ?? ?? ?? ?? E8 ?? ?? ?? ?? +GUObjectArray_RESULT=GetGlobalAddress(result + 3) + +UStruct_IsChildOf=48 85 D2 74 ?? 48 63 42 ?? 4C 8D 42 +;UStruct_IsChildOf_RESULT= + +UUserDefinedEnum_GetDisplayNameTextByIndex=48 89 5C 24 ?? 55 56 57 48 83 EC 30 48 8B FA +;UUserDefinedEnum_GetDisplayNameTextByIndex_RESULT= + +UDataTable_HandleDataTableChanged=48 89 54 24 ?? 55 53 56 48 8D 6C 24 ?? 48 81 EC D0 00 00 00 +;UDataTable_HandleDataTableChanged_RESULT= + +GMalloc=48 8B 0D ?? ?? ?? ?? 48 85 C9 75 ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 48 8B D3 FF 50 ?? 48 83 C4 20 +GMalloc_RESULT=GetGlobalAddress(result + 3) + +FTextInspector_GetTableIdAndKey=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 48 8B D9 + +GetPrivateStaticClassBody_UE4=DISABLED +;GetPrivateStaticClassBody_RESULT= + +GetPrivateStaticClassBody_UE5=48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 30 49 8B E9 49 8B D8 +;GetPrivateStaticClassBody_RESULT= + +StaticConstructObject_Internal=4C 8B DC 49 89 5B ?? 49 89 73 ?? 55 57 41 54 41 56 41 57 49 8D AB ?? ?? ?? ?? +;StaticConstructObject_Internal_RESULT= + +GetStaticStruct=83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 45 8B C1 +GetStaticStruct_RESULT=result - 58 + +GetStaticEnum=83 7C 24 ?? 00 48 8D 0D ?? ?? ?? ?? 41 B9 02 00 00 00 48 89 74 24 ?? 48 0F 45 4C 24 ?? 48 8B D7 +GetStaticEnum_RESULT=result - 58 + +ConstructUScriptStruct=40 53 56 57 41 54 41 55 41 56 48 81 EC B8 03 00 00 +;ConstructUScriptStruct + +;FEngineLoop::Init +GEngine=48 89 05 ?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ?? 48 8D 4D ?? E8 ?? ?? ?? ?? 0F 28 D6 +GEngine_RESULT=GetGlobalAddress(result + 3) + +;ConvertInstanceToActor +UWorld_SpawnActor=E8 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B F8 48 8D 0D ?? ?? ?? ?? +UWorld_SpawnActor_RESULT=GetGlobalAddress(result + 1) \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/unreal.ini b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/unreal.ini new file mode 100644 index 0000000..2e8fe44 --- /dev/null +++ b/UE.Toolkit.Reloaded/Project/UE.Toolkit.Reloaded/sevgame-win64-shipping/unreal.ini @@ -0,0 +1,20 @@ +;Based on Third Person Template for UE 5.6 + +[FMallocBinned2] +Free=0x48 +GetAllocSize=0x68 +Malloc=0x28 +Realloc=0x38 +QuantizeSize=0x60 + +[UObject] +ProcessEvent=0x268 + +[UScriptStruct] +GetCustomGuid=0x378 + +[UPackage] +GamePackage=/Script/SevGame + +[UStruct] +Link=0x2c8 \ No newline at end of file diff --git a/UE.Toolkit.Reloaded/Toolkit/ToolkitApi.cs b/UE.Toolkit.Reloaded/Toolkit/ToolkitApi.cs index f3adb6b..c37d564 100644 --- a/UE.Toolkit.Reloaded/Toolkit/ToolkitApi.cs +++ b/UE.Toolkit.Reloaded/Toolkit/ToolkitApi.cs @@ -1,10 +1,13 @@ using UE.Toolkit.Interfaces; using UE.Toolkit.Reloaded.ObjectWriters; +using UnrealEssentials.Interfaces; namespace UE.Toolkit.Reloaded.Toolkit; -public class ToolkitApi(ObjectWriterService objWriters) : IToolkit +public class ToolkitApi(ObjectWriterService objWriters, IUnrealEssentials essentials) : IToolkit { + private IUnrealEssentials Essentials => essentials; + public void AddToolkitFolder(string folder) { var objsDir = Path.Join(folder, "objects"); diff --git a/UE.Toolkit.Reloaded/UE.Toolkit.Reloaded.csproj b/UE.Toolkit.Reloaded/UE.Toolkit.Reloaded.csproj index 968c12d..78d30f8 100644 --- a/UE.Toolkit.Reloaded/UE.Toolkit.Reloaded.csproj +++ b/UE.Toolkit.Reloaded/UE.Toolkit.Reloaded.csproj @@ -1,10 +1,10 @@  - net8.0-windows + net9.0 false true - 12.0 + 13.0 enable True $(RELOADEDIIMODS)/UE.Toolkit.Reloaded @@ -46,11 +46,13 @@ + + diff --git a/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs b/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs index d6cd6f6..a14486e 100644 --- a/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs +++ b/UE.Toolkit.Reloaded/Unreal/UnrealClasses.cs @@ -5,6 +5,7 @@ using Reloaded.Hooks.Definitions; using UE.Toolkit.Core.Types; using UE.Toolkit.Core.Types.Interfaces; +using UE.Toolkit.Core.Types.Unreal.Common; using UE.Toolkit.Core.Types.Unreal.Factories; using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces; using UE.Toolkit.Core.Types.Unreal.UE5_4_4; @@ -218,7 +219,7 @@ private nint GetStaticStructImpl(nint pInRegister, nint pStructOuter, nint pStru PackageNameToUObject.TryAdd(PackageName, StructOuter); // Log.Debug($"GetStaticStruct({StructName}): (outer: {StructOuter.NamePrivate}, size: 0x{Size:x}, crc: 0x{Crc:x})"); */ - return _GetStaticStruct!.Hook!.OriginalFunction(pInRegister, pStructOuter, pStructName, Size, Crc); + return _GetStaticStruct!.Hook!.OriginalFunction(pInRegister, pStructOuter, pStructName, Size, Crc); } [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] @@ -265,6 +266,24 @@ public bool GetScriptStructInfoFromType(out IUScriptStruct? Value) wher public bool GetScriptStructInfoFromName(string Name, out IUScriptStruct? Value) => ScriptStructs.TryGetValue(Name[1..], out Value); + public bool GetEnumInfoFromType(out IUEnum? Value) where TObject : unmanaged + => GetEnumInfoFromName(typeof(TObject).Name, out Value); + + private delegate nint GetStaticEnum(nint pInRegister, nint pEnumOuter, nint pEnumName, nint Size, int Crc); + private SHFunction _GetStaticEnum; + private nint GetStaticEnumImpl(nint pInRegister, nint pEnumOuter, nint pEnumName, nint Size, int Crc) + { + var pEnum = _GetStaticEnum!.Hook!.OriginalFunction(pInRegister, pEnumOuter, pEnumName, Size, Crc); + var Enum = Factory.CreateUEnum(pEnum); + Enums.TryAdd(Enum.NamePrivate.ToString(), Enum); + return pEnum; + } + + public bool GetEnumInfoFromName(string Name, out IUEnum? Value) + => Enums.TryGetValue(Name, out Value); + + // START ADD PROPERTIES + public bool AddI8Property(string Name, int Offset, out IFProperty? Property) where TObject : unmanaged { @@ -450,10 +469,98 @@ public bool GetFieldClassGlobal(FName Name, out FieldClassGlobal FieldClass) => FieldTypes.TryGetValue(Name.ComparisonIndex.Value, out FieldClass); #endregion + + #region ITypeReflection implementation + + public Type GetFText() => GameConfig.Instance.GetFText(); + public int GetFTextSize() => GameConfig.Instance.GetFTextSize(); + public ISoftObjectPath IntoSoftObjectPath(nint ptr) => GameConfig.Instance.IntoSoftObjectPath(ptr); + + public string GetPropertyTypeName(IFProperty prop) + { + var className = prop.ClassPrivate.Name; + switch (className) + { + case "BoolProperty": + return "bool"; + case "ByteProperty": + var byteProp = Factory.Cast(prop); + return byteProp.Enum?.NamePrivate.ToString() ?? "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 "ObjectProperty": + return $"{Factory.Cast(prop).PropertyClass.NamePrivate}*"; + case "SoftObjectProperty": + return $"TSoftObjectPtr<{Factory.Cast(prop).PropertyClass.NamePrivate}>"; + case "SoftClassProperty": + return $"TSoftClassPtr<{Factory.Cast(prop).MetaClass.NamePrivate}>"; + case "StructProperty": + return Factory.Cast(prop).Struct.NamePrivate.ToString(); + case "ClassProperty": + case "ClassPtrProperty": + return Factory.Cast(prop).MetaClass!.NamePrivate.ToString(); + case "EnumProperty": + return Factory.Cast(prop).Enum.NamePrivate.ToString(); + case "MapProperty": + var mapProp = Factory.Cast(prop); + var mapPropKeyType = GetPropertyTypeName(mapProp.KeyProp); + var mapPropValueType = GetPropertyTypeName(mapProp.ValueProp); + return $"TMap<{mapPropKeyType}, {mapPropValueType}>"; + case "InterfaceProperty": + return $"TScriptInterface<{Factory.Cast(prop).NamePrivate}>"; + case "ArrayProperty": + return $"TArray<{GetPropertyTypeName(Factory.Cast(prop).Inner)}>"; + case "SetProperty": + return $"TSet<{GetPropertyTypeName(Factory.Cast(prop).ElementProp)}>"; + case "DelegateProperty": + return "FScriptDelegate"; + case "MulticastInlineDelegateProperty": + case "MulticastSparseDelegateProperty": + return "FMulticastScriptDelegate"; + case "WeakObjectProperty": + return "FWeakObjectPtr"; + case "FieldPathProperty": + return "FFieldPath"; + case "Utf8StrProperty": + return "FUtf8String"; + case "AnsiStrProperty": + return "FAnsiString"; + default: + Log.Warning($"Unknown Property: {className}"); + return className; + } + } + + #endregion private ConcurrentDictionary ClassExtensions = new(); private ConcurrentDictionary Classes = new(); private ConcurrentDictionary ScriptStructs = new(); + private ConcurrentDictionary Enums = new(); private ConcurrentDictionary FieldTypes = new(); private ConcurrentDictionary DataTableRowToVTable = new(); @@ -490,6 +597,7 @@ public UnrealClasses(IUnrealFactory _Factory, IUnrealMemory _Memory, IUnrealObje _GetPrivateStaticClassBodyUE4 = new(GetPrivateStaticClassBodyUE4); _GetPrivateStaticClassBodyUE5 = new(GetPrivateStaticClassBodyUE5); _GetStaticStruct = new(GetStaticStructImpl); + _GetStaticEnum = new(GetStaticEnumImpl); _ConstructUScriptStruct = new(ConstructUScriptStructImpl); // Store the vtable for each DataTable's row type. Since structs don't have default objects like classes, we