mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add handling for cached TMBs.
This commit is contained in:
parent
f679e0ccee
commit
b3883c1306
9 changed files with 415 additions and 40 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit ffc149cc8c169c2c6e838cbd138676f6fe4daeea
|
Subproject commit 19355cfa0ec80e8d5a91de11ecffc49257b37b53
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using Penumbra.Collections;
|
using Penumbra.Collections;
|
||||||
using Penumbra.Mods;
|
|
||||||
using Penumbra.Mods.Editor;
|
using Penumbra.Mods.Editor;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
|
@ -33,5 +32,8 @@ public sealed class ResolvedFileChanged()
|
||||||
{
|
{
|
||||||
/// <seealso cref="Api.DalamudSubstitutionProvider.OnResolvedFileChange"/>
|
/// <seealso cref="Api.DalamudSubstitutionProvider.OnResolvedFileChange"/>
|
||||||
DalamudSubstitutionProvider = 0,
|
DalamudSubstitutionProvider = 0,
|
||||||
|
|
||||||
|
/// <seealso cref="Interop.Services.SchedulerResourceManagementService.OnResolvedFileChange"/>
|
||||||
|
SchedulerResourceManagementService = 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,9 @@ public class GameState : IService
|
||||||
public void RestoreAnimationData(ResolveData old)
|
public void RestoreAnimationData(ResolveData old)
|
||||||
=> _animationLoadData.Value = old;
|
=> _animationLoadData.Value = old;
|
||||||
|
|
||||||
|
public readonly ThreadLocal<bool> InLoadActionTmb = new(() => false);
|
||||||
|
public readonly ThreadLocal<bool> SkipTmbCache = new(() => false);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Sound Data
|
#region Sound Data
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Hooks.Animation;
|
||||||
|
|
||||||
|
/// <summary> Load a cached TMB resource from SchedulerResourceManagement. </summary>
|
||||||
|
public sealed unsafe class GetCachedScheduleResource : FastHook<GetCachedScheduleResource.Delegate>
|
||||||
|
{
|
||||||
|
private readonly GameState _state;
|
||||||
|
|
||||||
|
public GetCachedScheduleResource(HookManager hooks, GameState state)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
Task = hooks.CreateHook<Delegate>("Get Cached Schedule Resource", Sigs.GetCachedScheduleResource, Detour,
|
||||||
|
!HookOverrides.Instance.Animation.GetCachedScheduleResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate SchedulerResource* Delegate(SchedulerResourceManagement* a, ScheduleResourceLoadData* b, byte useMap);
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private SchedulerResource* Detour(SchedulerResourceManagement* a, ScheduleResourceLoadData* b, byte c)
|
||||||
|
{
|
||||||
|
if (_state.SkipTmbCache.Value)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Verbose(
|
||||||
|
$"[GetCachedScheduleResource] Called with 0x{(ulong)a:X}, {b->Id}, {new CiByteString(b->Path, MetaDataComputation.None)}, {c} from LoadActionTmb with forced skipping of cache, returning NULL.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = Task.Result.Original(a, b, c);
|
||||||
|
Penumbra.Log.Excessive(
|
||||||
|
$"[GetCachedScheduleResource] Called with 0x{(ulong)a:X}, {b->Id}, {new CiByteString(b->Path, MetaDataComputation.None)}, {c}, returning 0x{(ulong)ret:X} ({(ret != null && Resource(ret) != null ? Resource(ret)->FileName().ToString() : "No Path")}).");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ScheduleResourceLoadData
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public byte* Path;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public uint Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #TODO: remove when fixed in CS.
|
||||||
|
public static ResourceHandle* Resource(SchedulerResource* r)
|
||||||
|
=> ((ResourceHandle**)r)[3];
|
||||||
|
}
|
||||||
55
Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs
Normal file
55
Penumbra/Interop/Hooks/Animation/LoadActionTmb.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.GameData;
|
||||||
|
using Penumbra.Interop.Services;
|
||||||
|
using Penumbra.String;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Hooks.Animation;
|
||||||
|
|
||||||
|
/// <summary> Load a Action TMB. </summary>
|
||||||
|
public sealed unsafe class LoadActionTmb : FastHook<LoadActionTmb.Delegate>
|
||||||
|
{
|
||||||
|
private readonly GameState _state;
|
||||||
|
private readonly SchedulerResourceManagementService _scheduler;
|
||||||
|
|
||||||
|
public LoadActionTmb(HookManager hooks, GameState state, SchedulerResourceManagementService scheduler)
|
||||||
|
{
|
||||||
|
_state = state;
|
||||||
|
_scheduler = scheduler;
|
||||||
|
Task = hooks.CreateHook<Delegate>("Load Action TMB", Sigs.LoadActionTmb, Detour, !HookOverrides.Instance.Animation.LoadActionTmb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate SchedulerResource* Delegate(SchedulerResourceManagement* scheduler,
|
||||||
|
GetCachedScheduleResource.ScheduleResourceLoadData* loadData, nint b, byte c, byte d, byte e);
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private SchedulerResource* Detour(SchedulerResourceManagement* scheduler, GetCachedScheduleResource.ScheduleResourceLoadData* loadData,
|
||||||
|
nint b, byte c, byte d, byte e)
|
||||||
|
{
|
||||||
|
_state.InLoadActionTmb.Value = true;
|
||||||
|
SchedulerResource* ret;
|
||||||
|
if (ShouldSkipCache(loadData))
|
||||||
|
{
|
||||||
|
_state.SkipTmbCache.Value = true;
|
||||||
|
ret = Task.Result.Original(scheduler, loadData, b, c, d, 1);
|
||||||
|
Penumbra.Log.Verbose(
|
||||||
|
$"[LoadActionTMB] Called with 0x{(ulong)scheduler:X}, {loadData->Id}, {new CiByteString(loadData->Path, MetaDataComputation.None)}, 0x{b:X}, {c}, {d}, {e}, forced no-cache use, returned 0x{(ulong)ret:X} ({(ret != null && GetCachedScheduleResource.Resource(ret) != null ? GetCachedScheduleResource.Resource(ret)->FileName().ToString() : "No Path")}).");
|
||||||
|
_state.SkipTmbCache.Value = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = Task.Result.Original(scheduler, loadData, b, c, d, e);
|
||||||
|
Penumbra.Log.Excessive(
|
||||||
|
$"[LoadActionTMB] Called with 0x{(ulong)scheduler:X}, {loadData->Id}, {new CiByteString(loadData->Path)}, 0x{b:X}, {c}, {d}, {e}, returned 0x{(ulong)ret:X} ({(ret != null && GetCachedScheduleResource.Resource(ret) != null ? GetCachedScheduleResource.Resource(ret)->FileName().ToString() : "No Path")}).");
|
||||||
|
}
|
||||||
|
|
||||||
|
_state.InLoadActionTmb.Value = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private bool ShouldSkipCache(GetCachedScheduleResource.ScheduleResourceLoadData* loadData)
|
||||||
|
=> _scheduler.Contains(loadData->Id);
|
||||||
|
}
|
||||||
|
|
@ -43,6 +43,8 @@ public class HookOverrides
|
||||||
public bool SomeMountAnimation;
|
public bool SomeMountAnimation;
|
||||||
public bool SomePapLoad;
|
public bool SomePapLoad;
|
||||||
public bool SomeParasolAnimation;
|
public bool SomeParasolAnimation;
|
||||||
|
public bool GetCachedScheduleResource;
|
||||||
|
public bool LoadActionTmb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct MetaHooks
|
public struct MetaHooks
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
using System.Collections.Frozen;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility.Signatures;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
using OtterGui.Services;
|
||||||
|
using Penumbra.Collections;
|
||||||
|
using Penumbra.Communication;
|
||||||
|
using Penumbra.GameData;
|
||||||
|
using Penumbra.Mods.Editor;
|
||||||
|
using Penumbra.Services;
|
||||||
|
using Penumbra.String;
|
||||||
|
using Penumbra.String.Classes;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.Services;
|
||||||
|
|
||||||
|
public unsafe class SchedulerResourceManagementService : IService, IDisposable
|
||||||
|
{
|
||||||
|
private static readonly CiByteString TmbExtension = new(".tmb"u8, MetaDataComputation.All);
|
||||||
|
private static readonly CiByteString FolderPrefix = new("chara/action/"u8, MetaDataComputation.All);
|
||||||
|
|
||||||
|
private readonly CommunicatorService _communicator;
|
||||||
|
private readonly FrozenDictionary<CiByteString, uint> _actionTmbs;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<uint, CiByteString> _listedTmbIds = [];
|
||||||
|
|
||||||
|
public bool Contains(uint tmbId)
|
||||||
|
=> _listedTmbIds.ContainsKey(tmbId);
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<uint, CiByteString> ListedTmbs
|
||||||
|
=> _listedTmbIds;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<CiByteString, uint> ActionTmbs
|
||||||
|
=> _actionTmbs;
|
||||||
|
|
||||||
|
public SchedulerResourceManagementService(IGameInteropProvider interop, CommunicatorService communicator, IDataManager dataManager)
|
||||||
|
{
|
||||||
|
_communicator = communicator;
|
||||||
|
_actionTmbs = CreateActionTmbs(dataManager);
|
||||||
|
_communicator.ResolvedFileChanged.Subscribe(OnResolvedFileChange, ResolvedFileChanged.Priority.SchedulerResourceManagementService);
|
||||||
|
interop.InitializeFromAttributes(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath gamePath, FullPath oldPath,
|
||||||
|
FullPath newPath, IMod? mod)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ResolvedFileChanged.Type.Added:
|
||||||
|
CheckFile(gamePath);
|
||||||
|
return;
|
||||||
|
case ResolvedFileChanged.Type.FullRecomputeFinished:
|
||||||
|
foreach (var path in collection.ResolvedFiles.Keys)
|
||||||
|
CheckFile(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
private void CheckFile(Utf8GamePath gamePath)
|
||||||
|
{
|
||||||
|
if (!gamePath.Extension().Equals(TmbExtension))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!gamePath.Path.StartsWith(FolderPrefix))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var tmb = gamePath.Path.Substring(FolderPrefix.Length, gamePath.Length - FolderPrefix.Length - TmbExtension.Length).Clone();
|
||||||
|
if (_actionTmbs.TryGetValue(tmb, out var rowId))
|
||||||
|
_listedTmbIds[rowId] = tmb;
|
||||||
|
else
|
||||||
|
Penumbra.Log.Debug($"Action TMB {gamePath} encountered with no corresponding row ID.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Signature(Sigs.SchedulerResourceManagementInstance, ScanType = ScanType.StaticAddress)]
|
||||||
|
public readonly SchedulerResourceManagement** Address = null;
|
||||||
|
|
||||||
|
public SchedulerResourceManagement* Scheduler
|
||||||
|
=> *Address;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_listedTmbIds.Clear();
|
||||||
|
_communicator.ResolvedFileChanged.Unsubscribe(OnResolvedFileChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FrozenDictionary<CiByteString, uint> CreateActionTmbs(IDataManager dataManager)
|
||||||
|
{
|
||||||
|
var sheet = dataManager.GetExcelSheet<ActionTimeline>();
|
||||||
|
return sheet.Where(row => !row.Key.IsEmpty).DistinctBy(row => row.Key).ToFrozenDictionary(row => new CiByteString(row.Key, MetaDataComputation.All).Clone(), row => row.RowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,38 +69,39 @@ public class Diagnostics(ServiceManager provider) : IUiService
|
||||||
|
|
||||||
public class DebugTab : Window, ITab, IUiService
|
public class DebugTab : Window, ITab, IUiService
|
||||||
{
|
{
|
||||||
private readonly PerformanceTracker _performance;
|
private readonly PerformanceTracker _performance;
|
||||||
private readonly Configuration _config;
|
private readonly Configuration _config;
|
||||||
private readonly CollectionManager _collectionManager;
|
private readonly CollectionManager _collectionManager;
|
||||||
private readonly ModManager _modManager;
|
private readonly ModManager _modManager;
|
||||||
private readonly ValidityChecker _validityChecker;
|
private readonly ValidityChecker _validityChecker;
|
||||||
private readonly HttpApi _httpApi;
|
private readonly HttpApi _httpApi;
|
||||||
private readonly ActorManager _actors;
|
private readonly ActorManager _actors;
|
||||||
private readonly StainService _stains;
|
private readonly StainService _stains;
|
||||||
private readonly GlobalVariablesDrawer _globalVariablesDrawer;
|
private readonly GlobalVariablesDrawer _globalVariablesDrawer;
|
||||||
private readonly ResourceManagerService _resourceManager;
|
private readonly ResourceManagerService _resourceManager;
|
||||||
private readonly CollectionResolver _collectionResolver;
|
private readonly CollectionResolver _collectionResolver;
|
||||||
private readonly DrawObjectState _drawObjectState;
|
private readonly DrawObjectState _drawObjectState;
|
||||||
private readonly PathState _pathState;
|
private readonly PathState _pathState;
|
||||||
private readonly SubfileHelper _subfileHelper;
|
private readonly SubfileHelper _subfileHelper;
|
||||||
private readonly IdentifiedCollectionCache _identifiedCollectionCache;
|
private readonly IdentifiedCollectionCache _identifiedCollectionCache;
|
||||||
private readonly CutsceneService _cutsceneService;
|
private readonly CutsceneService _cutsceneService;
|
||||||
private readonly ModImportManager _modImporter;
|
private readonly ModImportManager _modImporter;
|
||||||
private readonly ImportPopup _importPopup;
|
private readonly ImportPopup _importPopup;
|
||||||
private readonly FrameworkManager _framework;
|
private readonly FrameworkManager _framework;
|
||||||
private readonly TextureManager _textureManager;
|
private readonly TextureManager _textureManager;
|
||||||
private readonly ShaderReplacementFixer _shaderReplacementFixer;
|
private readonly ShaderReplacementFixer _shaderReplacementFixer;
|
||||||
private readonly RedrawService _redraws;
|
private readonly RedrawService _redraws;
|
||||||
private readonly DictEmote _emotes;
|
private readonly DictEmote _emotes;
|
||||||
private readonly Diagnostics _diagnostics;
|
private readonly Diagnostics _diagnostics;
|
||||||
private readonly ObjectManager _objects;
|
private readonly ObjectManager _objects;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly IDataManager _dataManager;
|
private readonly IDataManager _dataManager;
|
||||||
private readonly IpcTester _ipcTester;
|
private readonly IpcTester _ipcTester;
|
||||||
private readonly CrashHandlerPanel _crashHandlerPanel;
|
private readonly CrashHandlerPanel _crashHandlerPanel;
|
||||||
private readonly TexHeaderDrawer _texHeaderDrawer;
|
private readonly TexHeaderDrawer _texHeaderDrawer;
|
||||||
private readonly HookOverrideDrawer _hookOverrides;
|
private readonly HookOverrideDrawer _hookOverrides;
|
||||||
private readonly RsfService _rsfService;
|
private readonly RsfService _rsfService;
|
||||||
|
private readonly SchedulerResourceManagementService _schedulerService;
|
||||||
|
|
||||||
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
|
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
|
||||||
IClientState clientState, IDataManager dataManager,
|
IClientState clientState, IDataManager dataManager,
|
||||||
|
|
@ -110,7 +111,8 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework,
|
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework,
|
||||||
TextureManager textureManager, ShaderReplacementFixer shaderReplacementFixer, RedrawService redraws, DictEmote emotes,
|
TextureManager textureManager, ShaderReplacementFixer shaderReplacementFixer, RedrawService redraws, DictEmote emotes,
|
||||||
Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer,
|
Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer,
|
||||||
HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer)
|
HookOverrideDrawer hookOverrides, RsfService rsfService, GlobalVariablesDrawer globalVariablesDrawer,
|
||||||
|
SchedulerResourceManagementService schedulerService)
|
||||||
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
|
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
|
||||||
{
|
{
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
|
|
@ -148,6 +150,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
_hookOverrides = hookOverrides;
|
_hookOverrides = hookOverrides;
|
||||||
_rsfService = rsfService;
|
_rsfService = rsfService;
|
||||||
_globalVariablesDrawer = globalVariablesDrawer;
|
_globalVariablesDrawer = globalVariablesDrawer;
|
||||||
|
_schedulerService = schedulerService;
|
||||||
_objects = objects;
|
_objects = objects;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_dataManager = dataManager;
|
_dataManager = dataManager;
|
||||||
|
|
@ -672,6 +675,22 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using (var tmbCache = TreeNode("TMB Cache"))
|
||||||
|
{
|
||||||
|
if (tmbCache)
|
||||||
|
{
|
||||||
|
using var table = Table("###TmbTable", 2, ImGuiTableFlags.SizingFixedFit);
|
||||||
|
if (table)
|
||||||
|
{
|
||||||
|
foreach (var (id, name) in _schedulerService.ListedTmbs.OrderBy(kvp => kvp.Key))
|
||||||
|
{
|
||||||
|
ImUtf8.DrawTableColumn($"{id:D6}");
|
||||||
|
ImUtf8.DrawTableColumn(name.Span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawData()
|
private void DrawData()
|
||||||
|
|
@ -680,6 +699,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawEmotes();
|
DrawEmotes();
|
||||||
|
DrawActionTmbs();
|
||||||
DrawStainTemplates();
|
DrawStainTemplates();
|
||||||
DrawAtch();
|
DrawAtch();
|
||||||
}
|
}
|
||||||
|
|
@ -739,6 +759,27 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing());
|
ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawActionTmbs()
|
||||||
|
{
|
||||||
|
using var mainTree = TreeNode("Action TMBs");
|
||||||
|
if (!mainTree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var table = Table("##table", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.SizingFixedFit,
|
||||||
|
new Vector2(-1, 12 * ImGui.GetTextLineHeightWithSpacing()));
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing());
|
||||||
|
var dummy = ImGuiClip.ClippedDraw(_schedulerService.ActionTmbs.OrderBy(r => r.Value), skips,
|
||||||
|
p =>
|
||||||
|
{
|
||||||
|
ImUtf8.DrawTableColumn($"{p.Value}");
|
||||||
|
ImUtf8.DrawTableColumn(p.Key.Span);
|
||||||
|
});
|
||||||
|
ImGuiClip.DrawEndDummy(dummy, ImGui.GetTextLineHeightWithSpacing());
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawStainTemplates()
|
private void DrawStainTemplates()
|
||||||
{
|
{
|
||||||
using var mainTree = TreeNode("Staining Templates");
|
using var mainTree = TreeNode("Staining Templates");
|
||||||
|
|
@ -1061,7 +1102,7 @@ public class DebugTab : Window, ITab, IUiService
|
||||||
}
|
}
|
||||||
|
|
||||||
ImUtf8.HoverTooltip("Click to copy address to clipboard."u8);
|
ImUtf8.HoverTooltip("Click to copy address to clipboard."u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, nint address)
|
public static unsafe void DrawCopyableAddress(ReadOnlySpan<byte> label, nint address)
|
||||||
=> DrawCopyableAddress(label, (void*)address);
|
=> DrawCopyableAddress(label, (void*)address);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,23 @@
|
||||||
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Scheduler;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource;
|
||||||
|
using FFXIVClientStructs.Interop;
|
||||||
|
using FFXIVClientStructs.STD;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
using OtterGui.Text;
|
using OtterGui.Text;
|
||||||
using Penumbra.Interop.Services;
|
using Penumbra.Interop.Services;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.String;
|
||||||
|
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
|
||||||
|
|
||||||
namespace Penumbra.UI.Tabs.Debug;
|
namespace Penumbra.UI.Tabs.Debug;
|
||||||
|
|
||||||
public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, ResidentResourceManager residentResources) : IUiService
|
public unsafe class GlobalVariablesDrawer(
|
||||||
|
CharacterUtility characterUtility,
|
||||||
|
ResidentResourceManager residentResources,
|
||||||
|
SchedulerResourceManagementService scheduler) : IUiService
|
||||||
{
|
{
|
||||||
/// <summary> Draw information about some game global variables. </summary>
|
/// <summary> Draw information about some game global variables. </summary>
|
||||||
public void Draw()
|
public void Draw()
|
||||||
|
|
@ -16,13 +27,22 @@ public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, Res
|
||||||
if (!header)
|
if (!header)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DebugTab.DrawCopyableAddress("CharacterUtility"u8, characterUtility.Address);
|
var actionManager = (ActionTimelineManager**)ActionTimelineManager.Instance();
|
||||||
DebugTab.DrawCopyableAddress("ResidentResourceManager"u8, residentResources.Address);
|
DebugTab.DrawCopyableAddress("CharacterUtility"u8, characterUtility.Address);
|
||||||
DebugTab.DrawCopyableAddress("Device"u8, Device.Instance());
|
DebugTab.DrawCopyableAddress("ResidentResourceManager"u8, residentResources.Address);
|
||||||
|
DebugTab.DrawCopyableAddress("ScheduleManagement"u8, ScheduleManagement.Instance());
|
||||||
|
DebugTab.DrawCopyableAddress("ActionTimelineManager*"u8, actionManager);
|
||||||
|
DebugTab.DrawCopyableAddress("ActionTimelineManager"u8, actionManager != null ? *actionManager : null);
|
||||||
|
DebugTab.DrawCopyableAddress("SchedulerResourceManagement*"u8, scheduler.Address);
|
||||||
|
DebugTab.DrawCopyableAddress("SchedulerResourceManagement"u8, scheduler.Address != null ? *scheduler.Address : null);
|
||||||
|
DebugTab.DrawCopyableAddress("Device"u8, Device.Instance());
|
||||||
DrawCharacterUtility();
|
DrawCharacterUtility();
|
||||||
DrawResidentResources();
|
DrawResidentResources();
|
||||||
|
DrawSchedulerResourcesMap();
|
||||||
|
DrawSchedulerResourcesList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draw information about the character utility class from SE,
|
/// Draw information about the character utility class from SE,
|
||||||
/// displaying all files, their sizes, the default files and the default sizes.
|
/// displaying all files, their sizes, the default files and the default sizes.
|
||||||
|
|
@ -123,4 +143,111 @@ public unsafe class GlobalVariablesDrawer(CharacterUtility characterUtility, Res
|
||||||
ImUtf8.DrawTableColumn(length.ToString());
|
ImUtf8.DrawTableColumn(length.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string _schedulerFilterList = string.Empty;
|
||||||
|
private string _schedulerFilterMap = string.Empty;
|
||||||
|
private CiByteString _schedulerFilterListU8 = CiByteString.Empty;
|
||||||
|
private CiByteString _schedulerFilterMapU8 = CiByteString.Empty;
|
||||||
|
private int _shownResourcesList = 0;
|
||||||
|
private int _shownResourcesMap = 0;
|
||||||
|
|
||||||
|
private void DrawSchedulerResourcesMap()
|
||||||
|
{
|
||||||
|
using var tree = ImUtf8.TreeNode("Scheduler Resources (Map)"u8);
|
||||||
|
if (!tree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scheduler.Address == null || scheduler.Scheduler == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ImUtf8.InputText("##SchedulerMapFilter"u8, ref _schedulerFilterMap, "Filter..."u8))
|
||||||
|
_schedulerFilterMapU8 = CiByteString.FromString(_schedulerFilterMap, out var t, MetaDataComputation.All, false)
|
||||||
|
? t
|
||||||
|
: CiByteString.Empty;
|
||||||
|
ImUtf8.Text($"{_shownResourcesMap} / {scheduler.Scheduler->NumResources}");
|
||||||
|
using var table = ImUtf8.Table("##SchedulerMapResources"u8, 10, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
|
||||||
|
-Vector2.UnitX);
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var map = (StdMap<int, Pointer<SchedulerResource>>*)&scheduler.Scheduler->Unknown;
|
||||||
|
var total = 0;
|
||||||
|
_shownResourcesMap = 0;
|
||||||
|
foreach (var (key, resourcePtr) in *map)
|
||||||
|
{
|
||||||
|
var resource = resourcePtr.Value;
|
||||||
|
if (_schedulerFilterMap.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterMapU8.Span) >= 0)
|
||||||
|
{
|
||||||
|
ImUtf8.DrawTableColumn($"[{total:D4}]");
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Name.Unk1}");
|
||||||
|
ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span);
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Consumers}");
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource:X}");
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
var resourceHandle = *((ResourceHandle**)resource + 3);
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resourceHandle:X}");
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImUtf8.CopyOnClickSelectable(resourceHandle->FileName().Span);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
uint dataLength = 0;
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource->GetResourceData(&dataLength):X}");
|
||||||
|
ImUtf8.DrawTableColumn($"{dataLength}");
|
||||||
|
++_shownResourcesMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
++total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawSchedulerResourcesList()
|
||||||
|
{
|
||||||
|
using var tree = ImUtf8.TreeNode("Scheduler Resources (List)"u8);
|
||||||
|
if (!tree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scheduler.Address == null || scheduler.Scheduler == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ImUtf8.InputText("##SchedulerListFilter"u8, ref _schedulerFilterList, "Filter..."u8))
|
||||||
|
_schedulerFilterListU8 = CiByteString.FromString(_schedulerFilterList, out var t, MetaDataComputation.All, false)
|
||||||
|
? t
|
||||||
|
: CiByteString.Empty;
|
||||||
|
ImUtf8.Text($"{_shownResourcesList} / {scheduler.Scheduler->NumResources}");
|
||||||
|
using var table = ImUtf8.Table("##SchedulerListResources"u8, 10, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
|
||||||
|
-Vector2.UnitX);
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var resource = scheduler.Scheduler->Begin;
|
||||||
|
var total = 0;
|
||||||
|
_shownResourcesList = 0;
|
||||||
|
while (resource != null && total < (int)scheduler.Scheduler->NumResources)
|
||||||
|
{
|
||||||
|
if (_schedulerFilterList.Length is 0 || resource->Name.Buffer.IndexOf(_schedulerFilterListU8.Span) >= 0)
|
||||||
|
{
|
||||||
|
ImUtf8.DrawTableColumn($"[{total:D4}]");
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Name.Unk1}");
|
||||||
|
ImUtf8.DrawTableColumn(new CiByteString(resource->Name.Buffer, MetaDataComputation.None).Span);
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Consumers}");
|
||||||
|
ImUtf8.DrawTableColumn($"{resource->Unk1}"); // key
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource:X}");
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
var resourceHandle = *((ResourceHandle**)resource + 3);
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resourceHandle:X}");
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImUtf8.CopyOnClickSelectable(resourceHandle->FileName().Span);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
uint dataLength = 0;
|
||||||
|
ImUtf8.CopyOnClickSelectable($"0x{(ulong)resource->GetResourceData(&dataLength):X}");
|
||||||
|
ImUtf8.DrawTableColumn($"{dataLength}");
|
||||||
|
++_shownResourcesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource = resource->Previous;
|
||||||
|
++total;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue