diff --git a/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs b/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs index f9b8ff60..d5e41b56 100644 --- a/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs +++ b/Penumbra/Interop/Hooks/ResourceLoading/ResourceLoader.cs @@ -1,4 +1,3 @@ -using System.IO; using FFXIVClientStructs.FFXIV.Client.System.Resource; using OtterGui.Services; using Penumbra.Api.Enums; @@ -168,7 +167,7 @@ public unsafe class ResourceLoader : IDisposable, IService if (resolvedPath == null || !Utf8GamePath.FromByteString(resolvedPath.Value.InternalName, out var p)) { - returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, parameters, original: original); + returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, original, parameters); TrackResourceLoad(returnValue, original); ResourceLoaded?.Invoke(returnValue, path, resolvedPath, data); return; @@ -179,7 +178,7 @@ public unsafe class ResourceLoader : IDisposable, IService hash = ComputeHash(resolvedPath.Value.InternalName, parameters); var oldPath = path; path = p; - returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, parameters, original: original); + returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, original, parameters); TrackResourceLoad(returnValue, original); ResourceLoaded?.Invoke(returnValue, oldPath, resolvedPath.Value, data); } @@ -194,7 +193,7 @@ public unsafe class ResourceLoader : IDisposable, IService private void ResourceStateUpdatedHandler(ResourceHandle* handle, Utf8GamePath syncOriginal, (byte, LoadState) previousState, ref uint returnValue) { - if (handle->UnkState != 2 || handle->LoadState < LoadState.Success || previousState.Item1 == 2 && previousState.Item2 >= LoadState.Success) + if (handle->UnkState != 2 || handle->LoadState < LoadState.Success || previousState is { Item1: 2, Item2: >= LoadState.Success }) return; if (!_ongoingLoads.TryRemove((nint)handle, out var asyncOriginal)) @@ -205,7 +204,7 @@ public unsafe class ResourceLoader : IDisposable, IService Penumbra.Log.Warning($"[ResourceLoader] Resource original paths inconsistency: 0x{(nint)handle:X}, of path {path}, sync original {syncOriginal}, async original {asyncOriginal}."); var original = !asyncOriginal.IsEmpty ? asyncOriginal : syncOriginal; - // Penumbra.Log.Information($"[ResourceLoader] Resource is complete: 0x{(nint)handle:X}, of path {path}, original {original}, state {previousState.Item1}:{previousState.Item2} -> {handle->UnkState}:{handle->LoadState}, sync: {asyncOriginal.IsEmpty}"); + Penumbra.Log.Excessive($"[ResourceLoader] Resource is complete: 0x{(nint)handle:X}, of path {path}, original {original}, state {previousState.Item1}:{previousState.Item2} -> {handle->UnkState}:{handle->LoadState}, sync: {asyncOriginal.IsEmpty}"); if (PathDataHandler.Split(path.AsSpan(), out var actualPath, out var additionalData)) ResourceComplete?.Invoke(handle, new CiByteString(actualPath), original, additionalData, !asyncOriginal.IsEmpty); else @@ -223,7 +222,7 @@ public unsafe class ResourceLoader : IDisposable, IService var path = handle->CsHandle.FileName; var original = asyncOriginal.IsEmpty ? syncOriginal : asyncOriginal; - // Penumbra.Log.Information($"[ResourceLoader] Resource is about to be complete: 0x{(nint)handle:X}, of path {path}, original {original}"); + Penumbra.Log.Excessive($"[ResourceLoader] Resource is about to be complete: 0x{(nint)handle:X}, of path {path}, original {original}"); if (PathDataHandler.Split(path.AsSpan(), out var actualPath, out var additionalData)) BeforeResourceComplete?.Invoke(handle, new CiByteString(actualPath), original, additionalData, !asyncOriginal.IsEmpty); else diff --git a/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs b/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs index 238ed70f..e90b4575 100644 --- a/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs +++ b/Penumbra/Interop/Hooks/ResourceLoading/ResourceService.cs @@ -125,15 +125,13 @@ public unsafe class ResourceService : IDisposable, IRequiredService if (returnValue != null) return returnValue; - return GetOriginalResource(isSync, *categoryId, *resourceType, *resourceHash, gamePath.Path, pGetResParams, isUnk, unk8, unk9, original); + return GetOriginalResource(isSync, *categoryId, *resourceType, *resourceHash, gamePath.Path, original, pGetResParams, isUnk, unk8, unk9); } /// Call the original GetResource function. - public ResourceHandle* GetOriginalResource(bool sync, ResourceCategory categoryId, ResourceType type, int hash, CiByteString path, - GetResourceParameters* resourceParameters = null, byte unk = 0, nint unk8 = 0, uint unk9 = 0, Utf8GamePath original = default) + public ResourceHandle* GetOriginalResource(bool sync, ResourceCategory categoryId, ResourceType type, int hash, CiByteString path, Utf8GamePath original, + GetResourceParameters* resourceParameters = null, byte unk = 0, nint unk8 = 0, uint unk9 = 0) { - if (original.Path is null) // i. e. if original is default - Utf8GamePath.FromByteString(path, out original); var previous = _currentGetResourcePath.Value; try { @@ -187,7 +185,7 @@ public unsafe class ResourceService : IDisposable, IRequiredService private uint UpdateResourceStateDetour(ResourceHandle* handle, byte offFileThread) { var previousState = (handle->UnkState, handle->LoadState); - var syncOriginal = _currentGetResourcePath.IsValueCreated ? _currentGetResourcePath.Value! : Utf8GamePath.Empty; + var syncOriginal = _currentGetResourcePath.IsValueCreated ? _currentGetResourcePath.Value : Utf8GamePath.Empty; ResourceStateUpdating?.Invoke(handle, syncOriginal); var ret = _updateResourceStateHook.OriginalDisposeSafe(handle, offFileThread); ResourceStateUpdated?.Invoke(handle, syncOriginal, previousState, ref ret); diff --git a/Penumbra/Interop/Processing/FilePostProcessService.cs b/Penumbra/Interop/Processing/FilePostProcessService.cs index a27f6d45..71340178 100644 --- a/Penumbra/Interop/Processing/FilePostProcessService.cs +++ b/Penumbra/Interop/Processing/FilePostProcessService.cs @@ -23,20 +23,17 @@ public unsafe class FilePostProcessService : IRequiredService, IDisposable { _resourceLoader = resourceLoader; _processors = services.GetServicesImplementing().ToFrozenDictionary(s => s.Type, s => s); - _resourceLoader.BeforeResourceComplete += OnResourceComplete; + _resourceLoader.BeforeResourceComplete += OnBeforeResourceComplete; } public void Dispose() { - _resourceLoader.BeforeResourceComplete -= OnResourceComplete; + _resourceLoader.BeforeResourceComplete -= OnBeforeResourceComplete; } - private void OnResourceComplete(ResourceHandle* resource, CiByteString path, Utf8GamePath original, + private void OnBeforeResourceComplete(ResourceHandle* resource, CiByteString path, Utf8GamePath original, ReadOnlySpan additionalData, bool isAsync) { - if (resource->LoadState != LoadState.Success) - return; - if (_processors.TryGetValue(resource->FileType, out var processor)) processor.PostProcess(resource, original.Path, additionalData); } diff --git a/Penumbra/UI/ResourceWatcher/Record.cs b/Penumbra/UI/ResourceWatcher/Record.cs index 13a71656..8ab96f4b 100644 --- a/Penumbra/UI/ResourceWatcher/Record.cs +++ b/Penumbra/UI/ResourceWatcher/Record.cs @@ -143,11 +143,11 @@ internal unsafe struct Record Crc64 = 0, }; - public static Record CreateResourceComplete(CiByteString path, ResourceHandle* handle, Utf8GamePath originalPath) + public static Record CreateResourceComplete(CiByteString path, ResourceHandle* handle, Utf8GamePath originalPath, ReadOnlySpan additionalData) => new() { Time = DateTime.UtcNow, - Path = path.IsOwned ? path : path.Clone(), + Path = CombinedPath(path, additionalData), OriginalPath = originalPath.Path.IsOwned ? originalPath.Path : originalPath.Path.Clone(), Collection = null, Handle = handle, @@ -162,4 +162,17 @@ internal unsafe struct Record LoadState = handle->LoadState, Crc64 = 0, }; + + private static CiByteString CombinedPath(CiByteString path, ReadOnlySpan additionalData) + { + if (additionalData.Length is 0) + return path.IsOwned ? path : path.Clone(); + + fixed (byte* ptr = additionalData) + { + // If a path has additional data and is split, it is always in the form of |{additionalData}|{path}, + // so we can just read from the start of additional data - 1 and sum their length +2 for the pipes. + return new CiByteString(new ReadOnlySpan(ptr - 1, additionalData.Length + 2 + path.Length)).Clone(); + } + } } diff --git a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs index 53d7e79d..94bd4307 100644 --- a/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs +++ b/Penumbra/UI/ResourceWatcher/ResourceWatcher.cs @@ -257,7 +257,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService _newRecords.Enqueue(record); } - private unsafe void OnResourceComplete(ResourceHandle* resource, CiByteString path, Utf8GamePath original, ReadOnlySpan _, bool isAsync) + private unsafe void OnResourceComplete(ResourceHandle* resource, CiByteString path, Utf8GamePath original, ReadOnlySpan additionalData, bool isAsync) { if (!isAsync) return; @@ -269,7 +269,7 @@ public sealed class ResourceWatcher : IDisposable, ITab, IUiService if (!_ephemeral.EnableResourceWatcher) return; - var record = Record.CreateResourceComplete(path, resource, original); + var record = Record.CreateResourceComplete(path, resource, original, additionalData); if (!_ephemeral.OnlyAddMatchingResources || _table.WouldBeVisible(record)) _newRecords.Enqueue(record); } diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index 5dc203c2..8f76a54a 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -1104,7 +1104,7 @@ public class DebugTab : Window, ITab, IUiService private unsafe void DrawResourceLoader() { - if (!ImGui.CollapsingHeader("Resource Loader")) + if (!ImUtf8.CollapsingHeader("Resource Loader"u8)) return; var ongoingLoads = _resourceLoader.OngoingLoads; @@ -1125,12 +1125,9 @@ public class DebugTab : Window, ITab, IUiService foreach (var (handle, original) in ongoingLoads) { - ImGui.TableNextColumn(); - ImUtf8.Text($"0x{handle:X}"); - ImGui.TableNextColumn(); - ImUtf8.Text(((ResourceHandle*)handle)->CsHandle.FileName); - ImGui.TableNextColumn(); - ImUtf8.Text(original.Path.Span); + ImUtf8.DrawTableColumn($"0x{handle:X}"); + ImUtf8.DrawTableColumn(((ResourceHandle*)handle)->CsHandle.FileName); + ImUtf8.DrawTableColumn(original.Path.Span); } }