Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@

Object XML requires game-specific support with an extension mod. Supported games [are listed here](#installing-the-ue-toolkit-extension-mod).

| Feature | UE 4.27.2 | UE 5.4.4 |
| - | - | - |
| Object Logging | ✅|
| Object Editing | ✅| ✅
| `FMemory` Functions | ✅|
| Dumper | ✅| ✅
| Property Editing (Object XML) | ✅| ✅
| Add List Entry (Object XML) | ✅|
| Add Map Entry (Object XML) | ✅|
| Type Information | ✅|
| Custom Constructor | ✅| ❔
| Add Properties | ✅| ❌
| Register Struct | ✅| ❌
| Feature | 4.27 | 5.0 | 5.1 | 5.2 | 5.3 | 5.4 | 5.5 | 5.6 | 5.7
| - | - | - | - | - | - | - | - | - | - |
| Object Logging |✅|✅|✅|✅|✅|✅|✅|✅|
| Object Editing |✅|❔|❔|❔|❔|✅|❔|❔|❔
| `FMemory` Functions |✅|✅|✅|✅|✅|✅|✅|✅|
| Dumper |✅|✅|✅|✅|️️️️️️✅|✅|✅|✅️|️️️️✅️
| Property Editing (Object XML) |✅|❔|❔|❔|❔|✅|❔|❔|❔
| Add List Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|
| Add Map Entry (Object XML) |✅|❔|❔|❔|❔|❔|❔|❔|
| Type Information |✅|✅|✅|✅|✅|✅|✅|✅|
| Custom Constructor |✅|✅|✅|✅|✅|✅|✅|✅|✅
| Add Properties |✅|✅|✅|✅|✅|✅|✅|✅|✅
| Register Struct |✅|✅|✅|✅|✅|✅|✅|✅|✅

Features marked with ❔ are currently untested.

Expand Down Expand Up @@ -90,9 +90,11 @@ You can easily get both using [__FModel__](https://github.com/4sval/FModel/relea
#### Steps
1. Inside your chosen folder from the previous steps, create a new __text file__.
2. Name the file the same as the object's __Name__.
3. Change the file extension from `.txt` to `.obj.xml`. Your file's name should be similar to: `DT_jRPG_CharacterDefinitions.obj.xml`
3. Change the file extension from `.txt` to `.obj.xml`. For example, if the file you want to edit is named `DT_jRPG_CharacterDefinitions.uasset` in FModel, then your Object XML would be named `DT_jRPG_CharacterDefinitions.obj.xml`.
4. Open your file in a text editor.

While this is suitable for most cases, there are certain situations where there may be multiple files with the same filename but you only want to edit a file with a particular **file path**. In this case, you can name your Object XML to anything you want and specify the path inside the file, which is covered below.

### Writing an Object XML
Generally, your XML will match the "shape" of the object, starting with its __Class__. I highly recommend looking at the object in __FModel__ or going to the extension mod's source if you can read code.

Expand All @@ -107,6 +109,14 @@ The first element in your XML, aka the __Root Element__, will be the object's __
</DataLayerAsset>
```

To specify a file path, add a `path` property to the root element, using the same path formatting style used by UE4SS. For example, if your object's file path is `/Sandfall/Content/Levels/Goblu/DataLayers/DL_Goblu_00Entry`, then you would replace `/Sandfall/Content` with `/Game` and add a "file extension" with the same name as the filename:

```xml
<DataLayerAsset path="/Game/Levels/Goblu/DataLayers/DL_Goblu_00Entry.DL_Goblu_00Entry">

</DataLayerAsset>
```

#### Editing Properties
A `DataLayerAsset` has two properties: `DataLayerType` and `DebugColor`. We'll first edit `DataLayerType`.

Expand Down Expand Up @@ -274,14 +284,14 @@ public unsafe struct UFldManagerSubsystem
A new struct can be registered into UE's type reflection. `IUnrealClasses` contains methods `(Create[Type]Param)` to build a list of property params to pass into `CreateScriptStruct`:

```c#
TryCreateScriptStruct("AgePanelSection", 0x30, new List<IFPropertyParams>
CreateScriptStruct("AgePanelSection", 0x30, new List<IFPropertyParams>
{
_context._toolkitClasses.CreateF32Param("X1", 0),
_context._toolkitClasses.CreateF32Param("X2", 4),
_context._toolkitClasses.CreateF32Param("Y1", 8),
_context._toolkitClasses.CreateF32Param("Y2", 0xc),
_context._toolkitClasses.CreateF32Param("Field28", 0x28),
});
}, out var NewStruct);
```

Much like adding new fields, this is viewable in Object XML and blueprints.
Expand Down
44 changes: 21 additions & 23 deletions UE.Toolkit.Core/Common/ToolkitUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,30 @@ public static string GetNativeName(nint objPtr)
return nameNative;
}

public static string GetNativeName(IUObject uobj)
public static string GetNativeName(IUObject uobj) => GetNativeName(uobj.Ptr);

/// <summary>
/// Returns the fully qualified pathname for this object, in the format "Outermost.[Outer:]Name"
/// </summary>
/// <param name="objPtr">Pointer to the object</param>
/// <returns>Fully qualified pathname for this object</returns>
public static string GetPathName(nint objPtr)
{
if (PrivateToNativeNameMap.TryGetValue(uobj.NamePrivate, out var nameNative)) return nameNative;

var namePrivate = uobj.NamePrivate.ToString();
nameNative = namePrivate;
// From UObjectBaseUtility::GetPathName
var Object = (UObjectBase*)objPtr;
if (Object == null) return "None";

if (uobj.IsChildOf<UClass>())
{
nameNative = $"U{namePrivate}";
}
if (uobj.IsChildOf<AActor>())
{
nameNative = $"A{namePrivate}";
}
if (uobj.IsChildOf<UScriptStruct>())
var Outer = Object->OuterPrivate;
var Result = Object->NamePrivate.ToString();
var OuterResult = string.Empty;
if (Outer != null)
{
// Already has the F struct prefix, UserDefinedStructs usually I think.
var hasStructPrefix = namePrivate[0] == 'F' && char.IsUpper(namePrivate[1]);
if (!hasStructPrefix)
{
nameNative = $"F{namePrivate}";
}
var bIsOuterPackage = ((UObjectBase*)Outer->ClassPrivate)->NamePrivate.ToString() != "Package"
&& ((UObjectBase*)Outer->OuterPrivate->ClassPrivate)->NamePrivate.ToString() == "Package";
OuterResult = GetPathName((nint)Outer) + (bIsOuterPackage ? ":" : ".");
}

PrivateToNativeNameMap[uobj.NamePrivate] = nameNative;
return nameNative;
return OuterResult + Result;
}

public static string GetPathName(IUObject uobj) => GetPathName(uobj.Ptr);
}
2 changes: 2 additions & 0 deletions UE.Toolkit.Core/Types/Unreal/Factories/Interfaces/IUObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,7 @@ public interface IUObject : IPtr

string GetNativeName();

string GetPathName();

// IUObject GetWorld();
}
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@
public IUObject GetOutermost() => _factory.CreateUObject((nint)_self->GetOutermost());

public string GetNativeName() => ToolkitUtils.GetNativeName(this);

public string GetPathName() => ToolkitUtils.GetPathName(this);
}

public unsafe class UClassUE4_27_2(nint ptr, IUnrealFactory factory)
Expand Down Expand Up @@ -532,8 +534,7 @@
public IUObject? IndexToObject(int idx)
{
var objItem = _self->ObjObjects.GetItem(idx);
if (objItem == null || objItem->Object == null) return null;

if (objItem == null || objItem->Object == null || objItem->Flags.HasFlag(EInternalObjectFlags.Unreachable)) return null;
return factory.CreateUObject((nint)objItem->Object);
}

Expand Down Expand Up @@ -730,7 +731,7 @@
public FActorSpawnParametersUE4_27_2(IUnrealFactory factory)
{
_factory = factory;
_self = (FActorSpawnParameters*)_factory.Memory.MallocZeroed(sizeof(FActorSpawnParameters));

Check warning on line 734 in UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}

public void SetParams(EObjectFlags Flags)
Expand All @@ -749,7 +750,7 @@
protected virtual void Disposing()
{
if (Disposed) return;
_factory.Memory.Free(Ptr);

Check warning on line 753 in UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
Disposed = true;
}

Expand All @@ -759,7 +760,7 @@
}

public unsafe class UGameInstanceUE4_27_2(nint ptr, IUnrealFactory factory)
: UObjectUE4_27_2(ptr, factory), IUGameInstance

Check warning on line 763 in UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IUnrealFactory factory' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.
{
private readonly UGameInstance* _self = (UGameInstance*)ptr;

Expand All @@ -767,7 +768,7 @@
{
var Subsystems = new TMapDictionary<HashablePtr<UClass>, HashablePtr<UObjectBase>>(
(TMap<HashablePtr<UClass>, HashablePtr<UObjectBase>>*)(&_self->SubsystemCollection.Subsystems),
factory.Memory);

Check warning on line 771 in UE.Toolkit.Core/Types/Unreal/Factories/UE4_27_2/UnrealFactory.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter '_Allocator' in 'TMapDictionary<HashablePtr<UClass>, HashablePtr<UObjectBase>>.TMapDictionary(TMap<HashablePtr<UClass>, HashablePtr<UObjectBase>>* _Self, IUnrealMemoryInternal _Allocator, Action<string>? _DebugCallback = null)'.
Subsystem = Subsystems.TryGetValue(new HashablePtr<UClass>(new Ptr<UClass>((UClass*)Class.Ptr)),
out var pSubsystem)
? _factory.CreateUObject((nint)pSubsystem.Value->Ptr.Value)
Expand Down
176 changes: 176 additions & 0 deletions UE.Toolkit.Core/Types/Unreal/Factories/UE5_0_3/UnrealFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using UE.Toolkit.Core.Types.Unreal.Factories.Interfaces;

namespace UE.Toolkit.Core.Types.Unreal.Factories.UE5_0_3;

public class UnrealFactory : BaseUnrealFactory
{
public override IntPtr SizeOf<T>()
{
throw new NotImplementedException();
}

public override IFProperty CreateFProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFBoolProperty CreateFBoolProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFByteProperty CreateFByteProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFEnumProperty CreateFEnumProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFObjectProperty CreateFObjectProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFSoftClassProperty CreateFSoftClassProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFClassProperty CreateFClassProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFStructProperty CreateFStructProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFMapProperty CreateFMapProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFInterfaceProperty CreateFInterfaceProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFArrayProperty CreateFArrayProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFSetProperty CreateFSetProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFOptionalProperty CreateFOptionalProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFDelegateProperty CreateFDelegateProperty(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUObjectArray CreateUObjectArray(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUObject CreateUObject(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUClass CreateUClass(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUScriptStruct CreateUScriptStruct(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUEnum CreateUEnum(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUField CreateUField(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUStruct CreateUStruct(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUUserDefinedEnum CreateUUserDefinedEnum(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUFunction CreateUFunction(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFFieldClass CreateFFieldClass(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFField CreateFField(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFStructParams CreateFStructParams(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFPropertyParams CreateFPropertyParams(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFGenericPropertyParams CreateFGenericPropertyParams(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFWorldContext CreateFWorldContext(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUEngine CreateUEngine(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IUGameInstance CreateUGameInstance(IntPtr ptr)
{
throw new NotImplementedException();
}

public override IFStaticConstructObjectParameters CreateFStaticConstructObjectParameters()
{
throw new NotImplementedException();
}

public override IFActorSpawnParameters CreateFActorSpawnParameters()
{
throw new NotImplementedException();
}
}
Loading
Loading