Fix some issues with ResourceWatcher.

This commit is contained in:
Ottermandias 2023-03-29 14:42:34 +02:00
parent a8000fbf14
commit 185be81e73
8 changed files with 519 additions and 529 deletions

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData.Actors;
namespace Penumbra.Collections; namespace Penumbra.Collections;
@ -36,30 +37,6 @@ public readonly struct ResolveData
public override string ToString() public override string ToString()
=> ModCollection.Name; => ModCollection.Name;
public unsafe string AssociatedName()
{
if (AssociatedGameObject == IntPtr.Zero)
return "no associated object.";
try
{
var id = Penumbra.Actors.FromObject((GameObject*)AssociatedGameObject, out _, false, true, true);
if (id.IsValid)
{
var name = id.ToString();
var parts = name.Split(' ', 3);
return string.Join(" ",
parts.Length != 3 ? parts.Select(n => $"{n[0]}.") : parts[..2].Select(n => $"{n[0]}.").Append(parts[2]));
}
}
catch
{
// ignored
}
return $"0x{AssociatedGameObject:X}";
}
} }
public static class ResolveDataExtensions public static class ResolveDataExtensions

View file

@ -64,7 +64,7 @@ public class Configuration : IPluginConfiguration, ISavable
public ResourceTypeFlag ResourceWatcherResourceTypes { get; set; } = ResourceExtensions.AllResourceTypes; public ResourceTypeFlag ResourceWatcherResourceTypes { get; set; } = ResourceExtensions.AllResourceTypes;
public ResourceCategoryFlag ResourceWatcherResourceCategories { get; set; } = ResourceExtensions.AllResourceCategories; public ResourceCategoryFlag ResourceWatcherResourceCategories { get; set; } = ResourceExtensions.AllResourceCategories;
public ResourceWatcher.RecordType ResourceWatcherRecordTypes { get; set; } = ResourceWatcher.AllRecords; public RecordType ResourceWatcherRecordTypes { get; set; } = ResourceWatcher.AllRecords;
[JsonConverter(typeof(SortModeConverter))] [JsonConverter(typeof(SortModeConverter))]

View file

@ -66,7 +66,7 @@ public unsafe class ResourceLoader : IDisposable
_fileReadService.ReadSqPack -= ReadSqPackDetour; _fileReadService.ReadSqPack -= ReadSqPackDetour;
} }
private void ResourceHandler(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path, private void ResourceHandler(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path, Utf8GamePath original,
GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue) GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue)
{ {
if (returnValue != null) if (returnValue != null)
@ -86,9 +86,10 @@ public unsafe class ResourceLoader : IDisposable
_texMdlService.AddCrc(type, resolvedPath); _texMdlService.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;
path = p; path = p;
returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, parameters); returnValue = _resources.GetOriginalResource(sync, category, type, hash, path.Path, parameters);
ResourceLoaded?.Invoke(returnValue, p, resolvedPath.Value, data); ResourceLoaded?.Invoke(returnValue, oldPath, resolvedPath.Value, data);
} }
private void ReadSqPackDetour(SeFileDescriptor* fileDescriptor, ref int priority, ref bool isSync, ref byte? returnValue) private void ReadSqPackDetour(SeFileDescriptor* fileDescriptor, ref int priority, ref bool isSync, ref byte? returnValue)

View file

@ -60,7 +60,7 @@ public unsafe class ResourceService : IDisposable
/// <param name="parameters">Mainly used for SCD streaming, can be null.</param> /// <param name="parameters">Mainly used for SCD streaming, can be null.</param>
/// <param name="sync">Whether to request the resource synchronously or asynchronously.</param> /// <param name="sync">Whether to request the resource synchronously or asynchronously.</param>
/// <param name="returnValue">The returned resource handle. If this is not null, calling original will be skipped. </param> /// <param name="returnValue">The returned resource handle. If this is not null, calling original will be skipped. </param>
public delegate void GetResourcePreDelegate(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path, public delegate void GetResourcePreDelegate(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path, Utf8GamePath original,
GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue); GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue);
/// <summary> <inheritdoc cref="GetResourcePreDelegate"/> <para/> /// <summary> <inheritdoc cref="GetResourcePreDelegate"/> <para/>
@ -104,7 +104,7 @@ public unsafe class ResourceService : IDisposable
} }
ResourceHandle* returnValue = null; ResourceHandle* returnValue = null;
ResourceRequested?.Invoke(ref *categoryId, ref *resourceType, ref *resourceHash, ref gamePath, pGetResParams, ref isSync, ResourceRequested?.Invoke(ref *categoryId, ref *resourceType, ref *resourceHash, ref gamePath, gamePath, pGetResParams, ref isSync,
ref returnValue); ref returnValue);
if (returnValue != null) if (returnValue != null)
return returnValue; return returnValue;

