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}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F89C9EAE-25C8-43BE-8108-5921E5A93502}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
.github\workflows\build.yml = .github\workflows\build.yml
.github\workflows\release.yml = .github\workflows\release.yml
repo.json = repo.json repo.json = repo.json
.github\workflows\test_release.yml = .github\workflows\test_release.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumbra.GameData\Penumbra.GameData.csproj", "{EE551E87-FDB3-4612-B500-DC870C07C605}" 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 ResourceService _resources;
private readonly FileReadService _fileReadService; private readonly FileReadService _fileReadService;
private readonly TexMdlService _texMdlService; private readonly TexMdlScdService _texMdlScdService;
private readonly PapHandler _papHandler; private readonly PapHandler _papHandler;
private readonly Configuration _config; private readonly Configuration _config;
private ResolveData _resolvedData = ResolveData.Invalid; private ResolveData _resolvedData = ResolveData.Invalid;
public event Action<Utf8GamePath, FullPath?, ResolveData>? PapRequested; 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; _resources = resources;
_fileReadService = fileReadService; _fileReadService = fileReadService;
_texMdlService = texMdlService; _texMdlScdService = texMdlScdService;
_config = config; _config = config;
ResetResolvePath(); ResetResolvePath();
@ -140,7 +140,7 @@ public unsafe class ResourceLoader : IDisposable, IService
return; return;
} }
_texMdlService.AddCrc(type, resolvedPath); _texMdlScdService.AddCrc(type, resolvedPath);
// Replace the hash and path with the correct one for the replacement. // Replace the hash and path with the correct one for the replacement.
hash = ComputeHash(resolvedPath.Value.InternalName, parameters); hash = ComputeHash(resolvedPath.Value.InternalName, parameters);
var oldPath = path; var oldPath = path;

View file

