diff --git a/FFXIVClientStructs.Common.dll b/FFXIVClientStructs.Common.dll
new file mode 100644
index 00000000..69fee26d
Binary files /dev/null and b/FFXIVClientStructs.Common.dll differ
diff --git a/FFXIVClientStructs.Generators.dll b/FFXIVClientStructs.Generators.dll
new file mode 100644
index 00000000..19281c93
Binary files /dev/null and b/FFXIVClientStructs.Generators.dll differ
diff --git a/FFXIVClientStructs.dll b/FFXIVClientStructs.dll
new file mode 100644
index 00000000..e1130434
Binary files /dev/null and b/FFXIVClientStructs.dll differ
diff --git a/Penumbra/Interop/GameResourceManagement.cs b/Penumbra/Interop/GameResourceManagement.cs
index 2bc1d62b..930f7cd1 100644
--- a/Penumbra/Interop/GameResourceManagement.cs
+++ b/Penumbra/Interop/GameResourceManagement.cs
@@ -4,6 +4,8 @@ using System.Runtime.InteropServices;
using Dalamud.Logging;
using Dalamud.Plugin;
using Penumbra.Structs;
+using Reloaded.Hooks.Definitions.X64;
+using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
namespace Penumbra.Interop
{
@@ -85,9 +87,11 @@ namespace Penumbra.Interop
for( var i = 0; i < NumResources; i++ )
{
+ var handle = ( ResourceHandle* )oldResources[ i ];
if( oldResources[ i ].ToPointer() == pResources[ i ] )
{
PluginLog.Debug( $"Unchanged resource: {ResourceToPath( ( byte* )oldResources[ i ].ToPointer() )}" );
+ ( ( ResourceHandle* )oldResources[ i ] )->DecRef();
continue;
}
@@ -96,7 +100,14 @@ namespace Penumbra.Interop
+ $"{ResourceToPath( ( byte* )pResources[ i ] )}" );
UnloadCharacterResource( oldResources[ i ] );
+ // Temporary fix against crashes?
+ if( handle->RefCount <= 0 )
+ {
+ handle->RefCount = 1;
+ handle->IncRef();
+ handle->RefCount = 1;
+ }
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs
index d5df2eaa..3f56806a 100644
--- a/Penumbra/Penumbra.cs
+++ b/Penumbra/Penumbra.cs
@@ -38,6 +38,7 @@ namespace Penumbra
public Penumbra( DalamudPluginInterface pluginInterface )
{
+ FFXIVClientStructs.Resolver.Initialize();
Dalamud.Initialize( pluginInterface );
GameData.GameData.GetIdentifier( Dalamud.GameData, Dalamud.ClientState.ClientLanguage );
Config = Configuration.Load();
@@ -186,4 +187,4 @@ namespace Penumbra
SettingsInterface.FlipVisibility();
}
}
-}
\ No newline at end of file
+}
diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj
index 77e428fe..12a0b033 100644
--- a/Penumbra/Penumbra.csproj
+++ b/Penumbra/Penumbra.csproj
@@ -29,38 +29,48 @@
+
- $(DALAMUD_ROOT)\Dalamud.dll
- ..\libs\Dalamud.dll
$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll
False
- $(DALAMUD_ROOT)\ImGui.NET.dll
- ..\libs\ImGui.NET.dll
$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll
False
- $(DALAMUD_ROOT)\ImGuiScene.dll
- ..\libs\ImGuiScene.dll
$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll
False
- $(DALAMUD_ROOT)\Lumina.dll
- ..\libs\Lumina.dll
$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll
False
- $(DALAMUD_ROOT)\Lumina.Excel.dll
- ..\libs\Lumina.Excel.dll
$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll
False
+
+ ..\FFXIVClientStructs.dll
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -68,8 +78,8 @@
-
-
+
+
@@ -77,4 +87,4 @@
Always
-
\ No newline at end of file
+
diff --git a/Penumbra/UI/MenuTabs/TabResourceManager.cs b/Penumbra/UI/MenuTabs/TabResourceManager.cs
new file mode 100644
index 00000000..47ea0c8c
--- /dev/null
+++ b/Penumbra/UI/MenuTabs/TabResourceManager.cs
@@ -0,0 +1,125 @@
+using System.Numerics;
+using FFXIVClientStructs.FFXIV.Client.System.Resource;
+using FFXIVClientStructs.STD;
+using ImGuiNET;
+using ResourceHandle = FFXIVClientStructs.FFXIV.Client.System.Resource.Handle.ResourceHandle;
+using ResourceManager = FFXIVClientStructs.FFXIV.Client.System.Resource.ResourceManager;
+
+namespace Penumbra.UI
+{
+ public partial class SettingsInterface
+ {
+ private static string GetNodeLabel( string label, uint type, ulong count )
+ {
+ var byte1 = type >> 24;
+ var byte2 = ( type >> 16 ) & 0xFF;
+ var byte3 = ( type >> 8 ) & 0xFF;
+ var byte4 = type & 0xFF;
+ return byte1 == 0
+ ? $"{( char )byte2}{( char )byte3}{( char )byte4} - {count}###{label}{type}Debug"
+ : $"{( char )byte1}{( char )byte2}{( char )byte3}{( char )byte4} - {count}###{label}{type}Debug";
+ }
+
+ private unsafe void DrawResourceMap( string label, StdMap< uint, Pointer< ResourceHandle > >* typeMap )
+ {
+ if( typeMap == null || !ImGui.TreeNodeEx( label ) )
+ {
+ return;
+ }
+
+
+ if( typeMap->Count == 0 || !ImGui.BeginTable( $"##{label}_table", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg ) )
+ {
+ ImGui.TreePop();
+ return;
+ }
+
+ ImGui.TableSetupColumn( "Hash", ImGuiTableColumnFlags.WidthFixed, 100 );
+ ImGui.TableSetupColumn( "Ptr", ImGuiTableColumnFlags.WidthFixed, 100 );
+ ImGui.TableSetupColumn( "Path", ImGuiTableColumnFlags.WidthFixed, ImGui.GetWindowContentRegionWidth() - 300 );
+ ImGui.TableSetupColumn( "Refs", ImGuiTableColumnFlags.WidthFixed, 30 );
+ ImGui.TableHeadersRow();
+
+ var node = typeMap->SmallestValue;
+ while( !node->IsNil )
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.Text( node->KeyValuePair.Item1.ToString() );
+ ImGui.TableNextColumn();
+ var address = $"0x{( ulong )node->KeyValuePair.Item2.Value:X}";
+ ImGui.Text( address );
+ if( ImGui.IsItemClicked() )
+ {
+ ImGui.SetClipboardText( address );
+ }
+
+ ImGui.TableNextColumn();
+ ImGui.Text( node->KeyValuePair.Item2.Value->FileName.ToString() );
+ ImGui.TableNextColumn();
+ ImGui.Text( node->KeyValuePair.Item2.Value->RefCount.ToString() );
+ node = node->Next();
+ }
+
+ ImGui.EndTable();
+ ImGui.TreePop();
+ }
+
+ private unsafe void DrawCategoryContainer( string label, ResourceGraph.CategoryContainer container )
+ {
+ var map = container.MainMap;
+ if( map == null || !ImGui.TreeNodeEx( $"{label} - {map->Count}###{label}Debug" ) )
+ {
+ return;
+ }
+
+ var node = map->SmallestValue;
+ while( !node->IsNil )
+ {
+ DrawResourceMap( GetNodeLabel( label, node->KeyValuePair.Item1, node->KeyValuePair.Item2.Value->Count ),
+ node->KeyValuePair.Item2.Value );
+ node = node->Next();
+ }
+
+ ImGui.TreePop();
+ }
+
+ private unsafe void DrawResourceManagerTab()
+ {
+ if( !ImGui.BeginTabItem( "Resource Manager Tab" ) )
+ {
+ return;
+ }
+
+ var resourceHandler = *( ResourceManager** )( _plugin.PluginInterface.TargetModuleScanner.Module.BaseAddress + 0x1D93AC0 );
+
+ if( resourceHandler == null )
+ {
+ return;
+ }
+
+ if( ImGui.BeginChild( "##ResourceManagerChild", -Vector2.One, true ) )
+ {
+ DrawCategoryContainer( "Common", resourceHandler->ResourceGraph->CommonContainer );
+ DrawCategoryContainer( "BgCommon", resourceHandler->ResourceGraph->BgCommonContainer );
+ DrawCategoryContainer( "Bg", resourceHandler->ResourceGraph->BgContainer );
+ DrawCategoryContainer( "Cut", resourceHandler->ResourceGraph->CutContainer );
+ DrawCategoryContainer( "Chara", resourceHandler->ResourceGraph->CharaContainer );
+ DrawCategoryContainer( "Shader", resourceHandler->ResourceGraph->ShaderContainer );
+ DrawCategoryContainer( "Ui", resourceHandler->ResourceGraph->UiContainer );
+ DrawCategoryContainer( "Sound", resourceHandler->ResourceGraph->SoundContainer );
+ DrawCategoryContainer( "Vfx", resourceHandler->ResourceGraph->VfxContainer );
+ DrawCategoryContainer( "UiScript", resourceHandler->ResourceGraph->UiScriptContainer );
+ DrawCategoryContainer( "Exd", resourceHandler->ResourceGraph->ExdContainer );
+ DrawCategoryContainer( "GameScript", resourceHandler->ResourceGraph->GameScriptContainer );
+ DrawCategoryContainer( "Music", resourceHandler->ResourceGraph->MusicContainer );
+ DrawCategoryContainer( "SqpackTest", resourceHandler->ResourceGraph->SqpackTestContainer );
+ DrawCategoryContainer( "Debug", resourceHandler->ResourceGraph->DebugContainer );
+ }
+
+ ImGui.EndChild();
+
+ ImGui.EndTabItem();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Penumbra/UI/SettingsMenu.cs b/Penumbra/UI/SettingsMenu.cs
index 54395def..9ab96004 100644
--- a/Penumbra/UI/SettingsMenu.cs
+++ b/Penumbra/UI/SettingsMenu.cs
@@ -80,6 +80,7 @@ namespace Penumbra.UI
if( DebugTabVisible )
{
_base.DrawDebugTab();
+ _base.DrawResourceManagerTab();
}
ImGui.EndTabBar();
@@ -87,4 +88,4 @@ namespace Penumbra.UI
}
}
}
-}
\ No newline at end of file
+}