View file

@ -7,13 +7,21 @@ using Penumbra.String;
namespace Penumbra.UI; namespace Penumbra.UI;
public partial class ResourceWatcher [Flags]
public enum RecordType : byte
{ {
private unsafe struct Record Request = 0x01,
ResourceLoad = 0x02,
FileLoad = 0x04,
Destruction = 0x08,
}
internal unsafe struct Record
{ {
public DateTime Time; public DateTime Time;
public ByteString Path; public ByteString Path;
public ByteString OriginalPath; public ByteString OriginalPath;
public string AssociatedGameObject;
public ModCollection? Collection; public ModCollection? Collection;
public ResourceHandle* Handle; public ResourceHandle* Handle;
public ResourceTypeFlag ResourceType; public ResourceTypeFlag ResourceType;
@ -39,9 +47,10 @@ public partial class ResourceWatcher
Synchronously = sync, Synchronously = sync,
ReturnValue = OptionalBool.Null, ReturnValue = OptionalBool.Null,
CustomLoad = OptionalBool.Null, CustomLoad = OptionalBool.Null,
AssociatedGameObject = string.Empty,
}; };
public static Record CreateDefaultLoad( ByteString path, ResourceHandle* handle, ModCollection collection ) public static Record CreateDefaultLoad(ByteString path, ResourceHandle* handle, ModCollection collection, string associatedGameObject)
{ {
path = path.IsOwned ? path : path.Clone(); path = path.IsOwned ? path : path.Clone();
return new Record return new Record
@ -58,10 +67,12 @@ public partial class ResourceWatcher
Synchronously = OptionalBool.Null, Synchronously = OptionalBool.Null,
ReturnValue = OptionalBool.Null, ReturnValue = OptionalBool.Null,
CustomLoad = false, CustomLoad = false,
AssociatedGameObject = associatedGameObject,
}; };
} }
public static Record CreateLoad( ByteString path, ByteString originalPath, ResourceHandle* handle, ModCollection collection ) public static Record CreateLoad(ByteString path, ByteString originalPath, ResourceHandle* handle, ModCollection collection,
string associatedGameObject)
=> new() => new()
{ {
Time = DateTime.UtcNow, Time = DateTime.UtcNow,
@ -76,6 +87,7 @@ public partial class ResourceWatcher
Synchronously = OptionalBool.Null, Synchronously = OptionalBool.Null,
ReturnValue = OptionalBool.Null, ReturnValue = OptionalBool.Null,
CustomLoad = true, CustomLoad = true,
AssociatedGameObject = associatedGameObject,
}; };
public static Record CreateDestruction(ResourceHandle* handle) public static Record CreateDestruction(ResourceHandle* handle)
@ -115,4 +127,3 @@ public partial class ResourceWatcher
CustomLoad = custom, CustomLoad = custom,
}; };
} }
}

View file

@ -1,17 +0,0 @@
using System;
namespace Penumbra.UI;
public partial class ResourceWatcher
{
[Flags]
public enum RecordType : byte
{
Request = 0x01,
ResourceLoad = 0x02,
FileLoad = 0x04,
Destruction = 0x08,
}
public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction;
}

View file

@ -13,27 +13,24 @@ using Penumbra.String;
namespace Penumbra.UI; namespace Penumbra.UI;
public partial class ResourceWatcher internal sealed class ResourceWatcherTable : Table<Record>
{ {
private sealed class Table : Table< Record > public ResourceWatcherTable(Configuration config, ICollection<Record> records)
{ : base("##records",
private static readonly PathColumn Path = new() { Label = "Path" }; records,
private static readonly RecordTypeColumn RecordType = new() { Label = "Record" }; new PathColumn { Label = "Path" },
private static readonly DateColumn Date = new() { Label = "Time" }; new RecordTypeColumn(config) { Label = "Record" },
new CollectionColumn { Label = "Collection" },
private static readonly CollectionColumn Coll = new() { Label = "Collection" }; new ObjectColumn { Label = "Game Object" },
private static readonly CustomLoadColumn Custom = new() { Label = "Custom" }; new CustomLoadColumn { Label = "Custom" },
private static readonly SynchronousLoadColumn Sync = new() { Label = "Sync" }; new SynchronousLoadColumn { Label = "Sync" },
new OriginalPathColumn { Label = "Original Path" },
private static readonly OriginalPathColumn Orig = new() { Label = "Original Path" }; new ResourceCategoryColumn { Label = "Category" },
private static readonly ResourceCategoryColumn Cat = new() { Label = "Category" }; new ResourceTypeColumn { Label = "Type" },
private static readonly ResourceTypeColumn Type = new() { Label = "Type" }; new HandleColumn { Label = "Resource" },
new RefCountColumn { Label = "#Ref" },
private static readonly HandleColumn Handle = new() { Label = "Resource" }; new DateColumn { Label = "Time" }
private static readonly RefCountColumn Ref = new() { Label = "#Ref" }; )
public Table( ICollection< Record > records )
: base( "##records", records, Path, RecordType, Coll, Custom, Sync, Orig, Cat, Type, Handle, Ref, Date )
{ } { }
public void Reset() public void Reset()
@ -51,7 +48,7 @@ public partial class ResourceWatcher
=> lhs.Path.CompareTo(rhs.Path); => lhs.Path.CompareTo(rhs.Path);
public override void DrawColumn(Record item, int _) public override void DrawColumn(Record item, int _)
=> DrawByteString( item.Path, 290 * UiHelpers.Scale ); => DrawByteString(item.Path, 280 * UiHelpers.Scale);
} }
private static unsafe void DrawByteString(ByteString path, float length) private static unsafe void DrawByteString(ByteString path, float length)
@ -81,21 +78,22 @@ public partial class ResourceWatcher
ImGuiNative.igTextUnformatted(shortPath.Path, shortPath.Path + shortPath.Length); ImGuiNative.igTextUnformatted(shortPath.Path, shortPath.Path + shortPath.Length);
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
{
ImGuiNative.igSetClipboardText(path.Path); ImGuiNative.igSetClipboardText(path.Path);
}
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{
ImGuiNative.igSetTooltip(path.Path); ImGuiNative.igSetTooltip(path.Path);
} }
} }
}
private sealed class RecordTypeColumn : ColumnFlags<RecordType, Record> private sealed class RecordTypeColumn : ColumnFlags<RecordType, Record>
{ {
public RecordTypeColumn() private readonly Configuration _config;
=> AllFlags = AllRecords;
public RecordTypeColumn(Configuration config)
{
AllFlags = ResourceWatcher.AllRecords;
_config = config;
}
public override float Width public override float Width
=> 80 * UiHelpers.Scale; => 80 * UiHelpers.Scale;
@ -104,18 +102,14 @@ public partial class ResourceWatcher
=> FilterValue.HasFlag(item.RecordType); => FilterValue.HasFlag(item.RecordType);
public override RecordType FilterValue public override RecordType FilterValue
=> Penumbra.Config.ResourceWatcherRecordTypes; => _config.ResourceWatcherRecordTypes;
protected override void SetValue(RecordType value, bool enable) protected override void SetValue(RecordType value, bool enable)
{ {
if (enable) if (enable)
{ _config.ResourceWatcherRecordTypes |= value;
Penumbra.Config.ResourceWatcherRecordTypes |= value;
}
else else
{ _config.ResourceWatcherRecordTypes &= ~value;
Penumbra.Config.ResourceWatcherRecordTypes &= ~value;
}
Penumbra.Config.Save(); Penumbra.Config.Save();
} }
@ -124,10 +118,10 @@ public partial class ResourceWatcher
{ {
ImGui.TextUnformatted(item.RecordType switch ImGui.TextUnformatted(item.RecordType switch
{ {
ResourceWatcher.RecordType.Request => "REQ", RecordType.Request => "REQ",
ResourceWatcher.RecordType.ResourceLoad => "LOAD", RecordType.ResourceLoad => "LOAD",
ResourceWatcher.RecordType.FileLoad => "FILE", RecordType.FileLoad => "FILE",
ResourceWatcher.RecordType.Destruction => "DEST", RecordType.Destruction => "DEST",
_ => string.Empty, _ => string.Empty,
}); });
} }
@ -155,6 +149,15 @@ public partial class ResourceWatcher
=> item.Collection?.Name ?? string.Empty; => item.Collection?.Name ?? string.Empty;
} }
private sealed class ObjectColumn : ColumnString<Record>
{
public override float Width
=> 200 * UiHelpers.Scale;
public override string ToName(Record item)
=> item.AssociatedGameObject;
}
private sealed class OriginalPathColumn : ColumnString<Record> private sealed class OriginalPathColumn : ColumnString<Record>
{ {
public override float Width public override float Width
@ -187,13 +190,9 @@ public partial class ResourceWatcher
protected override void SetValue(ResourceCategoryFlag value, bool enable) protected override void SetValue(ResourceCategoryFlag value, bool enable)
{ {
if (enable) if (enable)
{
Penumbra.Config.ResourceWatcherResourceCategories |= value; Penumbra.Config.ResourceWatcherResourceCategories |= value;
}
else else
{
Penumbra.Config.ResourceWatcherResourceCategories &= ~value; Penumbra.Config.ResourceWatcherResourceCategories &= ~value;
}
Penumbra.Config.Save(); Penumbra.Config.Save();
} }
@ -210,10 +209,8 @@ public partial class ResourceWatcher
{ {
AllFlags = Enum.GetValues<ResourceTypeFlag>().Aggregate((v, f) => v | f); AllFlags = Enum.GetValues<ResourceTypeFlag>().Aggregate((v, f) => v | f);
for (var i = 0; i < Names.Length; ++i) for (var i = 0; i < Names.Length; ++i)
{
Names[i] = Names[i].ToLowerInvariant(); Names[i] = Names[i].ToLowerInvariant();
} }
}
public override float Width public override float Width
=> 50 * UiHelpers.Scale; => 50 * UiHelpers.Scale;
@ -227,13 +224,9 @@ public partial class ResourceWatcher
protected override void SetValue(ResourceTypeFlag value, bool enable) protected override void SetValue(ResourceTypeFlag value, bool enable)
{ {
if (enable) if (enable)
{
Penumbra.Config.ResourceWatcherResourceTypes |= value; Penumbra.Config.ResourceWatcherResourceTypes |= value;
}
else else
{
Penumbra.Config.ResourceWatcherResourceTypes &= ~value; Penumbra.Config.ResourceWatcherResourceTypes &= ~value;
}
Penumbra.Config.Save(); Penumbra.Config.Save();
} }
@ -292,14 +285,10 @@ public partial class ResourceWatcher
protected override void SetValue(BoolEnum value, bool enable) protected override void SetValue(BoolEnum value, bool enable)
{ {
if (enable) if (enable)
{
_filter |= value; _filter |= value;
}
else else
{
_filter &= ~value; _filter &= ~value;
} }
}
protected static void DrawColumn(OptionalBool b) protected static void DrawColumn(OptionalBool b)
{ {
@ -349,4 +338,3 @@ public partial class ResourceWatcher
=> lhs.RefCount.CompareTo(rhs.RefCount); => lhs.RefCount.CompareTo(rhs.RefCount);
} }
} }
}

