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 +}