Skip to content

Commit

Permalink
Add SCD handling and crc cache visualization.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ottermandias committed Nov 17, 2024
1 parent 5973803 commit 7ab5299
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 22 deletions.
3 changes: 3 additions & 0 deletions Penumbra.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
8 changes: 4 additions & 4 deletions Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -140,7 +140,7 @@ private void ResourceHandler(ref ResourceCategory category, ref ResourceType typ
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;
Expand Down
62 changes: 45 additions & 17 deletions Penumbra/Interop/Hooks/ResourceLoading/TexMdlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

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.
Expand Down Expand Up @@ -42,7 +42,7 @@ public byte GetLod(TextureResourceHandle* handle)

private readonly LodService _lodService;

public TexMdlService(IGameInteropProvider interop)
public TexMdlScdService(IGameInteropProvider interop)
{
interop.InitializeFromAttributes(this);
_lodService = new LodService(interop);
Expand All @@ -52,15 +52,17 @@ public TexMdlService(IGameInteropProvider interop)
_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>
public void AddCrc(ResourceType type, FullPath? path)
{
_ = 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,
};
}
Expand All @@ -70,28 +72,51 @@ public void Dispose()
_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);

[Signature(Sigs.CheckFileState, DetourName = nameof(CheckFileStateDetour))]
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.
Expand All @@ -100,14 +125,17 @@ public void Dispose()
/// </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}.");
Expand All @@ -128,10 +156,10 @@ private nint CheckFileStateDetour(nint ptr, ulong crc64)

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)
Expand Down
29 changes: 28 additions & 1 deletion Penumbra/UI/Tabs/Debug/DebugTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -112,7 +113,7 @@ public DebugTab(PerformanceTracker performance, Configuration config, Collection
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;
Expand Down Expand Up @@ -150,6 +151,7 @@ public DebugTab(PerformanceTracker performance, Configuration config, Collection
_crashHandlerPanel = crashHandlerPanel;
_texHeaderDrawer = texHeaderDrawer;
_hookOverrides = hookOverrides;
_texMdlScdService = texMdlScdService;
_objects = objects;
_clientState = clientState;
}
Expand Down Expand Up @@ -183,6 +185,7 @@ public void DrawContent()
DrawDebugCharacterUtility();
DrawShaderReplacementFixer();
DrawData();
DrawCrcCache();
DrawResourceProblems();
_hookOverrides.Draw();
DrawPlayerModelInfo();
Expand Down Expand Up @@ -1021,6 +1024,30 @@ private unsafe void DrawGlobalVariableInfo()
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()
{
Expand Down

0 comments on commit 7ab5299

Please sign in to comment.