View file

@ -1,18 +1,19 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Dalamud.Interface; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImGuiNET; using ImGuiNET;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
@ -22,23 +23,26 @@ namespace Penumbra.UI;
public partial class ResourceWatcher : IDisposable, ITab public partial class ResourceWatcher : IDisposable, ITab
{ {
public const int DefaultMaxEntries = 1024; public const int DefaultMaxEntries = 1024;
public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction;
private readonly Configuration _config; private readonly Configuration _config;
private readonly ResourceService _resources; private readonly ResourceService _resources;
private readonly ResourceLoader _loader; private readonly ResourceLoader _loader;
private readonly ActorService _actors;
private readonly List<Record> _records = new(); private readonly List<Record> _records = new();
private readonly ConcurrentQueue<Record> _newRecords = new(); private readonly ConcurrentQueue<Record> _newRecords = new();
private readonly Table _table; private readonly ResourceWatcherTable _table;
private string _logFilter = string.Empty; private string _logFilter = string.Empty;
private Regex? _logRegex; private Regex? _logRegex;
private int _newMaxEntries; private int _newMaxEntries;
public unsafe ResourceWatcher(Configuration config, ResourceService resources, ResourceLoader loader) public unsafe ResourceWatcher(ActorService actors, Configuration config, ResourceService resources, ResourceLoader loader)
{ {
_actors = actors;
_config = config; _config = config;
_resources = resources; _resources = resources;
_loader = loader; _loader = loader;
_table = new Table(_records); _table = new ResourceWatcherTable(config, _records);
_resources.ResourceRequested += OnResourceRequested; _resources.ResourceRequested += OnResourceRequested;
_resources.ResourceHandleDestructor += OnResourceDestroyed; _resources.ResourceHandleDestructor += OnResourceDestroyed;
_loader.ResourceLoaded += OnResourceLoaded; _loader.ResourceLoaded += OnResourceLoaded;
@ -75,8 +79,8 @@ public partial class ResourceWatcher : IDisposable, ITab
var isEnabled = _config.EnableResourceWatcher; var isEnabled = _config.EnableResourceWatcher;
if (ImGui.Checkbox("Enable", ref isEnabled)) if (ImGui.Checkbox("Enable", ref isEnabled))
{ {
Penumbra.Config.EnableResourceWatcher = isEnabled; _config.EnableResourceWatcher = isEnabled;
Penumbra.Config.Save(); _config.Save();
} }
ImGui.SameLine(); ImGui.SameLine();
@ -89,16 +93,16 @@ public partial class ResourceWatcher : IDisposable, ITab
var onlyMatching = _config.OnlyAddMatchingResources; var onlyMatching = _config.OnlyAddMatchingResources;
if (ImGui.Checkbox("Store Only Matching", ref onlyMatching)) if (ImGui.Checkbox("Store Only Matching", ref onlyMatching))
{ {
Penumbra.Config.OnlyAddMatchingResources = onlyMatching; _config.OnlyAddMatchingResources = onlyMatching;
Penumbra.Config.Save(); _config.Save();
} }
ImGui.SameLine(); ImGui.SameLine();
var writeToLog = _config.EnableResourceLogging; var writeToLog = _config.EnableResourceLogging;
if (ImGui.Checkbox("Write to Log", ref writeToLog)) if (ImGui.Checkbox("Write to Log", ref writeToLog))
{ {
Penumbra.Config.EnableResourceLogging = writeToLog; _config.EnableResourceLogging = writeToLog;
Penumbra.Config.Save(); _config.Save();
} }
ImGui.SameLine(); ImGui.SameLine();
@ -137,8 +141,8 @@ public partial class ResourceWatcher : IDisposable, ITab
if (config) if (config)
{ {
Penumbra.Config.ResourceLoggingFilter = newString; _config.ResourceLoggingFilter = newString;
Penumbra.Config.Save(); _config.Save();
} }
} }
@ -168,20 +172,21 @@ public partial class ResourceWatcher : IDisposable, ITab
return; return;
_newMaxEntries = Math.Max(16, _newMaxEntries); _newMaxEntries = Math.Max(16, _newMaxEntries);
if (_newMaxEntries != maxEntries) if (_newMaxEntries == maxEntries)
{ return;
_config.MaxResourceWatcherRecords = _newMaxEntries; _config.MaxResourceWatcherRecords = _newMaxEntries;
Penumbra.Config.Save(); _config.Save();
if (_newMaxEntries > _records.Count) if (_newMaxEntries > _records.Count)
_records.RemoveRange(0, _records.Count - _newMaxEntries); _records.RemoveRange(0, _records.Count - _newMaxEntries);
} }
}
private void UpdateRecords() private void UpdateRecords()
{ {
var count = _newRecords.Count; var count = _newRecords.Count;
if (count > 0) if (count <= 0)
{ return;
while (_newRecords.TryDequeue(out var rec) && count-- > 0) while (_newRecords.TryDequeue(out var rec) && count-- > 0)
_records.Add(rec); _records.Add(rec);
@ -190,22 +195,22 @@ public partial class ResourceWatcher : IDisposable, ITab
_table.Reset(); _table.Reset();
} }
}
private unsafe void OnResourceRequested(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path, private unsafe void OnResourceRequested(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path,
Utf8GamePath original,
GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue) GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue)
{ {
if (_config.EnableResourceLogging && FilterMatch(path.Path, out var match)) if (_config.EnableResourceLogging && FilterMatch(original.Path, out var match))
Penumbra.Log.Information($"[ResourceLoader] [REQ] {match} was requested {(sync ? "synchronously." : "asynchronously.")}"); Penumbra.Log.Information($"[ResourceLoader] [REQ] {match} was requested {(sync ? "synchronously." : "asynchronously.")}");
if (_config.EnableResourceWatcher) if (!_config.EnableResourceWatcher)
{ return;
var record = Record.CreateRequest(path.Path, sync);
var record = Record.CreateRequest(original.Path, sync);
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record)) if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
_newRecords.Enqueue(record); _newRecords.Enqueue(record);
} }
}
private unsafe void OnResourceLoaded(ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData data) private unsafe void OnResourceLoaded(ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData data)
{ {
@ -220,20 +225,19 @@ public partial class ResourceWatcher : IDisposable, ITab
{ {
var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name; var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name;
Penumbra.Log.Information( Penumbra.Log.Information(
$"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.AnonymizedName} for {data.AssociatedName()} (Refcount {handle->RefCount}) "); $"[ResourceLoader] [LOAD] [{handle->FileType}] Loaded {pathString} to 0x{(ulong)handle:X} using collection {data.ModCollection.AnonymizedName} for {Name(data, "no associated object.")} (Refcount {handle->RefCount}) ");
} }
} }
if (_config.EnableResourceWatcher) if (!_config.EnableResourceWatcher)
{ return;
var record = manipulatedPath == null var record = manipulatedPath == null
? Record.CreateDefaultLoad(path.Path, handle, data.ModCollection) ? Record.CreateDefaultLoad(path.Path, handle, data.ModCollection, Name(data))
: Record.CreateLoad(path.Path, manipulatedPath.Value.InternalName, handle, : Record.CreateLoad(manipulatedPath.Value.InternalName, path.Path, handle, data.ModCollection, Name(data));
data.ModCollection);
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record)) if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
_newRecords.Enqueue(record); _newRecords.Enqueue(record);
} }
}
private unsafe void OnFileLoaded(ResourceHandle* resource, ByteString path, bool success, bool custom, ByteString _) private unsafe void OnFileLoaded(ResourceHandle* resource, ByteString path, bool success, bool custom, ByteString _)
{ {
@ -241,13 +245,13 @@ public partial class ResourceWatcher : IDisposable, ITab
Penumbra.Log.Information( Penumbra.Log.Information(
$"[ResourceLoader] [FILE] [{resource->FileType}] Loading {match} from {(custom ? "local files" : "SqPack")} into 0x{(ulong)resource:X} returned {success}."); $"[ResourceLoader] [FILE] [{resource->FileType}] Loading {match} from {(custom ? "local files" : "SqPack")} into 0x{(ulong)resource:X} returned {success}.");
if (_config.EnableResourceWatcher) if (!_config.EnableResourceWatcher)
{ return;
var record = Record.CreateFileLoad(path, resource, success, custom); var record = Record.CreateFileLoad(path, resource, success, custom);
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record)) if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
_newRecords.Enqueue(record); _newRecords.Enqueue(record);
} }
}
private unsafe void OnResourceDestroyed(ResourceHandle* resource) private unsafe void OnResourceDestroyed(ResourceHandle* resource)
{ {
@ -255,11 +259,37 @@ public partial class ResourceWatcher : IDisposable, ITab
Penumbra.Log.Information( Penumbra.Log.Information(
$"[ResourceLoader] [DEST] [{resource->FileType}] Destroyed {match} at 0x{(ulong)resource:X}."); $"[ResourceLoader] [DEST] [{resource->FileType}] Destroyed {match} at 0x{(ulong)resource:X}.");
if (_config.EnableResourceWatcher) if (!_config.EnableResourceWatcher)
{ return;
var record = Record.CreateDestruction(resource); var record = Record.CreateDestruction(resource);
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record)) if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
_newRecords.Enqueue(record); _newRecords.Enqueue(record);
} }
public unsafe string Name(ResolveData resolve, string none = "")
{
if (resolve.AssociatedGameObject == IntPtr.Zero || !_actors.Valid)
return none;
try
{
var id = _actors.AwaitedService.FromObject((GameObject*)resolve.AssociatedGameObject, out _, false, true, true);
if (id.IsValid)
{
if (id.Type is not (IdentifierType.Player or IdentifierType.Owned))
return id.ToString();
var parts = id.ToString().Split(' ', 3);
return string.Join(" ",
parts.Length != 3 ? parts.Select(n => $"{n[0]}.") : parts[..2].Select(n => $"{n[0]}.").Append(parts[2]));
}
}
catch
{
// ignored
}
return $"0x{resolve.AssociatedGameObject:X}";
} }
} }