mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Update animation hooks.
This commit is contained in:
parent
68135f3757
commit
4f0f3721a6
11 changed files with 83 additions and 61 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
|
|
@ -18,26 +19,26 @@ public sealed unsafe class Dismount : FastHook<Dismount.Delegate>
|
|||
Task = hooks.CreateHook<Delegate>("Dismount", Sigs.Dismount, Detour, HookSettings.VfxIdentificationHooks);
|
||||
}
|
||||
|
||||
public delegate void Delegate(nint a1, nint a2);
|
||||
public delegate void Delegate(MountContainer* a1, nint a2);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private void Detour(nint a1, nint a2)
|
||||
private void Detour(MountContainer* a1, nint a2)
|
||||
{
|
||||
Penumbra.Log.Excessive($"[Dismount] Invoked on {a1:X} with {a2:X}.");
|
||||
if (a1 == nint.Zero)
|
||||
Penumbra.Log.Excessive($"[Dismount] Invoked on 0x{(nint)a1:X} with {a2:X}.");
|
||||
if (a1 == null)
|
||||
{
|
||||
Task.Result.Original(a1, a2);
|
||||
return;
|
||||
}
|
||||
|
||||
var gameObject = *(GameObject**)(a1 + 8);
|
||||
var gameObject = a1->OwnerObject;
|
||||
if (gameObject == null)
|
||||
{
|
||||
Task.Result.Original(a1, a2);
|
||||
return;
|
||||
}
|
||||
|
||||
var last = _state.SetAnimationData(_collectionResolver.IdentifyCollection(gameObject, true));
|
||||
var last = _state.SetAnimationData(_collectionResolver.IdentifyCollection((GameObject*) gameObject, true));
|
||||
Task.Result.Original(a1, a2);
|
||||
_state.RestoreAnimationData(last);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ public sealed unsafe class LoadAreaVfx : FastHook<LoadAreaVfx.Delegate>
|
|||
{
|
||||
var newData = caster != null
|
||||
? _collectionResolver.IdentifyCollection(caster, true)
|
||||
: ResolveData.Invalid;
|
||||
: ResolveData.Invalid;
|
||||
|
||||
var last = _state.SetAnimationData(newData);
|
||||
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.LoadAreaVfx);
|
||||
var ret = Task.Result.Original(vfxId, pos, caster, unk1, unk2, unk3);
|
||||
Penumbra.Log.Excessive(
|
||||
var ret = Task.Result.Original(vfxId, pos, caster, unk1, unk2, unk3);
|
||||
Penumbra.Log.Information(
|
||||
$"[Load Area VFX] Invoked with {vfxId}, [{pos[0]} {pos[1]} {pos[2]}], 0x{(nint)caster:X}, {unk1}, {unk2}, {unk3} -> 0x{ret:X}.");
|
||||
_state.RestoreAnimationData(last);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -16,23 +16,25 @@ public sealed unsafe class LoadCharacterSound : FastHook<LoadCharacterSound.Dele
|
|||
|
||||
public LoadCharacterSound(HookManager hooks, GameState state, CollectionResolver collectionResolver, CrashHandlerService crashHandler)
|
||||
{
|
||||
_state = state;
|
||||
_state = state;
|
||||
_collectionResolver = collectionResolver;
|
||||
_crashHandler = crashHandler;
|
||||
Task = hooks.CreateHook<Delegate>("Load Character Sound", (nint)VfxContainer.MemberFunctionPointers.LoadCharacterSound, Detour, HookSettings.VfxIdentificationHooks);
|
||||
_crashHandler = crashHandler;
|
||||
Task = hooks.CreateHook<Delegate>("Load Character Sound", (nint)VfxContainer.MemberFunctionPointers.LoadCharacterSound, Detour,
|
||||
HookSettings.VfxIdentificationHooks);
|
||||
}
|
||||
|
||||
public delegate nint Delegate(nint container, int unk1, int unk2, nint unk3, ulong unk4, int unk5, int unk6, ulong unk7);
|
||||
public delegate nint Delegate(VfxContainer* container, int unk1, int unk2, nint unk3, ulong unk4, int unk5, int unk6, ulong unk7);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private nint Detour(nint container, int unk1, int unk2, nint unk3, ulong unk4, int unk5, int unk6, ulong unk7)
|
||||
private nint Detour(VfxContainer* container, int unk1, int unk2, nint unk3, ulong unk4, int unk5, int unk6, ulong unk7)
|
||||
{
|
||||
var character = *(GameObject**)(container + 8);
|
||||
var character = (GameObject*)container->OwnerObject;
|
||||
var newData = _collectionResolver.IdentifyCollection(character, true);
|
||||
var last = _state.SetSoundData(newData);
|
||||
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.LoadCharacterSound);
|
||||
var ret = Task.Result.Original(container, unk1, unk2, unk3, unk4, unk5, unk6, unk7);
|
||||
Penumbra.Log.Excessive($"[Load Character Sound] Invoked with {container:X} {unk1} {unk2} {unk3} {unk4} {unk5} {unk6} {unk7} -> {ret}.");
|
||||
Penumbra.Log.Excessive(
|
||||
$"[Load Character Sound] Invoked with {(nint)container:X} {unk1} {unk2} {unk3} {unk4} {unk5} {unk6} {unk7} -> {ret}.");
|
||||
_state.RestoreSoundData(last);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Base;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData;
|
||||
|
|
@ -25,20 +26,20 @@ public sealed unsafe class LoadTimelineResources : FastHook<LoadTimelineResource
|
|||
public LoadTimelineResources(HookManager hooks, GameState state, CollectionResolver collectionResolver, ICondition conditions,
|
||||
ObjectManager objects, CrashHandlerService crashHandler)
|
||||
{
|
||||
_state = state;
|
||||
_state = state;
|
||||
_collectionResolver = collectionResolver;
|
||||
_conditions = conditions;
|
||||
_objects = objects;
|
||||
_crashHandler = crashHandler;
|
||||
Task = hooks.CreateHook<Delegate>("Load Timeline Resources", Sigs.LoadTimelineResources, Detour, HookSettings.VfxIdentificationHooks);
|
||||
_conditions = conditions;
|
||||
_objects = objects;
|
||||
_crashHandler = crashHandler;
|
||||
Task = hooks.CreateHook<Delegate>("Load Timeline Resources", Sigs.LoadTimelineResources, Detour, HookSettings.VfxIdentificationHooks);
|
||||
}
|
||||
|
||||
public delegate ulong Delegate(nint timeline);
|
||||
public delegate ulong Delegate(SchedulerTimeline* timeline);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private ulong Detour(nint timeline)
|
||||
private ulong Detour(SchedulerTimeline* timeline)
|
||||
{
|
||||
Penumbra.Log.Excessive($"[Load Timeline Resources] Invoked on {timeline:X}.");
|
||||
Penumbra.Log.Excessive($"[Load Timeline Resources] Invoked on {(nint)timeline:X}.");
|
||||
// Do not check timeline loading in cutscenes.
|
||||
if (_conditions[ConditionFlag.OccupiedInCutSceneEvent] || _conditions[ConditionFlag.WatchingCutscene78])
|
||||
return Task.Result.Original(timeline);
|
||||
|
|
@ -50,20 +51,19 @@ public sealed unsafe class LoadTimelineResources : FastHook<LoadTimelineResource
|
|||
// This is called far too often and spams the log too much.
|
||||
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.LoadTimelineResources);
|
||||
#endif
|
||||
var ret = Task.Result.Original(timeline);
|
||||
var ret = Task.Result.Original(timeline);
|
||||
_state.RestoreAnimationData(last);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary> Use timelines vfuncs to obtain the associated game object. </summary>
|
||||
public static ResolveData GetDataFromTimeline(ObjectManager objects, CollectionResolver resolver, nint timeline)
|
||||
public static ResolveData GetDataFromTimeline(ObjectManager objects, CollectionResolver resolver, SchedulerTimeline* timeline)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (timeline != nint.Zero)
|
||||
if (timeline != null)
|
||||
{
|
||||
var getGameObjectIdx = ((delegate* unmanaged<nint, int>**)timeline)[0][Offsets.GetGameObjectIdxVfunc];
|
||||
var idx = getGameObjectIdx(timeline);
|
||||
var idx = timeline->GetOwningGameObjectIndex();
|
||||
if (idx >= 0 && idx < objects.TotalCount)
|
||||
{
|
||||
var obj = objects[idx];
|
||||
|
|
@ -73,7 +73,7 @@ public sealed unsafe class LoadTimelineResources : FastHook<LoadTimelineResource
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Penumbra.Log.Error($"Error getting timeline data for 0x{timeline:X}:\n{e}");
|
||||
Penumbra.Log.Error($"Error getting timeline data for 0x{(nint)timeline:X}:\n{e}");
|
||||
}
|
||||
|
||||
return ResolveData.Invalid;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ public static class HookSettings
|
|||
public const bool ResourceHooks = true && AllHooks;
|
||||
public const bool MetaEntryHooks = true && AllHooks;
|
||||
public const bool MetaParentHooks = true && AllHooks;
|
||||
public const bool VfxIdentificationHooks = false && AllHooks;
|
||||
public const bool VfxIdentificationHooks = true && AllHooks;
|
||||
public const bool PostProcessingHooks = true && AllHooks;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,21 +15,21 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
{
|
||||
private readonly ResourceService _resources;
|
||||
private readonly FileReadService _fileReadService;
|
||||
private readonly TexMdlService _texMdlService;
|
||||
private readonly TexMdlService _texMdlService;
|
||||
|
||||
private ResolveData _resolvedData = ResolveData.Invalid;
|
||||
|
||||
public ResourceLoader(ResourceService resources, FileReadService fileReadService, TexMdlService texMdlService)
|
||||
{
|
||||
_resources = resources;
|
||||
_resources = resources;
|
||||
_fileReadService = fileReadService;
|
||||
_texMdlService = texMdlService;
|
||||
_texMdlService = texMdlService;
|
||||
ResetResolvePath();
|
||||
|
||||
_resources.ResourceRequested += ResourceHandler;
|
||||
_resources.ResourceRequested += ResourceHandler;
|
||||
_resources.ResourceHandleIncRef += IncRefProtection;
|
||||
_resources.ResourceHandleDecRef += DecRefProtection;
|
||||
_fileReadService.ReadSqPack += ReadSqPackDetour;
|
||||
_fileReadService.ReadSqPack += ReadSqPackDetour;
|
||||
}
|
||||
|
||||
/// <summary> Load a resource for a given path and a specific collection. </summary>
|
||||
|
|
@ -80,10 +80,10 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
_resources.ResourceRequested -= ResourceHandler;
|
||||
_resources.ResourceRequested -= ResourceHandler;
|
||||
_resources.ResourceHandleIncRef -= IncRefProtection;
|
||||
_resources.ResourceHandleDecRef -= DecRefProtection;
|
||||
_fileReadService.ReadSqPack -= ReadSqPackDetour;
|
||||
_fileReadService.ReadSqPack -= ReadSqPackDetour;
|
||||
}
|
||||
|
||||
private void ResourceHandler(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path,
|
||||
|
|
@ -94,6 +94,9 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
|
||||
CompareHash(ComputeHash(path.Path, parameters), hash, path);
|
||||
|
||||
if (path.ToString() == "vfx/common/eff/abi_cnj022g.avfx")
|
||||
;
|
||||
|
||||
// If no replacements are being made, we still want to be able to trigger the event.
|
||||
var (resolvedPath, data) = _incMode.Value
|
||||
? (null, ResolveData.Invalid)
|
||||
|
|
@ -112,7 +115,7 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
// Replace the hash and path with the correct one for the replacement.
|
||||
hash = ComputeHash(resolvedPath.Value.InternalName, parameters);
|
||||
var oldPath = path;
|
||||
path = p;
|
||||
path = p;
|
||||
returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, parameters);
|
||||
ResourceLoaded?.Invoke(returnValue, oldPath, resolvedPath.Value, data);
|
||||
}
|
||||
|
|
@ -121,7 +124,8 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
{
|
||||
if (fileDescriptor->ResourceHandle == null)
|
||||
{
|
||||
Penumbra.Log.Error("[ResourceLoader] Failure to load file from SqPack: invalid File Descriptor.");
|
||||
Penumbra.Log.Verbose(
|
||||
$"[ResourceLoader] Failure to load file from SqPack: invalid File Descriptor: {Marshal.PtrToStringUni((nint)(&fileDescriptor->Utf16FileName))}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -140,12 +144,12 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
}
|
||||
|
||||
var path = ByteString.FromSpanUnsafe(actualPath, gamePath.Path.IsNullTerminated, gamePath.Path.IsAsciiLowerCase, gamePath.Path.IsAscii);
|
||||
fileDescriptor->ResourceHandle->FileNameData = path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameData = path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = path.Length;
|
||||
MtrlForceSync(fileDescriptor, ref isSync);
|
||||
returnValue = DefaultLoadResource(path, fileDescriptor, priority, isSync, data);
|
||||
// Return original resource handle path so that they can be loaded separately.
|
||||
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameData = gamePath.Path.Path;
|
||||
fileDescriptor->ResourceHandle->FileNameLength = gamePath.Path.Length;
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +169,7 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
// Ensure that the file descriptor has its wchar_t array on aligned boundary even if it has to be odd.
|
||||
var fd = stackalloc char[0x11 + 0x0B + 14];
|
||||
fileDescriptor->FileDescriptor = (byte*)fd + 1;
|
||||
CreateFileWHook.WritePtr(fd + 0x11, gamePath.Path, gamePath.Length);
|
||||
CreateFileWHook.WritePtr(fd + 0x11, gamePath.Path, gamePath.Length);
|
||||
CreateFileWHook.WritePtr(&fileDescriptor->Utf16FileName, gamePath.Path, gamePath.Length);
|
||||
|
||||
// Use the SE ReadFile function.
|
||||
|
|
@ -206,7 +210,7 @@ public unsafe class ResourceLoader : IDisposable, IService
|
|||
return;
|
||||
|
||||
_incMode.Value = true;
|
||||
returnValue = _resources.IncRef(handle);
|
||||
returnValue = _resources.IncRef(handle);
|
||||
_incMode.Value = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,11 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
|
|||
|
||||
private readonly ThreadLocal<bool> _texReturnData = new(() => default);
|
||||
|
||||
private delegate void UpdateCategoryDelegate(TextureResourceHandle* resourceHandle);
|
||||
|
||||
[Signature(Sigs.TexHandleUpdateCategory)]
|
||||
private readonly UpdateCategoryDelegate _updateCategory = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The function that checks a files CRC64 to determine whether it is 'protected'.
|
||||
/// We use it to check against our stored CRC64s and if it corresponds, we return the custom flag for models.
|
||||
|
|
@ -99,9 +104,14 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
|
|||
return CustomFileFlag;
|
||||
|
||||
if (_customTexCrc.Contains(crc64))
|
||||
{
|
||||
_texReturnData.Value = true;
|
||||
return nint.Zero;
|
||||
}
|
||||
|
||||
return _checkFileStateHook.Original(ptr, crc64);
|
||||
var ret = _checkFileStateHook.Original(ptr, crc64);
|
||||
Penumbra.Log.Excessive($"[CheckFileState] Called on 0x{ptr:X} with CRC {crc64:X16}, returned 0x{ret:X}.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
private delegate byte LoadTexFileLocalDelegate(TextureResourceHandle* handle, int unk1, SeFileDescriptor* unk2, bool unk3);
|
||||
|
|
@ -118,7 +128,7 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
|
|||
|
||||
private delegate byte TexResourceHandleOnLoadPrototype(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2);
|
||||
|
||||
[Signature(Sigs.TexResourceHandleOnLoad, DetourName = nameof(OnLoadDetour))]
|
||||
[Signature(Sigs.TexHandleOnLoad, DetourName = nameof(OnLoadDetour))]
|
||||
private readonly Hook<TexResourceHandleOnLoadPrototype> _textureOnLoadHook = null!;
|
||||
|
||||
private byte OnLoadDetour(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2)
|
||||
|
|
@ -129,7 +139,9 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
|
|||
|
||||
// Function failed on a replaced texture, call local.
|
||||
_texReturnData.Value = false;
|
||||
return _loadTexFileLocal(handle, _lodService.GetLod(handle), descriptor, unk2 != 0);
|
||||
ret = _loadTexFileLocal(handle, _lodService.GetLod(handle), descriptor, unk2 != 0);
|
||||
_updateCategory(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private delegate byte LoadMdlFileExternPrototype(ResourceHandle* handle, nint unk1, bool unk2, nint unk3);
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ public class ResourceTree
|
|||
|
||||
// This way to tell apart MainHand and OffHand is not always accurate, but seems good enough for what we're doing with it.
|
||||
var slot = weaponIndex > 0 ? EquipSlot.OffHand : EquipSlot.MainHand;
|
||||
var equipment = new CharacterArmor(weapon->ModelSetId, (byte)weapon->Variant, new StainIds(weapon->Stain1, weapon->Stain2));
|
||||
var equipment = new CharacterArmor(weapon->ModelSetId, (byte)weapon->Variant, new StainIds(weapon->Stain0, weapon->Stain1));
|
||||
var weaponType = weapon->SecondaryId;
|
||||
|
||||
var genericContext = globalContext.CreateContext(subObject, 0xFFFFFFFFu, slot, equipment, weaponType);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Base;
|
||||
|
||||
namespace Penumbra.Interop.Structs;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
|
|
@ -7,5 +9,5 @@ public unsafe struct ClipScheduler
|
|||
public nint* VTable;
|
||||
|
||||
[FieldOffset(0x38)]
|
||||
public nint SchedulerTimeline;
|
||||
public SchedulerTimeline* SchedulerTimeline;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,37 +9,37 @@ internal static class StructExtensions
|
|||
public static unsafe ByteString AsByteString(in this StdString str)
|
||||
=> ByteString.FromSpanUnsafe(str.AsSpan(), true);
|
||||
|
||||
public static ByteString ResolveEidPathAsByteString(in this CharacterBase character)
|
||||
public static ByteString ResolveEidPathAsByteString(ref this CharacterBase character)
|
||||
{
|
||||
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveEidPath(pathBuffer));
|
||||
}
|
||||
|
||||
public static ByteString ResolveImcPathAsByteString(in this CharacterBase character, uint slotIndex)
|
||||
public static ByteString ResolveImcPathAsByteString(ref this CharacterBase character, uint slotIndex)
|
||||
{
|
||||
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveImcPath(pathBuffer, slotIndex));
|
||||
}
|
||||
|
||||
public static ByteString ResolveMdlPathAsByteString(in this CharacterBase character, uint slotIndex)
|
||||
public static ByteString ResolveMdlPathAsByteString(ref this CharacterBase character, uint slotIndex)
|
||||
{
|
||||
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveMdlPath(pathBuffer, slotIndex));
|
||||
}
|
||||
|
||||
public static unsafe ByteString ResolveMtrlPathAsByteString(in this CharacterBase character, uint slotIndex, byte* mtrlFileName)
|
||||
public static unsafe ByteString ResolveMtrlPathAsByteString(ref this CharacterBase character, uint slotIndex, byte* mtrlFileName)
|
||||
{
|
||||
var pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveMtrlPath(pathBuffer, CharacterBase.PathBufferSize, slotIndex, mtrlFileName));
|
||||
}
|
||||
|
||||
public static ByteString ResolveSklbPathAsByteString(in this CharacterBase character, uint partialSkeletonIndex)
|
||||
public static ByteString ResolveSklbPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex)
|
||||
{
|
||||
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveSklbPath(pathBuffer, partialSkeletonIndex));
|
||||
}
|
||||
|
||||
public static ByteString ResolveSkpPathAsByteString(in this CharacterBase character, uint partialSkeletonIndex)
|
||||
public static ByteString ResolveSkpPathAsByteString(ref this CharacterBase character, uint partialSkeletonIndex)
|
||||
{
|
||||
Span<byte> pathBuffer = stackalloc byte[CharacterBase.PathBufferSize];
|
||||
return ToOwnedByteString(character.ResolveSkpPath(pathBuffer, partialSkeletonIndex));
|
||||
|
|
|
|||
|
|
@ -430,7 +430,7 @@ public class DebugTab : Window, ITab, IUiService
|
|||
|
||||
foreach (var obj in _objects)
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero ? $"{((GameObject*)obj.Address)->ObjectIndex}" : "NULL");
|
||||
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? $"{((GameObject*)obj.Address)->ObjectIndex}" : "NULL");
|
||||
ImGuiUtil.DrawTableColumn($"0x{obj.Address:X}");
|
||||
ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero
|
||||
? string.Empty
|
||||
|
|
@ -482,14 +482,15 @@ public class DebugTab : Window, ITab, IUiService
|
|||
{
|
||||
var gameObject = (GameObject*)gameObjectPtr;
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted($"0x{drawObject:X}");
|
||||
|
||||
ImGuiUtil.CopyOnClickSelectable($"0x{drawObject:X}");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(gameObject->ObjectIndex.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(child ? "Child" : "Main");
|
||||
ImGui.TableNextColumn();
|
||||
var (address, name) = ($"0x{gameObjectPtr:X}", new ByteString(gameObject->Name).ToString());
|
||||
ImGui.TextUnformatted(address);
|
||||
ImGuiUtil.CopyOnClickSelectable(address);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(name);
|
||||
ImGui.TableNextColumn();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue