From 5997ddca02b321df487031f1c7e3f3e47bbd97c1 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 2 Feb 2023 11:35:59 +0100 Subject: [PATCH] Add even more handling for stupid banners and some debug info for them. --- Penumbra.GameData/Actors/ActorManager.Data.cs | 21 +++-- Penumbra.GameData/Actors/AgentBannerParty.cs | 91 +++++++++++++++++++ Penumbra/UI/ConfigWindow.DebugTab.cs | 71 +++++++++++++-- 3 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 Penumbra.GameData/Actors/AgentBannerParty.cs diff --git a/Penumbra.GameData/Actors/ActorManager.Data.cs b/Penumbra.GameData/Actors/ActorManager.Data.cs index 20fbe679..fb161304 100644 --- a/Penumbra.GameData/Actors/ActorManager.Data.cs +++ b/Penumbra.GameData/Actors/ActorManager.Data.cs @@ -205,18 +205,23 @@ public sealed partial class ActorManager : IDisposable public unsafe bool ResolvePartyBannerPlayer(ScreenActor type, out ActorIdentifier id) { id = ActorIdentifier.Invalid; - var addon = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.BannerParty); - if (addon == null || !addon->IsAgentActive()) + var module = Framework.Instance()->GetUiModule()->GetAgentModule(); + if (module == null) return false; - var idx = (ushort)type - (ushort)ScreenActor.CharacterScreen; - if (idx is < 0 or > 7) + var agent = (AgentBannerInterface*)module->GetAgentByInternalId(AgentId.BannerParty); + if (agent == null || !agent->AgentInterface.IsAgentActive()) + agent = (AgentBannerInterface*)module->GetAgentByInternalId(AgentId.BannerMIP); + if (agent == null || !agent->AgentInterface.IsAgentActive()) + return false; + + var idx = (ushort)type - (ushort)ScreenActor.CharacterScreen; + var character = agent->Character(idx); + if (character == null) return true; - var obj = GroupManager.Instance()->GetPartyMemberByIndex(idx); - if (obj != null) - id = CreatePlayer(new ByteString(obj->Name), obj->HomeWorld); - + var name = new ByteString(character->Name1.StringPtr); + id = CreatePlayer(name, (ushort)character->WorldId); return true; } diff --git a/Penumbra.GameData/Actors/AgentBannerParty.cs b/Penumbra.GameData/Actors/AgentBannerParty.cs new file mode 100644 index 00000000..9756a289 --- /dev/null +++ b/Penumbra.GameData/Actors/AgentBannerParty.cs @@ -0,0 +1,91 @@ +using FFXIVClientStructs.FFXIV.Client.System.String; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Component.GUI; +using System.Runtime.InteropServices; +using FFXIVClientStructs.FFXIV.Client.System.Framework; + +namespace Penumbra; + +[StructLayout( LayoutKind.Explicit )] +public unsafe struct AgentBannerInterface +{ + [FieldOffset( 0x0 )] public AgentInterface AgentInterface; + [FieldOffset( 0x28 )] public BannerInterfaceStorage* Data; + + public BannerInterfaceStorage.CharacterData* Character( int idx ) + => idx switch + { + _ when Data == null => null, + 0 => &Data->Character1, + 1 => &Data->Character2, + 2 => &Data->Character3, + 3 => &Data->Character4, + 4 => &Data->Character5, + 5 => &Data->Character6, + 6 => &Data->Character7, + 7 => &Data->Character8, + _ => null, + }; +} + +[StructLayout(LayoutKind.Explicit)] +public unsafe struct AgentBannerParty +{ + public static AgentBannerParty* Instance() => ( AgentBannerParty* )Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId( AgentId.BannerParty ); + + [FieldOffset( 0x0 )] public AgentBannerInterface AgentBannerInterface; +} + +[StructLayout( LayoutKind.Explicit )] +public unsafe struct AgentBannerMIP +{ + public static AgentBannerMIP* Instance() => ( AgentBannerMIP* )Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId( AgentId.BannerMIP ); + [FieldOffset( 0x0 )] public AgentBannerInterface AgentBannerInterface; +} + +// Client::UI::Agent::AgentBannerInterface::Storage +// destructed in Client::UI::Agent::AgentBannerInterface::dtor +[StructLayout( LayoutKind.Explicit, Size = 0x3BB0 )] +public unsafe struct BannerInterfaceStorage +{ + // vtable: 48 8D 05 ?? ?? ?? ?? 48 89 01 48 8B F9 7E + // dtor: E8 ?? ?? ?? ?? 48 83 EF ?? 75 ?? BA ?? ?? ?? ?? 48 8B CE E8 ?? ?? ?? ?? 48 89 7D + [StructLayout( LayoutKind.Explicit, Size = 0x770 )] + public struct CharacterData + { + [FieldOffset( 0x000 )] public void** VTable; + + [FieldOffset( 0x018 )] public Utf8String Name1; + [FieldOffset( 0x080 )] public Utf8String Name2; + [FieldOffset( 0x0E8 )] public Utf8String UnkString1; + [FieldOffset( 0x150 )] public Utf8String UnkString2; + [FieldOffset( 0x1C0 )] public Utf8String Job; + [FieldOffset( 0x238 )] public uint WorldId; + [FieldOffset( 0x240 )] public Utf8String UnkString3; + + [FieldOffset( 0x2B0 )] public void* CharaView; + [FieldOffset( 0x5D0 )] public AtkTexture AtkTexture; + + [FieldOffset( 0x6F8 )] public Utf8String Title; + [FieldOffset( 0x768 )] public void* SomePointer; + + } + + [FieldOffset( 0x0000 )] public void* Agent; // AgentBannerParty, maybe other Banner agents + [FieldOffset( 0x0008 )] public UIModule* UiModule; + [FieldOffset( 0x0010 )] public uint Unk1; // Maybe count or bitfield, but probably not + [FieldOffset( 0x0014 )] public uint Unk2; + + [FieldOffset( 0x0020 )] public CharacterData Character1; + [FieldOffset( 0x0790 )] public CharacterData Character2; + [FieldOffset( 0x0F00 )] public CharacterData Character3; + [FieldOffset( 0x1670 )] public CharacterData Character4; + [FieldOffset( 0x1DE0 )] public CharacterData Character5; + [FieldOffset( 0x2550 )] public CharacterData Character6; + [FieldOffset( 0x2CC0 )] public CharacterData Character7; + [FieldOffset( 0x3430 )] public CharacterData Character8; + + [FieldOffset( 0x3BA0 )] public long Unk3; + [FieldOffset( 0x3BA8 )] public long Unk4; +} \ No newline at end of file diff --git a/Penumbra/UI/ConfigWindow.DebugTab.cs b/Penumbra/UI/ConfigWindow.DebugTab.cs index f25ed1d4..52a1f636 100644 --- a/Penumbra/UI/ConfigWindow.DebugTab.cs +++ b/Penumbra/UI/ConfigWindow.DebugTab.cs @@ -4,9 +4,11 @@ using System.IO; using System.Linq; using System.Numerics; using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.System.Resource; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; using ImGuiNET; using OtterGui; using OtterGui.Raii; @@ -17,6 +19,7 @@ using Penumbra.Interop.Resolver; using Penumbra.Interop.Structs; using Penumbra.String; using Penumbra.Util; +using static OtterGui.Raii.ImRaii; using CharacterUtility = Penumbra.Interop.CharacterUtility; using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind; @@ -223,7 +226,8 @@ public partial class ConfigWindow return; } - ImGui.TextUnformatted( $"Last Game Object: 0x{_window._penumbra.PathResolver.LastGameObject:X} ({_window._penumbra.PathResolver.LastGameObjectData.ModCollection.Name})" ); + ImGui.TextUnformatted( + $"Last Game Object: 0x{_window._penumbra.PathResolver.LastGameObject:X} ({_window._penumbra.PathResolver.LastGameObjectData.ModCollection.Name})" ); using( var drawTree = ImRaii.TreeNode( "Draw Object to Object" ) ) { if( drawTree ) @@ -317,18 +321,65 @@ public partial class ConfigWindow } } - using var cutsceneTree = ImRaii.TreeNode( "Cutscene Actors" ); - if( cutsceneTree ) + using( var cutsceneTree = ImRaii.TreeNode( "Cutscene Actors" ) ) { - using var table = ImRaii.Table( "###PCutsceneResolverTable", 2, ImGuiTableFlags.SizingFixedFit ); - if( table ) + if( cutsceneTree ) { - foreach( var (idx, actor) in _window._penumbra.PathResolver.CutsceneActors ) + using var table = ImRaii.Table( "###PCutsceneResolverTable", 2, ImGuiTableFlags.SizingFixedFit ); + if( table ) { - ImGui.TableNextColumn(); - ImGui.TextUnformatted( $"Cutscene Actor {idx}" ); - ImGui.TableNextColumn(); - ImGui.TextUnformatted( actor.Name.ToString() ); + foreach( var (idx, actor) in _window._penumbra.PathResolver.CutsceneActors ) + { + ImGuiUtil.DrawTableColumn( $"Cutscene Actor {idx}" ); + ImGuiUtil.DrawTableColumn( actor.Name.ToString() ); + } + } + } + } + + using( var groupTree = ImRaii.TreeNode( "Group" ) ) + { + if( groupTree ) + { + using var table = ImRaii.Table( "###PGroupTable", 2, ImGuiTableFlags.SizingFixedFit ); + if( table ) + { + ImGuiUtil.DrawTableColumn( "Group Members" ); + ImGuiUtil.DrawTableColumn( GroupManager.Instance()->MemberCount.ToString() ); + for( var i = 0; i < 8; ++i ) + { + ImGuiUtil.DrawTableColumn( $"Member #{i}" ); + var member = GroupManager.Instance()->GetPartyMemberByIndex( i ); + ImGuiUtil.DrawTableColumn( member == null ? "NULL" : new ByteString( member->Name ).ToString() ); + } + } + } + } + + using( var bannerTree = ImRaii.TreeNode( "Party Banner" ) ) + { + if( bannerTree ) + { + var agent = &AgentBannerParty.Instance()->AgentBannerInterface; + if( agent->Data == null ) + agent = &AgentBannerMIP.Instance()->AgentBannerInterface; + if( agent->Data != null ) + { + using var table = ImRaii.Table( "###PBannerTable", 2, ImGuiTableFlags.SizingFixedFit ); + if( table ) + { + for( var i = 0; i < 8; ++i ) + { + var c = agent->Character( i ); + ImGuiUtil.DrawTableColumn( $"Character {i}" ); + var name = c->Name1.ToString(); + ImGuiUtil.DrawTableColumn( name.Length == 0 ? "NULL" : $"{name} ({c->WorldId})" ); + } + } + } + else + { + ImGui.TextUnformatted( "INACTIVE" ); } } }