Add SCD handling and crc cache visualization.

This commit is contained in:
Ottermandias 2024-11-18 00:28:27 +01:00
parent 597380355a
commit 7ab5299f7a
4 changed files with 80 additions and 22 deletions

View file

@ -8,7 +8,10 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F89C9EAE-25C8-43BE-8108-5921E5A93502}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.github\workflows\build.yml = .github\workflows\build.yml
.github\workflows\release.yml = .github\workflows\release.yml
repo.json = repo.json
.github\workflows\test_release.yml = .github\workflows\test_release.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumbra.GameData\Penumbra.GameData.csproj", "{EE551E87-FDB3-4612-B500-DC870C07C605}"

View file

@ -15,18 +15,18 @@ public unsafe class ResourceLoader : IDisposable, IService
{
private readonly ResourceService _resources;
private readonly FileReadService _fileReadService;
private readonly TexMdlService _texMdlService;
private readonly TexMdlScdService _texMdlScdService;
private readonly PapHandler _papHandler;
private readonly Configuration _config;
private ResolveData _resolvedData = ResolveData.Invalid;
public event Action<Utf8GamePath, FullPath?, ResolveData>? PapRequested;
public ResourceLoader(ResourceService resources, FileReadService fileReadService, TexMdlService texMdlService, Configuration config)
public ResourceLoader(ResourceService resources, FileReadService fileReadService, TexMdlScdService texMdlScdService, Configuration config)
{
_resources = resources;
_fileReadService = fileReadService;
_texMdlService = texMdlService;
_texMdlScdService = texMdlScdService;
_config = config;
ResetResolvePath();
@ -140,7 +140,7 @@ public unsafe class ResourceLoader : IDisposable, IService
return;
}
_texMdlService.AddCrc(type, resolvedPath);
_texMdlScdService.AddCrc(type, resolvedPath);
// Replace the hash and path with the correct one for the replacement.
hash = ComputeHash(resolvedPath.Value.InternalName, parameters);
var oldPath = path;

View file

@ -10,7 +10,7 @@ using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.Re
namespace Penumbra.Interop.Hooks.ResourceLoading;
public unsafe class TexMdlService : IDisposable, IRequiredService
public unsafe class TexMdlScdService : IDisposable, IRequiredService
{
/// <summary>
/// We need to be able to obtain the requested LoD level.
@ -42,7 +42,7 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
private readonly LodService _lodService;
public TexMdlService(IGameInteropProvider interop)
public TexMdlScdService(IGameInteropProvider interop)
{
interop.InitializeFromAttributes(this);
_lodService = new LodService(interop);
@ -52,6 +52,7 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
_loadMdlFileExternHook.Enable();
if (!HookOverrides.Instance.ResourceLoading.TexResourceHandleOnLoad)
_textureOnLoadHook.Enable();
_soundOnLoadHook.Enable();
}
/// <summary> Add CRC64 if the given file is a model or texture file and has an associated path. </summary>
@ -59,8 +60,9 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
{
_ = type switch
{
ResourceType.Mdl when path.HasValue => _customMdlCrc.Add(path.Value.Crc64),
ResourceType.Tex when path.HasValue => _customTexCrc.Add(path.Value.Crc64),
ResourceType.Mdl when path.HasValue => _customFileCrc.TryAdd(path.Value.Crc64, ResourceType.Mdl),
ResourceType.Tex when path.HasValue => _customFileCrc.TryAdd(path.Value.Crc64, ResourceType.Tex),
ResourceType.Scd when path.HasValue => _customFileCrc.TryAdd(path.Value.Crc64, ResourceType.Scd),
_ => false,
};
}
@ -70,15 +72,16 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
_checkFileStateHook.Dispose();
_loadMdlFileExternHook.Dispose();
_textureOnLoadHook.Dispose();
_soundOnLoadHook.Dispose();
}
/// <summary>
/// We need to keep a list of all CRC64 hash values of our replaced Mdl and Tex files,
/// i.e. CRC32 of filename in the lower bytes, CRC32 of parent path in the upper bytes.
/// </summary>
private readonly HashSet<ulong> _customMdlCrc = [];
private readonly HashSet<ulong> _customTexCrc = [];
private readonly Dictionary<ulong, ResourceType> _customFileCrc = [];
public IReadOnlyDictionary<ulong, ResourceType> CustomCache
=> _customFileCrc;
private delegate nint CheckFileStatePrototype(nint unk1, ulong crc64);
@ -86,12 +89,34 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
private readonly Hook<CheckFileStatePrototype> _checkFileStateHook = null!;
private readonly ThreadLocal<bool> _texReturnData = new(() => default);
private readonly ThreadLocal<bool> _scdReturnData = new(() => default);
private delegate void UpdateCategoryDelegate(TextureResourceHandle* resourceHandle);
[Signature(Sigs.TexHandleUpdateCategory)]
private readonly UpdateCategoryDelegate _updateCategory = null!;
private delegate byte SoundOnLoadDelegate(ResourceHandle* handle, SeFileDescriptor* descriptor, byte unk);
[Signature("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 8B 79 ?? 48 8B DA 8B D7")]
private readonly delegate* unmanaged<ResourceHandle*, SeFileDescriptor*, byte, byte> _loadScdFileLocal = null!;
[Signature("40 56 57 41 54 48 81 EC 90 00 00 00 80 3A 0B 45 0F B6 E0 48 8B F2", DetourName = nameof(OnScdLoadDetour))]
private readonly Hook<SoundOnLoadDelegate> _soundOnLoadHook = null!;
private byte OnScdLoadDetour(ResourceHandle* handle, SeFileDescriptor* descriptor, byte unk)
{
var ret = _soundOnLoadHook.Original(handle, descriptor, unk);
if (!_scdReturnData.Value)
return ret;
// Function failed on a replaced scd, call local.
_scdReturnData.Value = false;
ret = _loadScdFileLocal(handle, descriptor, unk);
_updateCategory((TextureResourceHandle*)handle);
return ret;
}
/// <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.
@ -100,14 +125,17 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
/// </summary>
private nint CheckFileStateDetour(nint ptr, ulong crc64)
{
if (_customMdlCrc.Contains(crc64))
return CustomFileFlag;
if (_customTexCrc.Contains(crc64))
{
_texReturnData.Value = true;
return nint.Zero;
}
if (_customFileCrc.TryGetValue(crc64, out var type))
switch (type)
{
case ResourceType.Mdl: return CustomFileFlag;
case ResourceType.Tex:
_texReturnData.Value = true;
return nint.Zero;
case ResourceType.Scd:
_scdReturnData.Value = true;
return nint.Zero;
}
var ret = _checkFileStateHook.Original(ptr, crc64);
Penumbra.Log.Excessive($"[CheckFileState] Called on 0x{ptr:X} with CRC {crc64:X16}, returned 0x{ret:X}.");
@ -128,10 +156,10 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
private delegate byte TexResourceHandleOnLoadPrototype(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2);
[Signature(Sigs.TexHandleOnLoad, DetourName = nameof(OnLoadDetour))]
[Signature(Sigs.TexHandleOnLoad, DetourName = nameof(OnTexLoadDetour))]
private readonly Hook<TexResourceHandleOnLoadPrototype> _textureOnLoadHook = null!;
private byte OnLoadDetour(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2)
private byte OnTexLoadDetour(TextureResourceHandle* handle, SeFileDescriptor* descriptor, byte unk2)
{
var ret = _textureOnLoadHook.Original(handle, descriptor, unk2);
if (!_texReturnData.Value)

View file

@ -102,6 +102,7 @@ public class DebugTab : Window, ITab, IUiService
private readonly CrashHandlerPanel _crashHandlerPanel;
private readonly TexHeaderDrawer _texHeaderDrawer;
private readonly HookOverrideDrawer _hookOverrides;
private readonly TexMdlScdService _texMdlScdService;
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
IClientState clientState,
@ -112,7 +113,7 @@ public class DebugTab : Window, ITab, IUiService
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework,
TextureManager textureManager, ShaderReplacementFixer shaderReplacementFixer, RedrawService redraws, DictEmote emotes,
Diagnostics diagnostics, IpcTester ipcTester, CrashHandlerPanel crashHandlerPanel, TexHeaderDrawer texHeaderDrawer,
HookOverrideDrawer hookOverrides)
HookOverrideDrawer hookOverrides, TexMdlScdService texMdlScdService)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
{
IsOpen = true;
@ -150,6 +151,7 @@ public class DebugTab : Window, ITab, IUiService
_crashHandlerPanel = crashHandlerPanel;
_texHeaderDrawer = texHeaderDrawer;
_hookOverrides = hookOverrides;
_texMdlScdService = texMdlScdService;
_objects = objects;
_clientState = clientState;
}
@ -183,6 +185,7 @@ public class DebugTab : Window, ITab, IUiService
DrawDebugCharacterUtility();
DrawShaderReplacementFixer();
DrawData();
DrawCrcCache();
DrawResourceProblems();
_hookOverrides.Draw();
DrawPlayerModelInfo();
@ -1021,6 +1024,30 @@ public class DebugTab : Window, ITab, IUiService
DrawDebugResidentResources();
}
private unsafe void DrawCrcCache()
{
var header = ImUtf8.CollapsingHeader("CRC Cache"u8);
if (!header)
return;
using var table = ImUtf8.Table("table"u8, 2);
if (!table)
return;
using var font = ImRaii.PushFont(UiBuilder.MonoFont);
ImUtf8.TableSetupColumn("Hash"u8, ImGuiTableColumnFlags.WidthFixed, 18 * UiBuilder.MonoFont.GetCharAdvance('0'));
ImUtf8.TableSetupColumn("Type"u8, ImGuiTableColumnFlags.WidthFixed, 5 * UiBuilder.MonoFont.GetCharAdvance('0'));
ImGui.TableHeadersRow();
foreach (var (hash, type) in _texMdlScdService.CustomCache)
{
ImGui.TableNextColumn();
ImUtf8.Text($"{hash:X16}");
ImGui.TableNextColumn();
ImUtf8.Text($"{type}");
}
}
/// <summary> Draw resources with unusual reference count. </summary>
private unsafe void DrawResourceProblems()
{