@ -10,7 +10,7 @@ using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.Re
namespace Penumbra.Interop.Hooks.ResourceLoading; namespace Penumbra.Interop.Hooks.ResourceLoading;
public unsafe class TexMdlService : IDisposable, IRequiredService public unsafe class TexMdlScdService : IDisposable, IRequiredService
{ {
/// <summary> /// <summary>
/// We need to be able to obtain the requested LoD level. /// 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; private readonly LodService _lodService;
public TexMdlService(IGameInteropProvider interop) public TexMdlScdService(IGameInteropProvider interop)
{ {
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_lodService = new LodService(interop); _lodService = new LodService(interop);
@ -52,6 +52,7 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
_loadMdlFileExternHook.Enable(); _loadMdlFileExternHook.Enable();
if (!HookOverrides.Instance.ResourceLoading.TexResourceHandleOnLoad) if (!HookOverrides.Instance.ResourceLoading.TexResourceHandleOnLoad)
_textureOnLoadHook.Enable(); _textureOnLoadHook.Enable();
_soundOnLoadHook.Enable();
} }
/// <summary> Add CRC64 if the given file is a model or texture file and has an associated path. </summary> /// <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 _ = type switch
{ {
ResourceType.Mdl when path.HasValue => _customMdlCrc.Add(path.Value.Crc64), ResourceType.Mdl when path.HasValue => _customFileCrc.TryAdd(path.Value.Crc64, ResourceType.Mdl),
ResourceType.Tex when path.HasValue => _customTexCrc.Add(path.Value.Crc64), 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, _ => false,
}; };
} }
@ -70,15 +72,16 @@ public unsafe class TexMdlService : IDisposable, IRequiredService
_checkFileStateHook.Dispose(); _checkFileStateHook.Dispose();
_loadMdlFileExternHook.Dispose(); _loadMdlFileExternHook.Dispose();
_textureOnLoadHook.Dispose(); _textureOnLoadHook.Dispose();
_soundOnLoadHook.Dispose();
} }
/// <summary> /// <summary>
/// We need to keep a list of all CRC64 hash values of our replaced Mdl and Tex files, /// 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. /// i.e. CRC32 of filename in the lower bytes, CRC32 of parent path in the upper bytes.
/// </summary> /// </summary>
private readonly HashSet<ulong> _customMdlCrc = []; private readonly Dictionary<ulong, ResourceType> _customFileCrc = [];
public IReadOnlyDictionary<ulong, ResourceType> CustomCache
private readonly HashSet<ulong> _customTexCrc = []; => _customFileCrc;
private delegate nint CheckFileStatePrototype(nint unk1, ulong crc64); 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 Hook<CheckFileStatePrototype> _checkFileStateHook = null!;
private readonly ThreadLocal<bool> _texReturnData = new(() => default); private readonly ThreadLocal<bool> _texReturnData = new(() => default);
private readonly ThreadLocal<bool> _scdReturnData = new(() => default);
private delegate void UpdateCategoryDelegate(TextureResourceHandle* resourceHandle); private delegate void UpdateCategoryDelegate(TextureResourceHandle* resourceHandle);
[Signature(Sigs.TexHandleUpdateCategory)] [Signature(Sigs.TexHandleUpdateCategory)]
private readonly UpdateCategoryDelegate _updateCategory = null!; 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> /// <summary>
/// The function that checks a files CRC64 to determine whether it is 'protected'. /// 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. /// 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> /// </summary>
private nint CheckFileStateDetour(nint ptr, ulong crc64) private nint CheckFileStateDetour(nint ptr, ulong crc64)
{ {
if (_customMdlCrc.Contains(crc64)) if (_customFileCrc.TryGetValue(crc64, out var type))
return CustomFileFlag; switch (type)
{
if (_customTexCrc.Contains(crc64)) case ResourceType.Mdl: return CustomFileFlag;
{ case ResourceType.Tex:
_texReturnData.Value = true; _texReturnData.Value = true;
return nint.Zero; return nint.Zero;
} case ResourceType.Scd:
_scdReturnData.Value = true;
return nint.Zero;
}
var ret = _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}."); 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); 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 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); var ret = _textureOnLoadHook.Original(handle, descriptor, unk2);
if (!_texReturnData.Value) if (!_texReturnData.Value)

View file

@ -102,6 +102,7 @@ public class DebugTab : Window, ITab, IUiService
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 TexMdlScdService _texMdlScdService;
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects, public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects,
IClientState clientState, IClientState clientState,
@ -112,7 +113,7 @@ 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) HookOverrideDrawer hookOverrides, TexMdlScdService texMdlScdService)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
{ {
IsOpen = true; IsOpen = true;
@ -150,6 +151,7 @@ public class DebugTab : Window, ITab, IUiService
_crashHandlerPanel = crashHandlerPanel; _crashHandlerPanel = crashHandlerPanel;
_texHeaderDrawer = texHeaderDrawer; _texHeaderDrawer = texHeaderDrawer;
_hookOverrides = hookOverrides; _hookOverrides = hookOverrides;
_texMdlScdService = texMdlScdService;
_objects = objects; _objects = objects;
_clientState = clientState; _clientState = clientState;
} }
@ -183,6 +185,7 @@ public class DebugTab : Window, ITab, IUiService
DrawDebugCharacterUtility(); DrawDebugCharacterUtility();
DrawShaderReplacementFixer(); DrawShaderReplacementFixer();
DrawData(); DrawData();
DrawCrcCache();
DrawResourceProblems(); DrawResourceProblems();
_hookOverrides.Draw(); _hookOverrides.Draw();
DrawPlayerModelInfo(); DrawPlayerModelInfo();
@ -1021,6 +1024,30 @@ public class DebugTab : Window, ITab, IUiService
DrawDebugResidentResources(); 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> /// <summary> Draw resources with unusual reference count. </summary>
private unsafe void DrawResourceProblems() private unsafe void DrawResourceProblems()
{ {