mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 21:24:18 +01:00
Rework Interop/Loader Services.
This commit is contained in:
parent
99fd4b7806
commit
0df12a34cb
32 changed files with 1137 additions and 1421 deletions
|
|
@ -1,12 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Interop.Loader;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.String;
|
||||
|
|
@ -19,38 +23,38 @@ public partial class ResourceWatcher : IDisposable, ITab
|
|||
{
|
||||
public const int DefaultMaxEntries = 1024;
|
||||
|
||||
private readonly ResourceLoader _loader;
|
||||
private readonly List< Record > _records = new();
|
||||
private readonly ConcurrentQueue< Record > _newRecords = new();
|
||||
private readonly Table _table;
|
||||
private bool _writeToLog;
|
||||
private bool _isEnabled;
|
||||
private string _logFilter = string.Empty;
|
||||
private Regex? _logRegex;
|
||||
private int _maxEntries;
|
||||
private int _newMaxEntries;
|
||||
private readonly Configuration _config;
|
||||
private readonly ResourceService _resources;
|
||||
private readonly ResourceLoader _loader;
|
||||
private readonly List<Record> _records = new();
|
||||
private readonly ConcurrentQueue<Record> _newRecords = new();
|
||||
private readonly Table _table;
|
||||
private string _logFilter = string.Empty;
|
||||
private Regex? _logRegex;
|
||||
private int _newMaxEntries;
|
||||
|
||||
public unsafe ResourceWatcher( ResourceLoader loader )
|
||||
public unsafe ResourceWatcher(Configuration config, ResourceService resources, ResourceLoader loader)
|
||||
{
|
||||
_loader = loader;
|
||||
_table = new Table( _records );
|
||||
_loader.ResourceRequested += OnResourceRequested;
|
||||
_loader.ResourceLoaded += OnResourceLoaded;
|
||||
_loader.FileLoaded += OnFileLoaded;
|
||||
UpdateFilter( Penumbra.Config.ResourceLoggingFilter, false );
|
||||
_writeToLog = Penumbra.Config.EnableResourceLogging;
|
||||
_isEnabled = Penumbra.Config.EnableResourceWatcher;
|
||||
_maxEntries = Penumbra.Config.MaxResourceWatcherRecords;
|
||||
_newMaxEntries = _maxEntries;
|
||||
_config = config;
|
||||
_resources = resources;
|
||||
_loader = loader;
|
||||
_table = new Table(_records);
|
||||
_resources.ResourceRequested += OnResourceRequested;
|
||||
_resources.ResourceHandleDestructor += OnResourceDestroyed;
|
||||
_loader.ResourceLoaded += OnResourceLoaded;
|
||||
_loader.FileLoaded += OnFileLoaded;
|
||||
UpdateFilter(_config.ResourceLoggingFilter, false);
|
||||
_newMaxEntries = _config.MaxResourceWatcherRecords;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
Clear();
|
||||
_records.TrimExcess();
|
||||
_loader.ResourceRequested -= OnResourceRequested;
|
||||
_loader.ResourceLoaded -= OnResourceLoaded;
|
||||
_loader.FileLoaded -= OnFileLoaded;
|
||||
_resources.ResourceRequested -= OnResourceRequested;
|
||||
_resources.ResourceHandleDestructor -= OnResourceDestroyed;
|
||||
_loader.ResourceLoaded -= OnResourceLoaded;
|
||||
_loader.FileLoaded -= OnFileLoaded;
|
||||
}
|
||||
|
||||
private void Clear()
|
||||
|
|
@ -67,183 +71,195 @@ public partial class ResourceWatcher : IDisposable, ITab
|
|||
{
|
||||
UpdateRecords();
|
||||
|
||||
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2 );
|
||||
if( ImGui.Checkbox( "Enable", ref _isEnabled ) )
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2);
|
||||
var isEnabled = _config.EnableResourceWatcher;
|
||||
if (ImGui.Checkbox("Enable", ref isEnabled))
|
||||
{
|
||||
Penumbra.Config.EnableResourceWatcher = _isEnabled;
|
||||
Penumbra.Config.EnableResourceWatcher = isEnabled;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawMaxEntries();
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Button( "Clear" ) )
|
||||
{
|
||||
if (ImGui.Button("Clear"))
|
||||
Clear();
|
||||
|
||||
ImGui.SameLine();
|
||||
var onlyMatching = _config.OnlyAddMatchingResources;
|
||||
if (ImGui.Checkbox("Store Only Matching", ref onlyMatching))
|
||||
{
|
||||
Penumbra.Config.OnlyAddMatchingResources = onlyMatching;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if( ImGui.Checkbox( "Write to Log", ref _writeToLog ) )
|
||||
var writeToLog = _config.EnableResourceLogging;
|
||||
if (ImGui.Checkbox("Write to Log", ref writeToLog))
|
||||
{
|
||||
Penumbra.Config.EnableResourceLogging = _writeToLog;
|
||||
Penumbra.Config.EnableResourceLogging = writeToLog;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
DrawFilterInput();
|
||||
|
||||
ImGui.SetCursorPosY( ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2 );
|
||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() / 2);
|
||||
|
||||
_table.Draw( ImGui.GetTextLineHeightWithSpacing() );
|
||||
_table.Draw(ImGui.GetTextLineHeightWithSpacing());
|
||||
}
|
||||
|
||||
private void DrawFilterInput()
|
||||
{
|
||||
ImGui.SetNextItemWidth( ImGui.GetContentRegionAvail().X );
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
var tmp = _logFilter;
|
||||
var invalidRegex = _logRegex == null && _logFilter.Length > 0;
|
||||
using var color = ImRaii.PushColor( ImGuiCol.Border, Colors.RegexWarningBorder, invalidRegex );
|
||||
using var style = ImRaii.PushStyle( ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, invalidRegex );
|
||||
if( ImGui.InputTextWithHint( "##logFilter", "If path matches this Regex...", ref tmp, 256 ) )
|
||||
{
|
||||
UpdateFilter( tmp, true );
|
||||
}
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Border, Colors.RegexWarningBorder, invalidRegex);
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, invalidRegex);
|
||||
if (ImGui.InputTextWithHint("##logFilter", "If path matches this Regex...", ref tmp, 256))
|
||||
UpdateFilter(tmp, true);
|
||||
}
|
||||
|
||||
private void UpdateFilter( string newString, bool config )
|
||||
private void UpdateFilter(string newString, bool config)
|
||||
{
|
||||
if( newString == _logFilter )
|
||||
{
|
||||
if (newString == _logFilter)
|
||||
return;
|
||||
}
|
||||
|
||||
_logFilter = newString;
|
||||
try
|
||||
{
|
||||
_logRegex = new Regex( _logFilter, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase );
|
||||
_logRegex = new Regex(_logFilter, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logRegex = null;
|
||||
}
|
||||
|
||||
if( config )
|
||||
if (config)
|
||||
{
|
||||
Penumbra.Config.ResourceLoggingFilter = newString;
|
||||
Penumbra.Config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private bool FilterMatch( ByteString path, out string match )
|
||||
private bool FilterMatch(ByteString path, out string match)
|
||||
{
|
||||
match = path.ToString();
|
||||
return _logFilter.Length == 0 || ( _logRegex?.IsMatch( match ) ?? false ) || match.Contains( _logFilter, StringComparison.OrdinalIgnoreCase );
|
||||
return _logFilter.Length == 0 || (_logRegex?.IsMatch(match) ?? false) || match.Contains(_logFilter, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
private void DrawMaxEntries()
|
||||
{
|
||||
ImGui.SetNextItemWidth( 80 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.InputInt( "Max. Entries", ref _newMaxEntries, 0, 0 );
|
||||
ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale);
|
||||
ImGui.InputInt("Max. Entries", ref _newMaxEntries, 0, 0);
|
||||
var change = ImGui.IsItemDeactivatedAfterEdit();
|
||||
if( ImGui.IsItemClicked( ImGuiMouseButton.Right ) && ImGui.GetIO().KeyCtrl )
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
|
||||
{
|
||||
change = true;
|
||||
_newMaxEntries = DefaultMaxEntries;
|
||||
}
|
||||
|
||||
if( _maxEntries != DefaultMaxEntries && ImGui.IsItemHovered() )
|
||||
{
|
||||
ImGui.SetTooltip( $"CTRL + Right-Click to reset to default {DefaultMaxEntries}." );
|
||||
}
|
||||
var maxEntries = _config.MaxResourceWatcherRecords;
|
||||
if (maxEntries != DefaultMaxEntries && ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip($"CTRL + Right-Click to reset to default {DefaultMaxEntries}.");
|
||||
|
||||
if( !change )
|
||||
{
|
||||
if (!change)
|
||||
return;
|
||||
}
|
||||
|
||||
_newMaxEntries = Math.Max( 16, _newMaxEntries );
|
||||
if( _newMaxEntries != _maxEntries )
|
||||
_newMaxEntries = Math.Max(16, _newMaxEntries);
|
||||
if (_newMaxEntries != maxEntries)
|
||||
{
|
||||
_maxEntries = _newMaxEntries;
|
||||
Penumbra.Config.MaxResourceWatcherRecords = _maxEntries;
|
||||
_config.MaxResourceWatcherRecords = _newMaxEntries;
|
||||
Penumbra.Config.Save();
|
||||
_records.RemoveRange( 0, _records.Count - _maxEntries );
|
||||
if (_newMaxEntries > _records.Count)
|
||||
_records.RemoveRange(0, _records.Count - _newMaxEntries);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRecords()
|
||||
{
|
||||
var count = _newRecords.Count;
|
||||
if( count > 0 )
|
||||
if (count > 0)
|
||||
{
|
||||
while( _newRecords.TryDequeue( out var rec ) && count-- > 0 )
|
||||
{
|
||||
_records.Add( rec );
|
||||
}
|
||||
while (_newRecords.TryDequeue(out var rec) && count-- > 0)
|
||||
_records.Add(rec);
|
||||
|
||||
if( _records.Count > _maxEntries )
|
||||
{
|
||||
_records.RemoveRange( 0, _records.Count - _maxEntries );
|
||||
}
|
||||
if (_records.Count > _config.MaxResourceWatcherRecords)
|
||||
_records.RemoveRange(0, _records.Count - _config.MaxResourceWatcherRecords);
|
||||
|
||||
_table.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnResourceRequested( Utf8GamePath data, bool synchronous )
|
||||
private unsafe void OnResourceRequested(ref ResourceCategory category, ref ResourceType type, ref int hash, ref Utf8GamePath path,
|
||||
GetResourceParameters* parameters, ref bool sync, ref ResourceHandle* returnValue)
|
||||
{
|
||||
if( _writeToLog && FilterMatch( data.Path, out var match ) )
|
||||
{
|
||||
Penumbra.Log.Information( $"[ResourceLoader] [REQ] {match} was requested {( synchronous ? "synchronously." : "asynchronously." )}" );
|
||||
}
|
||||
if (_config.EnableResourceLogging && FilterMatch(path.Path, out var match))
|
||||
Penumbra.Log.Information($"[ResourceLoader] [REQ] {match} was requested {(sync ? "synchronously." : "asynchronously.")}");
|
||||
|
||||
if( _isEnabled )
|
||||
if (_config.EnableResourceWatcher)
|
||||
{
|
||||
_newRecords.Enqueue( Record.CreateRequest( data.Path, synchronous ) );
|
||||
var record = Record.CreateRequest(path.Path, sync);
|
||||
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(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)
|
||||
{
|
||||
if( _writeToLog )
|
||||
if (_config.EnableResourceLogging)
|
||||
{
|
||||
var log = FilterMatch( path.Path, out var name );
|
||||
var log = FilterMatch(path.Path, out var name);
|
||||
var name2 = string.Empty;
|
||||
if( manipulatedPath != null )
|
||||
{
|
||||
log |= FilterMatch( manipulatedPath.Value.InternalName, out name2 );
|
||||
}
|
||||
if (manipulatedPath != null)
|
||||
log |= FilterMatch(manipulatedPath.Value.InternalName, out name2);
|
||||
|
||||
if( log )
|
||||
if (log)
|
||||
{
|
||||
var pathString = manipulatedPath != null ? $"custom file {name2} instead of {name}" : name;
|
||||
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 {data.AssociatedName()} (Refcount {handle->RefCount}) ");
|
||||
}
|
||||
}
|
||||
|
||||
if( _isEnabled )
|
||||
if (_config.EnableResourceWatcher)
|
||||
{
|
||||
var record = manipulatedPath == null
|
||||
? Record.CreateDefaultLoad( path.Path, handle, data.ModCollection )
|
||||
: Record.CreateLoad( path.Path, manipulatedPath.Value.InternalName, handle, data.ModCollection );
|
||||
_newRecords.Enqueue( record );
|
||||
? Record.CreateDefaultLoad(path.Path, handle, data.ModCollection)
|
||||
: Record.CreateLoad(path.Path, manipulatedPath.Value.InternalName, handle,
|
||||
data.ModCollection);
|
||||
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
|
||||
_newRecords.Enqueue(record);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void OnFileLoaded( ResourceHandle* resource, ByteString path, bool success, bool custom )
|
||||
private unsafe void OnFileLoaded(ResourceHandle* resource, ByteString path, bool success, bool custom, ByteString _)
|
||||
{
|
||||
if( _writeToLog && FilterMatch( path, out var match ) )
|
||||
{
|
||||
if (_config.EnableResourceLogging && FilterMatch(path, out var match))
|
||||
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( _isEnabled )
|
||||
if (_config.EnableResourceWatcher)
|
||||
{
|
||||
_newRecords.Enqueue( Record.CreateFileLoad( path, resource, success, custom ) );
|
||||
var record = Record.CreateFileLoad(path, resource, success, custom);
|
||||
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
|
||||
_newRecords.Enqueue(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void OnResourceDestroyed(ResourceHandle* resource)
|
||||
{
|
||||
if (_config.EnableResourceLogging && FilterMatch(resource->FileName(), out var match))
|
||||
Penumbra.Log.Information(
|
||||
$"[ResourceLoader] [DEST] [{resource->FileType}] Destroyed {match} at 0x{(ulong)resource:X}.");
|
||||
|
||||
if (_config.EnableResourceWatcher)
|
||||
{
|
||||
var record = Record.CreateDestruction(resource);
|
||||
if (!_config.OnlyAddMatchingResources || _table.WouldBeVisible(record))
|
||||
_newRecords.Enqueue(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue