From 3a6312422ce2fcc46a9c185d48bb4897e090ced8 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 13 Nov 2024 19:44:35 -0800 Subject: [PATCH 01/14] Move HandleActionHover to CS Co-authored-by: Infi --- Dalamud/Game/Gui/GameGui.cs | 2 +- Dalamud/Game/Gui/GameGuiAddressResolver.cs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 74e0a8df3..b21613a06 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -57,7 +57,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleActionHoverHook = Hook.FromAddress(this.address.HandleActionHover, this.HandleActionHoverDetour); + this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); this.handleActionOutHook = Hook.FromAddress(this.address.HandleActionOut, this.HandleActionOutDetour); this.handleImmHook = Hook.FromAddress(this.address.HandleImm, this.HandleImmDetour); diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index a742541ea..5a404fb2a 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -15,11 +15,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr SetGlobalBgm { get; private set; } - /// - /// Gets the address of the native HandleActionHover method. - /// - public IntPtr HandleActionHover { get; private set; } - /// /// Gets the address of the native HandleActionOut method. /// @@ -39,7 +34,6 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver protected override void Setup64Bit(ISigScanner sig) { this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); - this.HandleActionHover = sig.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 83 F8 0F"); this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F"); this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); From 4e76669779de5d95ed93f9443e2c5bd43c478787 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 14 Nov 2024 06:56:00 +0100 Subject: [PATCH 02/14] Add new city to the upload (#2078) --- .../Universalis/Types/UniversalisTaxData.cs | 6 ++++++ .../Universalis/UniversalisMarketBoardUploader.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs index 72f54773d..2277920a7 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/Types/UniversalisTaxData.cs @@ -48,4 +48,10 @@ internal class UniversalisTaxData /// [JsonProperty("sharlayan")] public uint Sharlayan { get; set; } + + /// + /// Gets or sets Tuliyollal's current tax rate. + /// + [JsonProperty("tuliyollal")] + public uint Tuliyollal { get; set; } } diff --git a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs index 5215cf030..9528a9b50 100644 --- a/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs +++ b/Dalamud/Game/Network/Internal/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs @@ -131,6 +131,7 @@ internal class UniversalisMarketBoardUploader : IMarketBoardUploader Kugane = taxRates.KuganeTax, Crystarium = taxRates.CrystariumTax, Sharlayan = taxRates.SharlayanTax, + Tuliyollal = taxRates.TuliyollalTax, }, }; From 7c6ed6de76646f8ef0cf47ebdb5abb5828b40d41 Mon Sep 17 00:00:00 2001 From: Infi Date: Thu, 14 Nov 2024 06:56:30 +0100 Subject: [PATCH 03/14] Use more of the CS version for ActionDetailHover (#2079) * - Use CS for delegate - Sync HoverActionKind with CS version * Undo changes in favor of #2080 --- Dalamud/Game/Gui/GameGui.cs | 34 ++++----- Dalamud/Game/Gui/HoverActionKind.cs | 107 +++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 18 deletions(-) diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index b21613a06..85ac36a66 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -92,7 +92,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); - + /// public event EventHandler? UiHideToggled; @@ -138,7 +138,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui inView = false; return false; } - + pCoords *= MathF.Abs(1.0f / pCoords.W); screenPos = new Vector2(pCoords.X, pCoords.Y); @@ -166,7 +166,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui worldPos = default; return false; } - + var camera = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CameraManager.Instance()->CurrentCamera; if (camera == null) { @@ -221,7 +221,7 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// public IntPtr FindAgentInterface(void* addon) => this.FindAgentInterface((IntPtr)addon); - + /// public IntPtr FindAgentInterface(IntPtr addonPtr) { @@ -422,13 +422,13 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.gameGuiService.HoveredItemChanged += this.HoveredItemForward; this.gameGuiService.HoveredActionChanged += this.HoveredActionForward; } - + /// public event EventHandler? UiHideToggled; - + /// public event EventHandler? HoveredItemChanged; - + /// public event EventHandler? HoveredActionChanged; @@ -444,7 +444,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public HoveredAction HoveredAction => this.gameGuiService.HoveredAction; - + /// void IInternalDisposableService.DisposeService() { @@ -456,7 +456,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui this.HoveredItemChanged = null; this.HoveredActionChanged = null; } - + /// public bool OpenMapWithMapLink(MapLinkPayload mapLink) => this.gameGuiService.OpenMapWithMapLink(mapLink); @@ -464,7 +464,7 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos) => this.gameGuiService.WorldToScreen(worldPos, out screenPos); - + /// public bool WorldToScreen(Vector3 worldPos, out Vector2 screenPos, out bool inView) => this.gameGuiService.WorldToScreen(worldPos, out screenPos, out inView); @@ -476,26 +476,26 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui /// public IntPtr GetUIModule() => this.gameGuiService.GetUIModule(); - + /// public IntPtr GetAddonByName(string name, int index = 1) => this.gameGuiService.GetAddonByName(name, index); - + /// public IntPtr FindAgentInterface(string addonName) => this.gameGuiService.FindAgentInterface(addonName); - + /// - public unsafe IntPtr FindAgentInterface(void* addon) + public unsafe IntPtr FindAgentInterface(void* addon) => this.gameGuiService.FindAgentInterface(addon); /// - public IntPtr FindAgentInterface(IntPtr addonPtr) + public IntPtr FindAgentInterface(IntPtr addonPtr) => this.gameGuiService.FindAgentInterface(addonPtr); private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled); - + private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId); - + private void HoveredActionForward(object sender, HoveredAction hoverAction) => this.HoveredActionChanged?.Invoke(sender, hoverAction); } diff --git a/Dalamud/Game/Gui/HoverActionKind.cs b/Dalamud/Game/Gui/HoverActionKind.cs index 4c7ae9166..ef8fe6400 100644 --- a/Dalamud/Game/Gui/HoverActionKind.cs +++ b/Dalamud/Game/Gui/HoverActionKind.cs @@ -16,6 +16,11 @@ public enum HoverActionKind /// Action = 28, + /// + /// A crafting action is hovered. + /// + CraftingAction = 29, + /// /// A general action is hovered. /// @@ -24,7 +29,7 @@ public enum HoverActionKind /// /// A companion order type of action is hovered. /// - CompanionOrder = 31, + CompanionOrder = 31, // Game Term: BuddyOrder /// /// A main command type of action is hovered. @@ -36,6 +41,11 @@ public enum HoverActionKind /// ExtraCommand = 33, + /// + /// A companion action is hovered. + /// + Companion = 34, + /// /// A pet order type of action is hovered. /// @@ -45,4 +55,99 @@ public enum HoverActionKind /// A trait is hovered. /// Trait = 36, + + /// + /// A buddy action is hovered. + /// + BuddyAction = 37, + + /// + /// A company action is hovered. + /// + CompanyAction = 38, + + /// + /// A mount is hovered. + /// + Mount = 39, + + /// + /// A chocobo race action is hovered. + /// + ChocoboRaceAction = 40, + + /// + /// A chocobo race item is hovered. + /// + ChocoboRaceItem = 41, + + /// + /// A deep dungeon equipment is hovered. + /// + DeepDungeonEquipment = 42, + + /// + /// A deep dungeon equipment 2 is hovered. + /// + DeepDungeonEquipment2 = 43, + + /// + /// A deep dungeon item is hovered. + /// + DeepDungeonItem = 44, + + /// + /// A quick chat is hovered. + /// + QuickChat = 45, + + /// + /// An action combo route is hovered. + /// + ActionComboRoute = 46, + + /// + /// A pvp trait is hovered. + /// + PvPSelectTrait = 47, + + /// + /// A squadron action is hovered. + /// + BgcArmyAction = 48, + + /// + /// A perform action is hovered. + /// + Perform = 49, + + /// + /// A deep dungeon magic stone is hovered. + /// + DeepDungeonMagicStone = 50, + + /// + /// A deep dungeon demiclone is hovered. + /// + DeepDungeonDemiclone = 51, + + /// + /// An eureka magia action is hovered. + /// + EurekaMagiaAction = 52, + + /// + /// An island sanctuary temporary item is hovered. + /// + MYCTemporaryItem = 53, + + /// + /// An ornament is hovered. + /// + Ornament = 54, + + /// + /// Glasses are hovered. + /// + Glasses = 55, } From 097f85eff6386557d1e6a29de3cec9a3d1b1d6e1 Mon Sep 17 00:00:00 2001 From: KazWolfe Date: Thu, 14 Nov 2024 08:29:28 -0800 Subject: [PATCH 04/14] Move more things to ClientStructs (#2080) * GameGui uses CS methods now Co-authored-by: Infi * Shove even more things over to CS * Clean up NetworkHandlers too * bump cs so things build at least --------- Co-authored-by: Infi --- .../AddonEventManagerAddressResolver.cs | 2 +- Dalamud/Game/ClientState/ClientState.cs | 12 ++- .../ClientState/ClientStateAddressResolver.cs | 11 +-- .../Game/Config/GameConfigAddressResolver.cs | 2 +- .../DutyState/DutyStateAddressResolver.cs | 2 +- Dalamud/Game/Gui/FlyText/FlyTextGui.cs | 73 +++---------------- .../Gui/FlyText/FlyTextGuiAddressResolver.cs | 29 -------- Dalamud/Game/Gui/GameGui.cs | 39 ++++------ Dalamud/Game/Gui/GameGuiAddressResolver.cs | 17 +---- .../PartyFinder/PartyFinderAddressResolver.cs | 18 ----- .../Game/Gui/PartyFinder/PartyFinderGui.cs | 21 ++---- Dalamud/Game/Internal/DalamudAtkTweaks.cs | 6 +- Dalamud/Game/Network/GameNetwork.cs | 30 ++++---- .../Network/GameNetworkAddressResolver.cs | 10 +-- .../Game/Network/Internal/NetworkHandlers.cs | 59 +++++++-------- .../NetworkHandlersAddressResolver.cs | 49 +------------ lib/FFXIVClientStructs | 2 +- 17 files changed, 96 insertions(+), 286 deletions(-) delete mode 100644 Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs delete mode 100644 Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs diff --git a/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs index 927ed87ab..415e1b169 100644 --- a/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs +++ b/Dalamud/Game/Addon/Events/AddonEventManagerAddressResolver.cs @@ -16,6 +16,6 @@ internal class AddonEventManagerAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner scanner) { - this.UpdateCursor = scanner.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 E8 ?? ?? ?? ?? 49 8B CE"); + this.UpdateCursor = scanner.ScanText("48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 E8 ?? ?? ?? ?? 49 8B CE"); // unnamed in CS } } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 750ba34c5..6ceea4c6b 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -37,7 +37,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState private readonly GameLifecycle lifecycle; private readonly ClientStateAddressResolver address; - private readonly Hook setupTerritoryTypeHook; + private readonly Hook setupTerritoryTypeHook; private readonly Hook uiModuleHandlePacketHook; private readonly Hook processPacketPlayerSetupHook; private readonly Hook onLogoutHook; @@ -56,9 +56,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language; - Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(this.address.SetupTerritoryType)}"); + var setTerritoryTypeAddr = EventFramework.Addresses.SetTerritoryTypeId.Value; + Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(setTerritoryTypeAddr)}"); - this.setupTerritoryTypeHook = Hook.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour); + this.setupTerritoryTypeHook = Hook.FromAddress(setTerritoryTypeAddr, this.SetupTerritoryTypeDetour); this.uiModuleHandlePacketHook = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->HandlePacket, this.UIModuleHandlePacketDetour); this.processPacketPlayerSetupHook = Hook.FromAddress(this.address.ProcessPacketPlayerSetup, this.ProcessPacketPlayerSetupDetour); this.onLogoutHook = Hook.FromAddress((nint)LogoutCallbackInterface.StaticVirtualTablePointer->OnLogout, this.OnLogoutDetour); @@ -70,10 +71,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.processPacketPlayerSetupHook.Enable(); this.onLogoutHook.Enable(); } - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private unsafe delegate void SetupTerritoryTypeDelegate(EventFramework* eventFramework, ushort terriType); - + private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); /// diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index c7d7837a4..d44275ef8 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -19,11 +19,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver // Functions - /// - /// Gets the address of the method which sets the territory type. - /// - public IntPtr SetupTerritoryType { get; private set; } - /// /// Gets the address of the method which sets up the player. /// @@ -41,9 +36,7 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC ?? 48 8B D9 48 89 6C 24"); - - this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); + this.ProcessPacketPlayerSetup = sig.ScanText("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B D3"); // not in cs struct // These resolve to fixed offsets only, without the base address added in, so GetStaticAddressFromSig() can't be used. // lea rcx, ds:1DB9F74h[rax*4] KeyboardState @@ -51,6 +44,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; this.KeyboardStateIndexArray = sig.ScanText("0F B6 94 33 ?? ?? ?? ?? 84 D2") + 0x4; - this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); + this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); // unnamed in cs } } diff --git a/Dalamud/Game/Config/GameConfigAddressResolver.cs b/Dalamud/Game/Config/GameConfigAddressResolver.cs index c171932a9..2491c4033 100644 --- a/Dalamud/Game/Config/GameConfigAddressResolver.cs +++ b/Dalamud/Game/Config/GameConfigAddressResolver.cs @@ -13,6 +13,6 @@ internal sealed class GameConfigAddressResolver : BaseAddressResolver /// protected override void Setup64Bit(ISigScanner scanner) { - this.ConfigChangeAddress = scanner.ScanText("E8 ?? ?? ?? ?? 48 8B 3F 49 3B 3E"); + this.ConfigChangeAddress = scanner.ScanText("E8 ?? ?? ?? ?? 48 8B 3F 49 3B 3E"); // unnamed in CS } } diff --git a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs index 01a0d39b7..1bca93efb 100644 --- a/Dalamud/Game/DutyState/DutyStateAddressResolver.cs +++ b/Dalamud/Game/DutyState/DutyStateAddressResolver.cs @@ -16,6 +16,6 @@ internal class DutyStateAddressResolver : BaseAddressResolver /// The signature scanner to facilitate setup. protected override void Setup64Bit(ISigScanner sig) { - this.ContentDirectorNetworkMessage = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC ?? 33 FF 48 8B D9 41 0F B7 08"); + this.ContentDirectorNetworkMessage = sig.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC ?? 33 FF 48 8B D9 41 0F B7 08"); // unnamed in cs } } diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs index b01f8c244..cbf166cfc 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs @@ -23,62 +23,21 @@ namespace Dalamud.Game.Gui.FlyText; internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui { /// - /// The native function responsible for adding fly text to the UI. See . + /// The hook that fires when the game creates a fly text element. /// - private readonly AddFlyTextDelegate addFlyTextNative; - - /// - /// The hook that fires when the game creates a fly text element. See . - /// - private readonly Hook createFlyTextHook; + private readonly Hook createFlyTextHook; [ServiceManager.ServiceConstructor] private unsafe FlyTextGui(TargetSigScanner sigScanner) { - this.Address = new FlyTextGuiAddressResolver(); - this.Address.Setup(sigScanner); - - this.addFlyTextNative = Marshal.GetDelegateForFunctionPointer(this.Address.AddFlyText); - this.createFlyTextHook = Hook.FromAddress(this.Address.CreateFlyText, this.CreateFlyTextDetour); + this.createFlyTextHook = Hook.FromAddress(AddonFlyText.Addresses.CreateFlyText.Value, this.CreateFlyTextDetour); this.createFlyTextHook.Enable(); } - /// - /// Private delegate for the native CreateFlyText function's hook. - /// - private unsafe delegate nint CreateFlyTextDelegate( - AtkUnitBase* thisPtr, - FlyTextKind kind, - int val1, - int val2, - byte* text2, - uint color, - uint icon, - uint damageTypeIcon, - byte* text1, - float yOffset); - - /// - /// Private delegate for the native AddFlyText function pointer. - /// - private unsafe delegate void AddFlyTextDelegate( - AtkUnitBase* thisPtr, - uint actorIndex, - uint messageMax, - NumberArrayData* numberArrayData, - uint offsetNum, - uint offsetNumMax, - StringArrayData* stringArrayData, - uint offsetStr, - uint offsetStrMax, - int unknown); - /// public event IFlyTextGui.OnFlyTextCreatedDelegate? FlyTextCreated; - private FlyTextGuiAddressResolver Address { get; } - /// /// Disposes of managed and unmanaged resources. /// @@ -94,7 +53,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var numOffset = 161u; var strOffset = 28u; - var flytext = RaptureAtkUnitManager.Instance()->GetAddonByName("_FlyText"); + var flytext = (AddonFlyText*)RaptureAtkUnitManager.Instance()->GetAddonByName("_FlyText"); if (flytext == null) return; @@ -116,23 +75,13 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false); strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false); - - this.addFlyTextNative( - flytext, - actorIndex, - 1, - numArray, - numOffset, - 10, - strArray, - strOffset, - 2, - 0); + + flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0); } private unsafe nint CreateFlyTextDetour( - AtkUnitBase* thisPtr, - FlyTextKind kind, + AddonFlyText* thisPtr, + int kind, int val1, int val2, byte* text2, @@ -149,7 +98,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var handled = false; - var tmpKind = kind; + var tmpKind = (FlyTextKind)kind; var tmpVal1 = val1; var tmpVal2 = val2; var tmpText1 = text1 == null ? string.Empty : MemoryHelper.ReadSeStringNullTerminated((nint)text1); @@ -193,7 +142,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui var maybeModifiedText2 = tmpText2.EncodeWithNullTerminator(); // Check if any values have changed - var dirty = tmpKind != kind || + var dirty = (int)tmpKind != kind || tmpVal1 != val1 || tmpVal2 != val2 || !maybeModifiedText1.SequenceEqual(originalText1) || @@ -219,7 +168,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui retVal = this.createFlyTextHook.Original( thisPtr, - tmpKind, + (int)tmpKind, tmpVal1, tmpVal2, (byte*)pText2, diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs b/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs deleted file mode 100644 index aa7c8026f..000000000 --- a/Dalamud/Game/Gui/FlyText/FlyTextGuiAddressResolver.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Dalamud.Game.Gui.FlyText; - -/// -/// An address resolver for the class. -/// -internal class FlyTextGuiAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native AddFlyText method, which occurs - /// when the game adds fly text elements to the UI. Multiple fly text - /// elements can be added in a single AddFlyText call. - /// - public IntPtr AddFlyText { get; private set; } - - /// - /// Gets the address of the native CreateFlyText method, which occurs - /// when the game creates a new fly text element. This method is called - /// once per fly text element, and can be called multiple times per - /// AddFlyText call. - /// - public IntPtr CreateFlyText { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.AddFlyText = sig.ScanText("E8 ?? ?? ?? ?? FF C7 41 D1 C7"); - this.CreateFlyText = sig.ScanText("E8 ?? ?? ?? ?? 48 8B F8 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B 18"); - } -} diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 85ac36a66..54f4253cd 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -38,11 +38,11 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui private readonly GameGuiAddressResolver address; private readonly Hook setGlobalBgmHook; - private readonly Hook handleActionHoverHook; - private readonly Hook handleActionOutHook; + private readonly Hook handleActionHoverHook; + private readonly Hook handleActionOutHook; private readonly Hook handleImmHook; private readonly Hook setUiVisibilityHook; - private readonly Hook utf8StringFromSequenceHook; + private readonly Hook utf8StringFromSequenceHook; [ServiceManager.ServiceConstructor] private GameGui(TargetSigScanner sigScanner) @@ -57,14 +57,14 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); - this.handleActionOutHook = Hook.FromAddress(this.address.HandleActionOut, this.HandleActionOutDetour); + this.handleActionHoverHook = Hook.FromAddress(AgentActionDetail.Addresses.HandleActionHover.Value, this.HandleActionHoverDetour); + this.handleActionOutHook = Hook.FromAddress((nint)AgentActionDetail.StaticVirtualTablePointer->ReceiveEvent, this.HandleActionOutDetour); this.handleImmHook = Hook.FromAddress(this.address.HandleImm, this.HandleImmDetour); this.setUiVisibilityHook = Hook.FromAddress((nint)RaptureAtkModule.StaticVirtualTablePointer->SetUiVisibility, this.SetUiVisibilityDetour); - this.utf8StringFromSequenceHook = Hook.FromAddress(this.address.Utf8StringFromSequence, this.Utf8StringFromSequenceDetour); + this.utf8StringFromSequenceHook = Hook.FromAddress(Utf8String.Addresses.Ctor_FromSequence.Value, this.Utf8StringFromSequenceDetour); this.setGlobalBgmHook.Enable(); this.handleImmHook.Enable(); @@ -77,19 +77,10 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui } // Hooked delegates - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate Utf8String* Utf8StringFromSequenceDelegate(Utf8String* thisPtr, byte* sourcePtr, nuint sourceLen); - + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr SetGlobalBgmDelegate(ushort bgmKey, byte a2, uint a3, uint a4, uint a5, byte a6); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void HandleActionHoverDelegate(IntPtr hoverState, HoverActionKind a2, uint a3, int a4, byte a5); - - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate IntPtr HandleActionOutDelegate(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3); @@ -309,24 +300,24 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui return retVal; } - private void HandleActionHoverDetour(IntPtr hoverState, HoverActionKind actionKind, uint actionId, int a4, byte a5) + private void HandleActionHoverDetour(AgentActionDetail* hoverState, ActionKind actionKind, uint actionId, int a4, byte a5) { this.handleActionHoverHook.Original(hoverState, actionKind, actionId, a4, a5); - this.HoveredAction.ActionKind = actionKind; + this.HoveredAction.ActionKind = (HoverActionKind)actionKind; this.HoveredAction.BaseActionID = actionId; - this.HoveredAction.ActionID = (uint)Marshal.ReadInt32(hoverState, 0x3C); + this.HoveredAction.ActionID = hoverState->ActionId; this.HoveredActionChanged?.InvokeSafely(this, this.HoveredAction); - Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{hoverState.ToInt64():X}"); + Log.Verbose($"HoverActionId: {actionKind}/{actionId} this:{(nint)hoverState:X}"); } - private IntPtr HandleActionOutDetour(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4) + private AtkValue* HandleActionOutDetour(AgentActionDetail* agentActionDetail, AtkValue* a2, AtkValue* a3, uint a4, ulong a5) { - var retVal = this.handleActionOutHook.Original(agentActionDetail, a2, a3, a4); + var retVal = this.handleActionOutHook.Original(agentActionDetail, a2, a3, a4, a5); - if (a3 != IntPtr.Zero && a4 == 1) + if (a3 != null && a4 == 1) { - var a3Val = Marshal.ReadByte(a3, 0x8); + var a3Val = a3->Int; if (a3Val == 255) { diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index 5a404fb2a..bdd579c7e 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -15,28 +15,15 @@ internal sealed class GameGuiAddressResolver : BaseAddressResolver /// public IntPtr SetGlobalBgm { get; private set; } - /// - /// Gets the address of the native HandleActionOut method. - /// - public IntPtr HandleActionOut { get; private set; } - /// /// Gets the address of the native HandleImm method. /// public IntPtr HandleImm { get; private set; } - /// - /// Gets the address of the native Utf8StringFromSequence method. - /// - public IntPtr Utf8StringFromSequence { get; private set; } - /// protected override void Setup64Bit(ISigScanner sig) { - this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); - this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F"); - this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); - - this.Utf8StringFromSequence = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 41 22 66 C7 41 ?? ?? ?? 48 89 01 49 8B D8"); + this.SetGlobalBgm = sig.ScanText("E8 ?? ?? ?? ?? 8B 2F"); // unnamed in CS + this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09"); // unnamed in CS } } diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs deleted file mode 100644 index 0b267694c..000000000 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderAddressResolver.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Dalamud.Game.Gui.PartyFinder; - -/// -/// The address resolver for the class. -/// -internal class PartyFinderAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the native ReceiveListing method. - /// - public IntPtr ReceiveListing { get; private set; } - - /// - protected override void Setup64Bit(ISigScanner sig) - { - this.ReceiveListing = sig.ScanText("40 53 41 57 48 83 EC ?? 48 8B D9 4C 8B FA"); - } -} diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs index ef4055b29..0b25a87be 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs @@ -7,6 +7,8 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.UI.Info; + using Serilog; namespace Dalamud.Game.Gui.PartyFinder; @@ -15,12 +17,11 @@ namespace Dalamud.Game.Gui.PartyFinder; /// This class handles interacting with the native PartyFinder window. /// [ServiceManager.EarlyLoadedService] -internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderGui +internal sealed unsafe class PartyFinderGui : IInternalDisposableService, IPartyFinderGui { - private readonly PartyFinderAddressResolver address; private readonly nint memory; - private readonly Hook receiveListingHook; + private readonly Hook receiveListingHook; /// /// Initializes a new instance of the class. @@ -29,18 +30,12 @@ internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderG [ServiceManager.ServiceConstructor] private PartyFinderGui(TargetSigScanner sigScanner) { - this.address = new PartyFinderAddressResolver(); - this.address.Setup(sigScanner); - this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize); - this.receiveListingHook = Hook.FromAddress(this.address.ReceiveListing, this.HandleReceiveListingDetour); + this.receiveListingHook = Hook.FromAddress(InfoProxyCrossRealm.Addresses.ReceiveListing.Value, this.HandleReceiveListingDetour); this.receiveListingHook.Enable(); } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ReceiveListingDelegate(nint managerPtr, nint data); - /// public event IPartyFinderGui.PartyFinderListingEventDelegate? ReceiveListing; @@ -61,18 +56,18 @@ internal sealed class PartyFinderGui : IInternalDisposableService, IPartyFinderG } } - private void HandleReceiveListingDetour(nint managerPtr, nint data) + private void HandleReceiveListingDetour(InfoProxyCrossRealm* infoProxy, nint packet) { try { - this.HandleListingEvents(data); + this.HandleListingEvents(packet); } catch (Exception ex) { Log.Error(ex, "Exception on ReceiveListing hook."); } - this.receiveListingHook.Original(managerPtr, data); + this.receiveListingHook.Original(infoProxy, packet); } private void HandleListingEvents(nint data) diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index c147f76e6..7834ab58f 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -24,7 +24,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService { private static readonly ModuleLog Log = new("DalamudAtkTweaks"); - private readonly Hook hookAgentHudOpenSystemMenu; + private readonly Hook hookAgentHudOpenSystemMenu; // TODO: Make this into events in Framework.Gui private readonly Hook hookUiModuleExecuteMainCommand; @@ -45,9 +45,7 @@ internal sealed unsafe class DalamudAtkTweaks : IInternalDisposableService [ServiceManager.ServiceConstructor] private DalamudAtkTweaks(TargetSigScanner sigScanner) { - var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CF 4C 89 B4 24 B8 08 00 00"); - - this.hookAgentHudOpenSystemMenu = Hook.FromAddress(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour); + this.hookAgentHudOpenSystemMenu = Hook.FromAddress(AgentHUD.Addresses.OpenSystemMenu.Value, this.AgentHudOpenSystemMenuDetour); this.hookUiModuleExecuteMainCommand = Hook.FromAddress((nint)UIModule.StaticVirtualTablePointer->ExecuteMainCommand, this.UiModuleExecuteMainCommandDetour); this.hookAtkUnitBaseReceiveGlobalEvent = Hook.FromAddress((nint)AtkUnitBase.StaticVirtualTablePointer->ReceiveGlobalEvent, this.AtkUnitBaseReceiveGlobalEventDetour); diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs index b51232ece..5022eb93d 100644 --- a/Dalamud/Game/Network/GameNetwork.cs +++ b/Dalamud/Game/Network/GameNetwork.cs @@ -6,6 +6,9 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Network; + using Serilog; namespace Dalamud.Game.Network; @@ -14,10 +17,10 @@ namespace Dalamud.Game.Network; /// This class handles interacting with game network events. /// [ServiceManager.EarlyLoadedService] -internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork +internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetwork { private readonly GameNetworkAddressResolver address; - private readonly Hook processZonePacketDownHook; + private readonly Hook processZonePacketDownHook; private readonly Hook processZonePacketUpHook; private readonly HitchDetector hitchDetectorUp; @@ -25,11 +28,9 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); - - private IntPtr baseAddress; - + [ServiceManager.ServiceConstructor] - private GameNetwork(TargetSigScanner sigScanner) + private unsafe GameNetwork(TargetSigScanner sigScanner) { this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch); this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch); @@ -37,20 +38,19 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork this.address = new GameNetworkAddressResolver(); this.address.Setup(sigScanner); + var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket; + Log.Verbose("===== G A M E N E T W O R K ====="); - Log.Verbose($"ProcessZonePacketDown address {Util.DescribeAddress(this.address.ProcessZonePacketDown)}"); + Log.Verbose($"OnReceivePacket address {Util.DescribeAddress(onReceivePacketAddress)}"); Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}"); - this.processZonePacketDownHook = Hook.FromAddress(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour); + this.processZonePacketDownHook = Hook.FromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour); this.processZonePacketUpHook = Hook.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour); this.processZonePacketDownHook.Enable(); this.processZonePacketUpHook.Enable(); } - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - private delegate void ProcessZonePacketDownDelegate(IntPtr a, uint targetId, IntPtr dataPtr); - [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4); @@ -64,10 +64,8 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork this.processZonePacketUpHook.Dispose(); } - private void ProcessZonePacketDownDetour(IntPtr a, uint targetId, IntPtr dataPtr) + private void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr) { - this.baseAddress = a; - this.hitchDetectorDown.Start(); // Go back 0x10 to get back to the start of the packet header @@ -78,7 +76,7 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork // Call events this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr, 0x12), 0, targetId, NetworkMessageDirection.ZoneDown); - this.processZonePacketDownHook.Original(a, targetId, dataPtr + 0x10); + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); } catch (Exception ex) { @@ -96,7 +94,7 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); - this.processZonePacketDownHook.Original(a, targetId, dataPtr + 0x10); + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); } this.hitchDetectorDown.Stop(); diff --git a/Dalamud/Game/Network/GameNetworkAddressResolver.cs b/Dalamud/Game/Network/GameNetworkAddressResolver.cs index 69b97c59d..de92f7c10 100644 --- a/Dalamud/Game/Network/GameNetworkAddressResolver.cs +++ b/Dalamud/Game/Network/GameNetworkAddressResolver.cs @@ -5,11 +5,6 @@ namespace Dalamud.Game.Network; /// internal sealed class GameNetworkAddressResolver : BaseAddressResolver { - /// - /// Gets the address of the ProcessZonePacketDown method. - /// - public IntPtr ProcessZonePacketDown { get; private set; } - /// /// Gets the address of the ProcessZonePacketUp method. /// @@ -18,9 +13,6 @@ internal sealed class GameNetworkAddressResolver : BaseAddressResolver /// protected override void Setup64Bit(ISigScanner sig) { - // ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 7A FF 0F B7 57 02 8D 42 89 3D 5F 02 00 00 0F 87 60 01 00 00 4C 8D 05"); - // ProcessZonePacket = sig.ScanText("48 89 74 24 18 57 48 83 EC 50 8B F2 49 8B F8 41 0F B7 50 02 8B CE E8 ?? ?? 73 FF 0F B7 57 02 8D 42 ?? 3D ?? ?? 00 00 0F 87 60 01 00 00 4C 8D 05"); - this.ProcessZonePacketDown = sig.ScanText("40 53 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 44 24 ?? 8B F2"); - this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); + this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); // unnamed in cs } } diff --git a/Dalamud/Game/Network/Internal/NetworkHandlers.cs b/Dalamud/Game/Network/Internal/NetworkHandlers.cs index 2017be4bf..a25444f10 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlers.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlers.cs @@ -15,6 +15,9 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Networking.Http; using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Client.Game.InstanceContent; +using FFXIVClientStructs.FFXIV.Client.Network; using FFXIVClientStructs.FFXIV.Client.UI.Info; using Lumina.Excel.Sheets; using Serilog; @@ -35,13 +38,13 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private readonly NetworkHandlersAddressResolver addressResolver; - private readonly Hook cfPopHook; - private readonly Hook mbPurchaseHook; - private readonly Hook mbHistoryHook; + private readonly Hook cfPopHook; + private readonly Hook mbPurchaseHook; + private readonly Hook mbHistoryHook; private readonly Hook customTalkHook; // used for marketboard taxes - private readonly Hook mbItemRequestStartHook; - private readonly Hook mbOfferingsHook; - private readonly Hook mbSendPurchaseRequestHook; + private readonly Hook mbItemRequestStartHook; + private readonly Hook mbOfferingsHook; + private readonly Hook mbSendPurchaseRequestHook; [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -134,14 +137,14 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.handleMarketBoardPurchaseHandler = this.HandleMarketBoardPurchaseHandler(); this.mbPurchaseHook = - Hook.FromAddress( - this.addressResolver.MarketBoardPurchasePacketHandler, + Hook.FromAddress( + PacketDispatcher.Addresses.HandleMarketBoardPurchasePacket.Value, this.MarketPurchasePacketDetour); this.mbPurchaseHook.Enable(); this.mbHistoryHook = - Hook.FromAddress( - this.addressResolver.MarketBoardHistoryPacketHandler, + Hook.FromAddress( + InfoProxyItemSearch.Addresses.ProcessItemHistory.Value, this.MarketHistoryPacketDetour); this.mbHistoryHook.Enable(); @@ -151,22 +154,22 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.CustomTalkReceiveResponseDetour); this.customTalkHook.Enable(); - this.mbItemRequestStartHook = Hook.FromAddress( - this.addressResolver.MarketBoardItemRequestStartPacketHandler, + this.mbItemRequestStartHook = Hook.FromAddress( + PacketDispatcher.Addresses.HandleMarketBoardItemRequestStartPacket.Value, this.MarketItemRequestStartDetour); this.mbItemRequestStartHook.Enable(); - this.mbOfferingsHook = Hook.FromAddress( - this.addressResolver.InfoProxyItemSearchAddPage, + this.mbOfferingsHook = Hook.FromAddress( + (nint)InfoProxyItemSearch.StaticVirtualTablePointer->AddPage, this.MarketBoardOfferingsDetour); this.mbOfferingsHook.Enable(); - this.mbSendPurchaseRequestHook = Hook.FromAddress( - this.addressResolver.BuildMarketBoardPurchaseHandlerPacket, + this.mbSendPurchaseRequestHook = Hook.FromAddress( + InfoProxyItemSearch.Addresses.SendPurchaseRequestPacket.Value, this.MarketBoardSendPurchaseRequestDetour); this.mbSendPurchaseRequestHook.Enable(); - this.cfPopHook = Hook.FromAddress(this.addressResolver.CfPopPacketHandler, this.CfPopDetour); + this.cfPopHook = Hook.FromAddress(PublicContentDirector.Addresses.HandleEnterContentInfoPacket.Value, this.CfPopDetour); this.cfPopHook.Enable(); } @@ -183,8 +186,6 @@ internal unsafe class NetworkHandlers : IInternalDisposableService private delegate byte MarketBoardSendPurchaseRequestPacket(InfoProxyItemSearch* infoProxy); - private delegate nint CfPopDelegate(nint packetData); - /// /// Event which gets fired when a duty is ready. /// @@ -263,7 +264,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.cfPopHook.Dispose(); } - private unsafe nint CfPopDetour(nint packetData) + private unsafe nint CfPopDetour(PublicContentDirector.EnterContentInfoPacket* packetData) { var result = this.cfPopHook.OriginalDisposeSafe(packetData); @@ -529,7 +530,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService return this.configuration.IsMbCollect; } - private nint MarketPurchasePacketDetour(nint a1, nint packetData) + private void MarketPurchasePacketDetour(PacketDispatcher* a1, nint packetData) { try { @@ -540,10 +541,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketPurchasePacketHandler threw an exception"); } - return this.mbPurchaseHook.OriginalDisposeSafe(a1, packetData); + this.mbPurchaseHook.OriginalDisposeSafe(a1, packetData); } - private nint MarketHistoryPacketDetour(nint a1, nint packetData, uint a3, char a4) + private void MarketHistoryPacketDetour(InfoProxyItemSearch* a1, nint packetData) { try { @@ -554,7 +555,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketHistoryPacketDetour threw an exception"); } - return this.mbHistoryHook.OriginalDisposeSafe(a1, packetData, a3, a4); + this.mbHistoryHook.OriginalDisposeSafe(a1, packetData); } private void CustomTalkReceiveResponseDetour(nuint a1, ushort eventId, byte responseId, uint* args, byte argCount) @@ -573,7 +574,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService this.customTalkHook.OriginalDisposeSafe(a1, eventId, responseId, args, argCount); } - private nint MarketItemRequestStartDetour(nint a1, nint packetRef) + private void MarketItemRequestStartDetour(PacketDispatcher* a1, nint packetRef) { try { @@ -584,10 +585,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketItemRequestStartDetour threw an exception"); } - return this.mbItemRequestStartHook.OriginalDisposeSafe(a1, packetRef); + this.mbItemRequestStartHook.OriginalDisposeSafe(a1, packetRef); } - private byte MarketBoardOfferingsDetour(nint a1, nint packetRef) + private void MarketBoardOfferingsDetour(InfoProxyItemSearch* a1, nint packetRef) { try { @@ -598,10 +599,10 @@ internal unsafe class NetworkHandlers : IInternalDisposableService Log.Error(ex, "MarketBoardOfferingsDetour threw an exception"); } - return this.mbOfferingsHook.OriginalDisposeSafe(a1, packetRef); + this.mbOfferingsHook.OriginalDisposeSafe(a1, packetRef); } - private byte MarketBoardSendPurchaseRequestDetour(InfoProxyItemSearch* infoProxyItemSearch) + private bool MarketBoardSendPurchaseRequestDetour(InfoProxyItemSearch* infoProxyItemSearch) { try { diff --git a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs index 166b4d67a..18c48b67d 100644 --- a/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs +++ b/Dalamud/Game/Network/Internal/NetworkHandlersAddressResolver.cs @@ -5,62 +5,17 @@ /// internal class NetworkHandlersAddressResolver : BaseAddressResolver { - /// - /// Gets or sets the pointer to the method responsible for handling CfPop packets. - /// - public nint CfPopPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for handling market board history. In this case, we are - /// sigging the packet handler method directly. - /// - public nint MarketBoardHistoryPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for processing the market board purchase packet. In this - /// case, we are sigging the packet handler method directly. - /// - public nint MarketBoardPurchasePacketHandler { get; set; } - /// /// Gets or sets the pointer to the method responsible for custom talk events. Necessary for marketboard tax data, /// as this isn't really exposed anywhere else. /// public nint CustomTalkEventResponsePacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the method responsible for the marketboard ItemRequestStart packet. - /// - public nint MarketBoardItemRequestStartPacketHandler { get; set; } - - /// - /// Gets or sets the pointer to the InfoProxyItemSearch.AddPage method, used to load market data. - /// - public nint InfoProxyItemSearchAddPage { get; set; } - - /// - /// Gets or sets the pointer to the method inside InfoProxyItemSearch that is responsible for building and sending - /// a purchase request packet. - /// - public nint BuildMarketBoardPurchaseHandlerPacket { get; set; } /// protected override void Setup64Bit(ISigScanner scanner) { - this.CfPopPacketHandler = scanner.ScanText("40 53 57 48 83 EC 78 48 8B D9 48 8D 0D"); - - // TODO: I know this is a CC. I want things working for now. (KW) - this.MarketBoardHistoryPacketHandler = scanner.ScanText( - "40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 85 C0 74 2F 4C 8B 00 48 8B C8 41 FF 90 18 01 00 00 48 8B C8 BA 0B 00 00 00 E8 ?? ?? ?? ?? 48 85 C0 74 10 48 8B D3 48 8B C8 48 83 C4 20 5B E9 ?? ?? ?? ?? 48 83 C4 20 5B C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 40 53"); - this.MarketBoardPurchasePacketHandler = - scanner.ScanText("40 55 56 41 56 48 8B EC 48 83 EC ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 ?? 48 8B 0D ?? ?? ?? ?? 4C 8B F2"); this.CustomTalkEventResponsePacketHandler = - scanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24"); - this.MarketBoardItemRequestStartPacketHandler = - scanner.ScanText("48 89 5C 24 08 57 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B FA E8 ?? ?? ?? ?? 48 8B D8 48 85 C0 74 4A"); - this.InfoProxyItemSearchAddPage = - scanner.ScanText("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 0F B6 82 ?? ?? ?? ?? 48 8B FA 48 8B D9 38 41 19 74 54"); - this.BuildMarketBoardPurchaseHandlerPacket = - scanner.ScanText("40 53 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 8B D0 48 85 C0 0F 84 ?? ?? ?? ?? 8B 8B"); + scanner.ScanText( + "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24"); // unnamed in CS } } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 78c39ef13..121f2f8d8 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 78c39ef1318525d766cdf31fd9a32e6f1c7cb453 +Subproject commit 121f2f8d82bce8632e79790a002d5e1ac17a635c From 913fe25cc562119e5fe864b02efc7c2197c4f111 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 08:47:32 -0800 Subject: [PATCH 05/14] InventoryItem size bump --- Dalamud/Game/Inventory/GameInventoryItem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs index 53aa9a9d9..145ddf841 100644 --- a/Dalamud/Game/Inventory/GameInventoryItem.cs +++ b/Dalamud/Game/Inventory/GameInventoryItem.cs @@ -18,7 +18,7 @@ public unsafe struct GameInventoryItem : IEquatable [FieldOffset(0)] internal readonly InventoryItem InternalItem; - private const int StructSizeInBytes = 0x40; + private const int StructSizeInBytes = 0x48; /// /// The view of the backing data, in . From 76df39f7b3e105d9c479ee3d59453267903dfc63 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 10:36:32 -0800 Subject: [PATCH 06/14] bump cs again --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 121f2f8d8..8ed804220 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 121f2f8d82bce8632e79790a002d5e1ac17a635c +Subproject commit 8ed8042205701aa5a1eb32c0fe437e006202fb06 From 7feeae99101b5e729cfbc2d279c238fbc70c2e81 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 15:21:07 -0800 Subject: [PATCH 07/14] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 8ed804220..6cc4f1c12 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 8ed8042205701aa5a1eb32c0fe437e006202fb06 +Subproject commit 6cc4f1c12c37785101a262cceac249753db3b1f5 From b483b63bf2d82327580c6e5f0c784a7fae7db897 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 15:34:56 -0800 Subject: [PATCH 08/14] GameInventoryItem matches StructSize from CS --- Dalamud/Game/Inventory/GameInventoryItem.cs | 20 +++++--------------- lib/FFXIVClientStructs | 2 +- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Dalamud/Game/Inventory/GameInventoryItem.cs b/Dalamud/Game/Inventory/GameInventoryItem.cs index 145ddf841..fb27346e0 100644 --- a/Dalamud/Game/Inventory/GameInventoryItem.cs +++ b/Dalamud/Game/Inventory/GameInventoryItem.cs @@ -9,7 +9,7 @@ namespace Dalamud.Game.Inventory; /// /// Dalamud wrapper around a ClientStructs InventoryItem. /// -[StructLayout(LayoutKind.Explicit, Size = StructSizeInBytes)] +[StructLayout(LayoutKind.Explicit, Size = InventoryItem.StructSize)] public unsafe struct GameInventoryItem : IEquatable { /// @@ -17,22 +17,12 @@ public unsafe struct GameInventoryItem : IEquatable /// [FieldOffset(0)] internal readonly InventoryItem InternalItem; - - private const int StructSizeInBytes = 0x48; - + /// /// The view of the backing data, in . /// [FieldOffset(0)] - private fixed ulong dataUInt64[StructSizeInBytes / 0x8]; - - static GameInventoryItem() - { - Debug.Assert( - sizeof(InventoryItem) == StructSizeInBytes, - $"Definition of {nameof(InventoryItem)} has been changed. " + - $"Update {nameof(StructSizeInBytes)} to {sizeof(InventoryItem)} to accommodate for the size change."); - } + private fixed ulong dataUInt64[InventoryItem.StructSize / 0x8]; /// /// Initializes a new instance of the struct. @@ -157,7 +147,7 @@ public unsafe struct GameInventoryItem : IEquatable /// true if the current object is equal to the parameter; otherwise, false. public readonly bool Equals(in GameInventoryItem other) { - for (var i = 0; i < StructSizeInBytes / 8; i++) + for (var i = 0; i < InventoryItem.StructSize / 8; i++) { if (this.dataUInt64[i] != other.dataUInt64[i]) return false; @@ -173,7 +163,7 @@ public unsafe struct GameInventoryItem : IEquatable public override int GetHashCode() { var k = 0x5a8447b91aff51b4UL; - for (var i = 0; i < StructSizeInBytes / 8; i++) + for (var i = 0; i < InventoryItem.StructSize / 8; i++) k ^= this.dataUInt64[i]; return unchecked((int)(k ^ (k >> 32))); } diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 6cc4f1c12..81ea903b7 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 6cc4f1c12c37785101a262cceac249753db3b1f5 +Subproject commit 81ea903b7dc90bd07a38703738fa7f84b1ed0775 From 94f16ac16e3e44c822665641983966239e96eff6 Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Fri, 15 Nov 2024 00:36:10 +0100 Subject: [PATCH 09/14] Alias ClientState.IsLoggedIn to AgentLobby.IsLoggedIn (#2082) --- Dalamud/Game/ClientState/ClientState.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 6ceea4c6b..364466cce 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Runtime.InteropServices; using Dalamud.Data; using Dalamud.Game.ClientState.Conditions; @@ -71,7 +70,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.processPacketPlayerSetupHook.Enable(); this.onLogoutHook.Enable(); } - + private unsafe delegate void ProcessPacketPlayerSetupDelegate(nint a1, nint packet); /// @@ -121,7 +120,14 @@ internal sealed class ClientState : IInternalDisposableService, IClientState public unsafe ulong LocalContentId => PlayerState.Instance()->ContentId; /// - public bool IsLoggedIn { get; private set; } + public unsafe bool IsLoggedIn + { + get + { + var agentLobby = AgentLobby.Instance(); + return agentLobby != null && agentLobby->IsLoggedIn; + } + } /// public bool IsPvP { get; private set; } @@ -259,7 +265,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState try { Log.Debug("Login"); - this.IsLoggedIn = true; this.Login?.InvokeSafely(); gameGui?.ResetUiHideState(); this.lifecycle.ResetLogout(); @@ -283,8 +288,6 @@ internal sealed class ClientState : IInternalDisposableService, IClientState Log.Debug("Logout: Type {type}, Code {code}", type, code); - this.IsLoggedIn = false; - if (this.Logout is { } callback) { foreach (var action in callback.GetInvocationList().Cast()) From ee63f6087796c69db922b849008169f561d10e20 Mon Sep 17 00:00:00 2001 From: ItsBexy <103910869+ItsBexy@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:36:27 -0700 Subject: [PATCH 10/14] Update UIDebug2, ImGuiComponents, ImGuiHelpers (#2081) * Update ImGuiComponents & ImGuiHelpers Took some helper functions created for `UiDebug2`, and incorporated them into `ImGuiComponents` / `ImGuiHelpers` instead - `IconButton()` (and its various overloads) now includes an optional size parameter - `IconButtonSelect()` has been added, allowing a row or grid of IconButtons to serve as a radio-like input - `HelpMarker()` now includes an optional color parameter - `ClickToCopyText()` now includes an optional color parameter, and includes the `FontAwesome.Copy` icon in the tooltip. - Implemented ImRaii in these files These changes are intended not to break any existing calls in plugins or within Dalamud itself. * Fix ambiguous overloads * UiDebug2 Updates - Fixed XY coordinate display - Added AtkValue table to AddonTree display - Restored old behaviour wherein the Addon display initially only shows the Root node and a collapsed node list - The above should also fix the Element Selector / Search behaviour, allowing it to scroll correctly to the searched node - Now displays field offsets for any node/component whose pointer exists in the addon struct - Tidied up node tree headers by removing memory addresses (they're still readable in the opened tree, of course) --- .../ImGuiComponents.ColorPickerWithPalette.cs | 8 +- .../ImGuiComponents.DisabledButton.cs | 54 ++-- .../Components/ImGuiComponents.HelpMarker.cs | 39 ++- .../Components/ImGuiComponents.IconButton.cs | 284 +++++++++++------- .../ImGuiComponents.IconButtonSelect.cs | 87 ++++++ .../ImGuiComponents.TextWithLabel.cs | 10 +- .../ImGuiComponents.ToggleSwitch.cs | 7 +- .../UiDebug2/Browsing/AddonTree.AtkValues.cs | 124 ++++++++ .../UiDebug2/Browsing/AddonTree.FieldNames.cs | 12 + .../Internal/UiDebug2/Browsing/AddonTree.cs | 53 ++-- .../Internal/UiDebug2/Browsing/Events.cs | 8 +- .../UiDebug2/Browsing/NodeTree.Component.cs | 38 ++- .../UiDebug2/Browsing/NodeTree.Editor.cs | 5 +- .../UiDebug2/Browsing/NodeTree.Res.cs | 55 +++- .../Internal/UiDebug2/ElementSelector.cs | 2 +- .../Internal/UiDebug2/Utility/Gui.cs | 99 ++---- .../Internal/UiDebug2/Utility/NodeBounds.cs | 10 +- Dalamud/Interface/Utility/ImGuiHelpers.cs | 32 +- 18 files changed, 649 insertions(+), 278 deletions(-) create mode 100644 Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs create mode 100644 Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs diff --git a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs index aa707aecb..e2f68eab2 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.ColorPickerWithPalette.cs @@ -1,6 +1,8 @@ using System.Numerics; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -41,7 +43,9 @@ public static partial class ImGuiComponents ImGui.OpenPopup($"###ColorPickerPopup{id}"); } - if (ImGui.BeginPopup($"###ColorPickerPopup{id}")) + using var popup = ImRaii.Popup($"###ColorPickerPopup{id}"); + + if (popup) { if (ImGui.ColorPicker4($"###ColorPicker{id}", ref existingColor, flags)) { @@ -61,8 +65,6 @@ public static partial class ImGuiComponents ImGui.SameLine(); } } - - ImGui.EndPopup(); } return selectedColor; diff --git a/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs b/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs index 907ad0aeb..ab2ed4724 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.DisabledButton.cs @@ -1,5 +1,7 @@ using System.Numerics; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -21,17 +23,16 @@ public static partial class ImGuiComponents /// Indicator if button is clicked. public static bool DisabledButton(FontAwesomeIcon icon, int? id = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f) { - ImGui.PushFont(UiBuilder.IconFont); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var text = icon.ToIconString(); + if (id.HasValue) + { + text = $"{text}##{id}"; + } - var text = icon.ToIconString(); - if (id.HasValue) - text = $"{text}##{id}"; - - var button = DisabledButton(text, defaultColor, activeColor, hoveredColor, alphaMult); - - ImGui.PopFont(); - - return button; + return DisabledButton(text, defaultColor, activeColor, hoveredColor, alphaMult); + } } /// @@ -45,31 +46,28 @@ public static partial class ImGuiComponents /// Indicator if button is clicked. public static bool DisabledButton(string labelWithId, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, float alphaMult = .5f) { + using var col = new ImRaii.Color(); + if (defaultColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); + { + col.Push(ImGuiCol.Button, defaultColor.Value); + } if (activeColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); + { + col.Push(ImGuiCol.ButtonActive, activeColor.Value); + } if (hoveredColor.HasValue) - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); + { + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); + } var style = ImGui.GetStyle(); - ImGui.PushStyleVar(ImGuiStyleVar.Alpha, style.Alpha * alphaMult); - var button = ImGui.Button(labelWithId); - - ImGui.PopStyleVar(); - - if (defaultColor.HasValue) - ImGui.PopStyleColor(); - - if (activeColor.HasValue) - ImGui.PopStyleColor(); - - if (hoveredColor.HasValue) - ImGui.PopStyleColor(); - - return button; + using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, style.Alpha * alphaMult)) + { + return ImGui.Button(labelWithId); + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs index f0ecf2f2f..3392136d1 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.HelpMarker.cs @@ -1,3 +1,7 @@ +using Dalamud.Interface.Utility.Raii; + +using FFXIVClientStructs.FFXIV.Common.Math; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -18,17 +22,32 @@ public static partial class ImGuiComponents /// /// The text to display on hover. /// The icon to use. - public static void HelpMarker(string helpText, FontAwesomeIcon icon) + /// The color of the icon. + public static void HelpMarker(string helpText, FontAwesomeIcon icon, Vector4? color = null) { + using var col = new ImRaii.Color(); + + if (color.HasValue) + { + col.Push(ImGuiCol.TextDisabled, color.Value); + } + ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.TextDisabled(icon.ToIconString()); - ImGui.PopFont(); - if (!ImGui.IsItemHovered()) return; - ImGui.BeginTooltip(); - ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f); - ImGui.TextUnformatted(helpText); - ImGui.PopTextWrapPos(); - ImGui.EndTooltip(); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.TextDisabled(icon.ToIconString()); + } + + if (ImGui.IsItemHovered()) + { + using (ImRaii.Tooltip()) + { + using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f)) + { + ImGui.TextUnformatted(helpText); + } + } + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs index dc2c99608..5e64fe463 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButton.cs @@ -1,6 +1,8 @@ using System.Numerics; using Dalamud.Interface.Utility; +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -15,8 +17,26 @@ public static partial class ImGuiComponents /// /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(FontAwesomeIcon icon) - => IconButton(icon, null, null, null); + public static bool IconButton(FontAwesomeIcon icon) => IconButton(icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(FontAwesomeIcon icon, Vector2 size) => IconButton(icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button. + /// + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) => IconButton($"{icon.ToIconString()}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. @@ -24,8 +44,28 @@ public static partial class ImGuiComponents /// The ID of the button. /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(int id, FontAwesomeIcon icon) - => IconButton(id, icon, null, null, null); + public static bool IconButton(int id, FontAwesomeIcon icon) => IconButton(id, icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The ID of the button. + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(int id, FontAwesomeIcon icon, Vector2 size) => IconButton(id, icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button with color options. + /// + /// The ID of the button. + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(int id, FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. @@ -33,51 +73,45 @@ public static partial class ImGuiComponents /// The ID of the button. /// The icon for the button. /// Indicator if button is clicked. - public static bool IconButton(string id, FontAwesomeIcon icon) - => IconButton(id, icon, null, null, null); + public static bool IconButton(string id, FontAwesomeIcon icon) => IconButton(id, icon, null); + + /// + /// IconButton component to use an icon as a button. + /// + /// The ID of the button. + /// The icon for the button. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(string id, FontAwesomeIcon icon, Vector2 size) + => IconButton(id, icon, null, null, null, size); + + /// + /// IconButton component to use an icon as a button with color options. + /// + /// The ID of the button. + /// The icon for the button. + /// The default color of the button. + /// The color of the button when active. + /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// Indicator if button is clicked. + public static bool IconButton(string id, FontAwesomeIcon icon, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) + => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor, size); /// /// IconButton component to use an icon as a button. /// /// Text already containing the icon string. /// Indicator if button is clicked. - public static bool IconButton(string iconText) - => IconButton(iconText, null, null, null); + public static bool IconButton(string iconText) => IconButton(iconText, null); /// /// IconButton component to use an icon as a button. /// - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. + /// Text already containing the icon string. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. /// Indicator if button is clicked. - public static bool IconButton(FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}", defaultColor, activeColor, hoveredColor); - - /// - /// IconButton component to use an icon as a button with color options. - /// - /// The ID of the button. - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. - /// Indicator if button is clicked. - public static bool IconButton(int id, FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor); - - /// - /// IconButton component to use an icon as a button with color options. - /// - /// The ID of the button. - /// The icon for the button. - /// The default color of the button. - /// The color of the button when active. - /// The color of the button when hovered. - /// Indicator if button is clicked. - public static bool IconButton(string id, FontAwesomeIcon icon, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) - => IconButton($"{icon.ToIconString()}##{id}", defaultColor, activeColor, hoveredColor); + public static bool IconButton(string iconText, Vector2 size) => IconButton(iconText, null, null, null, size); /// /// IconButton component to use an icon as a button with color options. @@ -86,62 +120,72 @@ public static partial class ImGuiComponents /// The default color of the button. /// The color of the button when active. /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon. /// Indicator if button is clicked. - public static bool IconButton(string iconText, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + public static bool IconButton(string iconText, Vector4? defaultColor, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) { - var numColors = 0; + using var col = new ImRaii.Color(); if (defaultColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); - numColors++; + col.Push(ImGuiCol.Button, defaultColor.Value); } if (activeColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonActive, activeColor.Value); } if (hoveredColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); + } + + if (size.HasValue) + { + size *= ImGuiHelpers.GlobalScale; } var icon = iconText; - if (icon.Contains("#")) - icon = icon[..icon.IndexOf("#", StringComparison.Ordinal)]; + if (icon.Contains('#')) + { + icon = icon[..icon.IndexOf('#', StringComparison.Ordinal)]; + } - ImGui.PushID(iconText); + bool button; - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon); - ImGui.PopFont(); - - var dl = ImGui.GetWindowDrawList(); - var cursor = ImGui.GetCursorScreenPos(); - - // Draw an ImGui button with the icon and text - var buttonWidth = iconSize.X + (ImGui.GetStyle().FramePadding.X * 2); - var buttonHeight = ImGui.GetFrameHeight(); - var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); - - // Draw the icon on the window drawlist - var iconPos = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X, cursor.Y + ImGui.GetStyle().FramePadding.Y); - - ImGui.PushFont(UiBuilder.IconFont); - dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon); - ImGui.PopFont(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var iconSize = ImGui.CalcTextSize(icon); + var cursor = ImGui.GetCursorScreenPos(); - ImGui.PopID(); + var width = size is { X: not 0 } ? size.Value.X : iconSize.X + (ImGui.GetStyle().FramePadding.X * 2); + var height = size is { Y: not 0 } ? size.Value.Y : ImGui.GetFrameHeight(); - if (numColors > 0) - ImGui.PopStyleColor(numColors); + var buttonSize = new Vector2(width, height); + + using (ImRaii.PushId(iconText)) + { + button = ImGui.Button(string.Empty, buttonSize); + } + + var iconPos = cursor + ((buttonSize - iconSize) / 2f); + + ImGui.GetWindowDrawList().AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon); + } return button; } + /// + /// IconButton component to use an icon as a button with color options. + /// + /// Icon to show. + /// Text to show. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon & text. + /// Indicator if button is clicked. + public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector2 size) => IconButtonWithText(icon, text, null, null, null, size); + /// /// IconButton component to use an icon as a button with color options. /// @@ -150,61 +194,72 @@ public static partial class ImGuiComponents /// The default color of the button. /// The color of the button when active. /// The color of the button when hovered. + /// Sets the size of the button. If either dimension is set to 0, that dimension will conform to the size of the icon & text. /// Indicator if button is clicked. - public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + public static bool IconButtonWithText(FontAwesomeIcon icon, string text, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null, Vector2? size = null) { - var numColors = 0; + using var col = new ImRaii.Color(); if (defaultColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.Button, defaultColor.Value); - numColors++; + col.Push(ImGuiCol.Button, defaultColor.Value); } if (activeColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonActive, activeColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonActive, activeColor.Value); } if (hoveredColor.HasValue) { - ImGui.PushStyleColor(ImGuiCol.ButtonHovered, hoveredColor.Value); - numColors++; + col.Push(ImGuiCol.ButtonHovered, hoveredColor.Value); } - ImGui.PushID(text); + if (size.HasValue) + { + size *= ImGuiHelpers.GlobalScale; + } + + bool button; + + Vector2 iconSize; + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + iconSize = ImGui.CalcTextSize(icon.ToIconString()); + } + + var textStr = text; + if (textStr.Contains('#')) + { + textStr = textStr[..textStr.IndexOf('#', StringComparison.Ordinal)]; + } + + var framePadding = ImGui.GetStyle().FramePadding; + var iconPadding = 3 * ImGuiHelpers.GlobalScale; - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - ImGui.PopFont(); - - var textSize = ImGui.CalcTextSize(text); - var dl = ImGui.GetWindowDrawList(); var cursor = ImGui.GetCursorScreenPos(); - var iconPadding = 3 * ImGuiHelpers.GlobalScale; - - // Draw an ImGui button with the icon and text - var buttonWidth = iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; - var buttonHeight = ImGui.GetFrameHeight(); - var button = ImGui.Button(string.Empty, new Vector2(buttonWidth, buttonHeight)); - - // Draw the icon on the window drawlist - var iconPos = new Vector2(cursor.X + ImGui.GetStyle().FramePadding.X, cursor.Y + ImGui.GetStyle().FramePadding.Y); - - ImGui.PushFont(UiBuilder.IconFont); - dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); - ImGui.PopFont(); - - // Draw the text on the window drawlist - var textPos = new Vector2(iconPos.X + iconSize.X + iconPadding, cursor.Y + ImGui.GetStyle().FramePadding.Y); - dl.AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), text); + using (ImRaii.PushId(text)) + { + var textSize = ImGui.CalcTextSize(textStr); - ImGui.PopID(); + var width = size is { X: not 0 } ? size.Value.X : iconSize.X + textSize.X + (framePadding.X * 2) + iconPadding; + var height = size is { Y: not 0 } ? size.Value.Y : ImGui.GetFrameHeight(); - if (numColors > 0) - ImGui.PopStyleColor(numColors); + button = ImGui.Button(string.Empty, new Vector2(width, height)); + } + + var iconPos = cursor + framePadding; + var textPos = new Vector2(iconPos.X + iconSize.X + iconPadding, cursor.Y + framePadding.Y); + + var dl = ImGui.GetWindowDrawList(); + + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + dl.AddText(iconPos, ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString()); + } + + dl.AddText(textPos, ImGui.GetColorU32(ImGuiCol.Text), textStr); return button; } @@ -217,16 +272,15 @@ public static partial class ImGuiComponents /// Width. internal static float GetIconButtonWithTextWidth(FontAwesomeIcon icon, string text) { - ImGui.PushFont(UiBuilder.IconFont); - var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - ImGui.PopFont(); - - var textSize = ImGui.CalcTextSize(text); - var dl = ImGui.GetWindowDrawList(); - var cursor = ImGui.GetCursorScreenPos(); + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + var iconSize = ImGui.CalcTextSize(icon.ToIconString()); - var iconPadding = 3 * ImGuiHelpers.GlobalScale; - - return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; + var textSize = ImGui.CalcTextSize(text); + + var iconPadding = 3 * ImGuiHelpers.GlobalScale; + + return iconSize.X + textSize.X + (ImGui.GetStyle().FramePadding.X * 2) + iconPadding; + } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs b/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs new file mode 100644 index 000000000..3f9c469bb --- /dev/null +++ b/Dalamud/Interface/Components/ImGuiComponents.IconButtonSelect.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Numerics; + +using Dalamud.Interface.Utility; + +using ImGuiNET; + +namespace Dalamud.Interface.Components; + +public static partial class ImGuiComponents +{ + /// + /// A radio-like input that uses icon buttons. + /// + /// The type of the value being set. + /// Text that will be used to generate individual labels for the buttons. + /// The value to set. + /// The icons that will be displayed on each button. + /// The options that each button will apply. + /// Arranges the buttons in a grid with the given number of columns. 0 = ignored (all buttons drawn in one row). + /// Sets the size of all buttons. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// The default color of the button range. + /// The color of the actively-selected button. + /// The color of the buttons when hovered. + /// True if any button is clicked. + internal static bool IconButtonSelect(string label, ref T val, IEnumerable optionIcons, IEnumerable optionValues, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + { + var options = optionIcons.Zip(optionValues, static (icon, value) => new KeyValuePair(icon, value)); + return IconButtonSelect(label, ref val, options, columns, buttonSize, defaultColor, activeColor, hoveredColor); + } + + /// + /// A radio-like input that uses icon buttons. + /// + /// The type of the value being set. + /// Text that will be used to generate individual labels for the buttons. + /// The value to set. + /// A list of all icon/option pairs. + /// Arranges the buttons in a grid with the given number of columns. 0 = ignored (all buttons drawn in one row). + /// Sets the size of all buttons. If either dimension is set to 0, that dimension will conform to the size of the icon. + /// The default color of the button range. + /// The color of the actively-selected button. + /// The color of the buttons when hovered. + /// True if any button is clicked. + internal static unsafe bool IconButtonSelect(string label, ref T val, IEnumerable> options, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) + { + defaultColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.Button); + activeColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonActive); + hoveredColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonHovered); + + var result = false; + + var innerSpacing = ImGui.GetStyle().ItemInnerSpacing; + var y = ImGui.GetCursorPosY(); + + var optArr = options.ToArray(); + for (var i = 0; i < optArr.Length; i++) + { + if (i > 0) + { + if (columns == 0 || i % columns != 0) + { + ImGui.SameLine(0, innerSpacing.X); + } + else + { + y += (buttonSize is { Y: not 0 } ? buttonSize.Value.Y * ImGuiHelpers.GlobalScale : ImGui.GetFrameHeight()) + innerSpacing.Y; + + ImGui.SetCursorPosY(y); + } + } + + optArr[i].Deconstruct(out var icon, out var option); + + var selected = val is not null && val.Equals(option); + + if (IconButton($"{label}{option}{i}", icon, selected ? activeColor : defaultColor, activeColor, hoveredColor, buttonSize)) + { + val = option; + result = true; + } + } + + return result; + } +} diff --git a/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs b/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs index 597b472c6..43b54fc93 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.TextWithLabel.cs @@ -1,3 +1,5 @@ +using Dalamud.Interface.Utility.Raii; + using ImGuiNET; namespace Dalamud.Interface.Components; @@ -24,7 +26,13 @@ public static partial class ImGuiComponents else { ImGui.Text(value + "*"); - if (ImGui.IsItemHovered()) ImGui.SetTooltip(hint); + if (ImGui.IsItemHovered()) + { + using (ImRaii.Tooltip()) + { + ImGui.TextUnformatted(hint); + } + } } } } diff --git a/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs b/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs index 64f3d01eb..6d6e0f6c3 100644 --- a/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs +++ b/Dalamud/Interface/Components/ImGuiComponents.ToggleSwitch.cs @@ -36,9 +36,14 @@ public static partial class ImGuiComponents } if (ImGui.IsItemHovered()) + { drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.ButtonActive] : new Vector4(0.78f, 0.78f, 0.78f, 1.0f)), height * 0.5f); + } else + { drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(!v ? colors[(int)ImGuiCol.Button] * 0.6f : new Vector4(0.35f, 0.35f, 0.35f, 1.0f)), height * 0.50f); + } + drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1))); return changed; @@ -62,7 +67,7 @@ public static partial class ImGuiComponents // TODO: animate ImGui.InvisibleButton(id, new Vector2(width, height)); - var dimFactor = 0.5f; + const float dimFactor = 0.5f; drawList.AddRectFilled(p, new Vector2(p.X + width, p.Y + height), ImGui.GetColorU32(v ? colors[(int)ImGuiCol.Button] * dimFactor : new Vector4(0.55f, 0.55f, 0.55f, 1.0f) * dimFactor), height * 0.50f); drawList.AddCircleFilled(new Vector2(p.X + radius + ((v ? 1 : 0) * (width - (radius * 2.0f))), p.Y + radius), radius - 1.5f, ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 1, 1) * dimFactor)); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs new file mode 100644 index 000000000..4b7a531c0 --- /dev/null +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.AtkValues.cs @@ -0,0 +1,124 @@ +using Dalamud.Interface.Internal.UiDebug2.Utility; +using Dalamud.Interface.Utility.Raii; +using Dalamud.Memory; +using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Component.GUI; + +using ImGuiNET; + +using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; + +namespace Dalamud.Interface.Internal.UiDebug2.Browsing; + +public unsafe partial class AddonTree +{ + /// + /// Prints a table of AtkValues associated with a given addon. + /// + /// The addon to look up. + internal static void PrintAtkValues(AtkUnitBase* addon) + { + var atkValue = addon->AtkValues; + if (addon->AtkValuesCount > 0 && atkValue != null) + { + using var tree = ImRaii.TreeNode($"Atk Values [{addon->AtkValuesCount}]###atkValues_{addon->NameString}"); + if (tree) + { + using (ImRaii.Table( + "atkUnitBase_atkValueTable", + 3, + ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg)) + { + ImGui.TableSetupColumn("Index"); + ImGui.TableSetupColumn("Type"); + ImGui.TableSetupColumn("Value"); + ImGui.TableHeadersRow(); + + try + { + for (var i = 0; i < addon->AtkValuesCount; i++) + { + ImGui.TableNextColumn(); + if (atkValue->Type == 0) + { + ImGui.TextDisabled($"#{i}"); + } + else + { + ImGui.Text($"#{i}"); + } + + ImGui.TableNextColumn(); + if (atkValue->Type == 0) + { + ImGui.TextDisabled("Not Set"); + } + else + { + ImGui.Text($"{atkValue->Type}"); + } + + ImGui.TableNextColumn(); + + switch (atkValue->Type) + { + case 0: + break; + case ValueType.Int: + case ValueType.UInt: + { + ImGui.TextUnformatted($"{atkValue->Int}"); + break; + } + + case ValueType.ManagedString: + case ValueType.String8: + case ValueType.String: + { + if (atkValue->String == null) + { + ImGui.TextDisabled("null"); + } + else + { + var str = MemoryHelper.ReadSeStringNullTerminated(new nint(atkValue->String)); + Util.ShowStruct(str, (ulong)atkValue); + } + + break; + } + + case ValueType.Bool: + { + ImGui.TextUnformatted($"{atkValue->Byte != 0}"); + break; + } + + case ValueType.Pointer: + ImGui.TextUnformatted($"{(nint)atkValue->Pointer}"); + break; + + default: + { + ImGui.TextDisabled("Unhandled Type"); + ImGui.SameLine(); + Util.ShowStruct(atkValue); + break; + } + } + + atkValue++; + } + } + catch (Exception ex) + { + ImGui.TextColored(new(1, 0, 0, 1), $"{ex}"); + } + } + } + + Gui.PaddedSeparator(); + } + } +} diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs index 8affa1eac..0b1dcb66c 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.FieldNames.cs @@ -23,6 +23,11 @@ public unsafe partial class AddonTree /// internal Dictionary> FieldNames { get; set; } = []; + /// + /// Gets or sets the size of the addon according to its Attributes in FFXIVClientStructs. + /// + internal int AddonSize { get; set; } + private object? GetAddonObj(AtkUnitBase* addon) { if (addon == null) @@ -42,6 +47,13 @@ public unsafe partial class AddonTree select t) { AddonTypeDict[this.AddonName] = t; + + var size = t.StructLayoutAttribute?.Size; + if (size != null) + { + this.AddonSize = size.Value; + } + break; } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs index 2823a2058..9d6575a55 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Numerics; using Dalamud.Interface.Components; + using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; @@ -19,14 +20,12 @@ namespace Dalamud.Interface.Internal.UiDebug2.Browsing; /// public unsafe partial class AddonTree : IDisposable { - private readonly nint initialPtr; - private AddonPopoutWindow? window; private AddonTree(string name, nint ptr) { this.AddonName = name; - this.initialPtr = ptr; + this.InitialPtr = ptr; this.PopulateFieldNames(ptr); } @@ -35,6 +34,11 @@ public unsafe partial class AddonTree : IDisposable /// internal string AddonName { get; init; } + /// + /// Gets the addon's pointer at the time this was created. + /// + internal nint InitialPtr { get; init; } + /// /// Gets or sets a collection of trees representing nodes within this addon. /// @@ -81,7 +85,7 @@ public unsafe partial class AddonTree : IDisposable { if (AddonTrees.TryGetValue(name, out var tree)) { - if (tree.initialPtr == ptr) + if (tree.InitialPtr == ptr) { return tree; } @@ -143,34 +147,47 @@ public unsafe partial class AddonTree : IDisposable ImGui.SetTooltip("Toggle Popout Window"); } - ImGui.Separator(); - - PrintFieldValuePair("Address", $"{(nint)addon:X}"); + PaddedSeparator(1); var uldManager = addon->UldManager; + + PrintFieldValuePair("Address", $"{(nint)addon:X}"); + PrintFieldValuePair("Agent", $"{GameGui.FindAgentInterface(addon):X}"); + PrintFieldValuePairs( ("X", $"{addon->X}"), - ("Y", $"{addon->X}"), + ("Y", $"{addon->Y}"), ("Scale", $"{addon->Scale}"), ("Widget Count", $"{uldManager.ObjectCount}")); - ImGui.Separator(); - var addonObj = this.GetAddonObj(addon); if (addonObj != null) { + PaddedSeparator(); ShowStruct(addonObj, (ulong)addon); } - ImGui.Dummy(new(25 * ImGui.GetIO().FontGlobalScale)); - ImGui.Separator(); + PaddedSeparator(); - ResNodeTree.PrintNodeList(uldManager.NodeList, uldManager.NodeListCount, this); + PrintAtkValues(addon); - ImGui.Dummy(new(25 * ImGui.GetIO().FontGlobalScale)); - ImGui.Separator(); + if (addon->RootNode != null) + { + ResNodeTree.GetOrCreate(addon->RootNode, this).Print(0); + PaddedSeparator(); + } - ResNodeTree.PrintNodeListAsTree(addon->CollisionNodeList, (int)addon->CollisionNodeListCount, "Collision List", this, new(0.5F, 0.7F, 1F, 1F)); + if (uldManager.NodeList != null) + { + var count = uldManager.NodeListCount; + ResNodeTree.PrintNodeListAsTree(uldManager.NodeList, count, $"Node List [{count}]:", this, new(0, 0.85F, 1, 1)); + PaddedSeparator(); + } + + if (addon->CollisionNodeList != null) + { + ResNodeTree.PrintNodeListAsTree(addon->CollisionNodeList, (int)addon->CollisionNodeListCount, "Collision List", this, new(0.5F, 0.7F, 1F, 1F)); + } if (SearchResults.Length > 0 && Countdown <= 0) { @@ -218,7 +235,7 @@ public unsafe partial class AddonTree : IDisposable private bool ValidateAddon(out AtkUnitBase* addon) { addon = (AtkUnitBase*)GameGui.GetAddonByName(this.AddonName); - if (addon == null || (nint)addon != this.initialPtr) + if (addon == null || (nint)addon != this.InitialPtr) { this.Dispose(); return false; @@ -231,7 +248,7 @@ public unsafe partial class AddonTree : IDisposable { if (this.window == null) { - this.window = new AddonPopoutWindow(this, $"{this.AddonName}###addonPopout{this.initialPtr}"); + this.window = new AddonPopoutWindow(this, $"{this.AddonName}###addonPopout{this.InitialPtr}"); PopoutWindows.AddWindow(this.window); } else diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs index 0c3a947dd..45a2d90eb 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/Events.cs @@ -1,4 +1,6 @@ -using Dalamud.Interface.Internal.UiDebug2.Utility; +using System.Numerics; + +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -56,9 +58,9 @@ public static class Events ImGui.TableNextColumn(); ImGui.TextUnformatted($"{evt->State.UnkFlags1}"); ImGui.TableNextColumn(); - Gui.ClickToCopyText($"{(nint)evt->Target:X}"); + ImGuiHelpers.ClickToCopyText($"{(nint)evt->Target:X}", null, new Vector4(0.6f, 0.6f, 0.6f, 1)); ImGui.TableNextColumn(); - Gui.ClickToCopyText($"{(nint)evt->Listener:X}"); + ImGuiHelpers.ClickToCopyText($"{(nint)evt->Listener:X}", null, new Vector4(0.6f, 0.6f, 0.6f, 1)); evt = evt->NextEvent; } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs index d9fcf52cc..4a1989441 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Component.cs @@ -34,11 +34,13 @@ internal unsafe class ComponentNodeTree : ResNodeTree private AtkUldManager* UldManager => &this.Component->UldManager; + private int? ComponentFieldOffset { get; set; } + /// private protected override string GetHeaderText() { var childCount = (int)this.UldManager->NodeListCount; - return $"{this.componentType} Component Node{(childCount > 0 ? $" [+{childCount}]" : string.Empty)} (Node: {(nint)this.Node:X} / Comp: {(nint)this.Component:X})"; + return $"{this.componentType} Component Node{(childCount > 0 ? $" [+{childCount}]" : string.Empty)}"; } /// @@ -62,10 +64,10 @@ internal unsafe class ComponentNodeTree : ResNodeTree } /// - private protected override void PrintFieldNames() + private protected override void PrintFieldLabels() { - this.PrintFieldName((nint)this.Node, new(0, 0.85F, 1, 1)); - this.PrintFieldName((nint)this.Component, new(0f, 0.5f, 0.8f, 1f)); + this.PrintFieldLabel((nint)this.Node, new(0, 0.85F, 1, 1), this.NodeFieldOffset); + this.PrintFieldLabel((nint)this.Component, new(0f, 0.5f, 0.8f, 1f), this.ComponentFieldOffset); } /// @@ -108,6 +110,34 @@ internal unsafe class ComponentNodeTree : ResNodeTree } } + /// + private protected override void GetFieldOffset() + { + var nodeFound = false; + var componentFound = false; + for (var i = 0; i < this.AddonTree.AddonSize; i += 0x8) + { + var readPtr = Marshal.ReadIntPtr(this.AddonTree.InitialPtr + i); + + if (readPtr == (nint)this.Node) + { + this.NodeFieldOffset = i; + nodeFound = true; + } + + if (readPtr == (nint)this.Component) + { + this.ComponentFieldOffset = i; + componentFound = true; + } + + if (nodeFound && componentFound) + { + break; + } + } + } + private void PrintComponentObject() { PrintFieldValuePair("Component", $"{(nint)this.Component:X}"); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs index 6cb178bd7..6b6522bb4 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Editor.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Numerics; +using Dalamud.Interface.Components; using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Utility.Raii; @@ -370,8 +371,8 @@ internal unsafe partial class TextNodeTree var hAlign = (int)alignment % 3; var vAlign = ((int)alignment - hAlign) / 3; - var hAlignInput = IconButtonSelect($"{label}H", ref hAlign, [0, 1, 2], [AlignLeft, AlignCenter, AlignRight]); - var vAlignInput = IconButtonSelect($"{label}V", ref vAlign, [0, 1, 2], [ArrowsUpToLine, GripLines, ArrowsDownToLine]); + var hAlignInput = ImGuiComponents.IconButtonSelect($"{label}H", ref hAlign, [AlignLeft, AlignCenter, AlignRight], [0, 1, 2], 3u, new(25, 0)); + var vAlignInput = ImGuiComponents.IconButtonSelect($"{label}V", ref vAlign, [ArrowsUpToLine, GripLines, ArrowsDownToLine], [0, 1, 2], 3u, new(25, 0)); if (hAlignInput || vAlignInput) { diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs index a333940c1..2edf4e570 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/NodeTree.Res.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using Dalamud.Interface.Components; using Dalamud.Interface.Internal.UiDebug2.Utility; @@ -60,6 +61,11 @@ internal unsafe partial class ResNodeTree : IDisposable /// private protected NodeType NodeType { get; init; } + /// + /// Gets or sets the offset of this node within its parent Addon. + /// + private protected int? NodeFieldOffset { get; set; } + /// /// Clears this NodeTree's popout window, if it has one. /// @@ -164,19 +170,26 @@ internal unsafe partial class ResNodeTree : IDisposable internal void WriteTreeHeading() { ImGui.TextUnformatted(this.GetHeaderText()); - this.PrintFieldNames(); + this.PrintFieldLabels(); } /// - /// If the given pointer has been identified as a field within the addon struct, this method prints that field's name. + /// If the given pointer is referenced with the addon struct, the offset within the addon will be printed. If the given pointer has been identified as a field within the addon struct, this method also prints that field's name. /// /// The pointer to check. /// The text color to use. - private protected void PrintFieldName(nint ptr, Vector4 color) + /// The field offset of the pointer, if it was found in the addon. + private protected void PrintFieldLabel(nint ptr, Vector4 color, int? fieldOffset) { + if (fieldOffset != null) + { + ImGui.SameLine(0, -1); + ImGui.TextColored(color * 0.85f, $"[0x{fieldOffset:X}]"); + } + if (this.AddonTree.FieldNames.TryGetValue(ptr, out var result)) { - ImGui.SameLine(); + ImGui.SameLine(0, -1); ImGui.TextColored(color, string.Join(".", result)); } } @@ -188,7 +201,15 @@ internal unsafe partial class ResNodeTree : IDisposable private protected virtual string GetHeaderText() { var count = this.GetDirectChildCount(); - return $"{this.NodeType} Node{(count > 0 ? $" [+{count}]" : string.Empty)} ({(nint)this.Node:X})"; + return $"{this.NodeType} Node{(count > 0 ? $" [+{count}]" : string.Empty)}"; + } + + /// + /// Prints any field names for the node. + /// + private protected virtual void PrintFieldLabels() + { + this.PrintFieldLabel((nint)this.Node, new(0, 0.85F, 1, 1), this.NodeFieldOffset); } /// @@ -201,11 +222,6 @@ internal unsafe partial class ResNodeTree : IDisposable ImGui.NewLine(); } - /// - /// Prints any field names for the node. - /// - private protected virtual void PrintFieldNames() => this.PrintFieldName((nint)this.Node, new(0, 0.85F, 1, 1)); - /// /// Prints all direct children of this node. /// @@ -227,6 +243,21 @@ internal unsafe partial class ResNodeTree : IDisposable { } + /// + /// Attempts to retrieve the field offset of the given pointer within the parent addon. + /// + private protected virtual void GetFieldOffset() + { + for (var i = 0; i < this.AddonTree.AddonSize; i += 0x8) + { + if (Marshal.ReadIntPtr(this.AddonTree.InitialPtr + i) == (nint)this.Node) + { + this.NodeFieldOffset = i; + break; + } + } + } + private int GetDirectChildCount() { var count = 0; @@ -273,6 +304,8 @@ internal unsafe partial class ResNodeTree : IDisposable ImGui.SetNextItemOpen(true, ImGuiCond.Always); } + this.GetFieldOffset(); + using var col = ImRaii.PushColor(Text, displayColor); using var tree = ImRaii.TreeNode(label, SpanFullWidth); @@ -281,7 +314,7 @@ internal unsafe partial class ResNodeTree : IDisposable new NodeBounds(this.Node).Draw(visible ? new(0.1f, 1f, 0.1f, 1f) : new(1f, 0f, 0.2f, 1f)); } - ImGui.SameLine(); + ImGui.SameLine(0, -1); this.WriteTreeHeading(); col.Pop(); diff --git a/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs index 7f603fdac..6693c3fb0 100644 --- a/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs +++ b/Dalamud/Interface/Internal/UiDebug2/ElementSelector.cs @@ -79,7 +79,7 @@ internal unsafe class ElementSelector : IDisposable /// internal void DrawInterface() { - using (ImRaii.Child("###sidebar_elementSelector", new(250, 0), true)) + using (ImRaii.Child("###sidebar_elementSelector", new(250, -1), true)) { using (ImRaii.PushFont(IconFont)) { diff --git a/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs index 954e5cb72..cc73b79c6 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/Gui.cs @@ -1,7 +1,6 @@ -using System.Collections.Generic; using System.Numerics; -using Dalamud.Interface.Components; +using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using FFXIVClientStructs.FFXIV.Client.Graphics; @@ -17,40 +16,6 @@ namespace Dalamud.Interface.Internal.UiDebug2.Utility; /// internal static class Gui { - /// - /// A radio-button-esque input that uses Fontawesome icon buttons. - /// - /// The type of value being set. - /// The label for the inputs. - /// The value being set. - /// A list of all options. - /// A list of icons corresponding to the options. - /// true if a button is clicked. - internal static unsafe bool IconButtonSelect(string label, ref T val, List options, List icons) - { - var ret = false; - - for (var i = 0; i < options.Count; i++) - { - if (i > 0) - { - ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X); - } - - var option = options[i]; - var icon = icons.Count > i ? icons[i] : FontAwesomeIcon.Question; - var color = *ImGui.GetStyleColorVec4(val is not null && val.Equals(option) ? ButtonActive : Button); - - if (ImGuiComponents.IconButton($"{label}{option}{i}", icon, color)) - { - val = option; - ret = true; - } - } - - return ret; - } - /// /// Prints field name and its value. /// @@ -61,13 +26,14 @@ internal static class Gui { ImGui.TextUnformatted($"{fieldName}:"); ImGui.SameLine(); + var grey60 = new Vector4(0.6f, 0.6f, 0.6f, 1); if (copy) { - ClickToCopyText(value); + ImGuiHelpers.ClickToCopyText(value, null, grey60); } else { - ImGui.TextColored(new(0.6f, 0.6f, 0.6f, 1), value); + ImGui.TextColored(grey60, value); } } @@ -102,7 +68,10 @@ internal static class Gui /// Colors the text itself either white or black, depending on the luminosity of the background color. internal static void PrintColor(Vector4 color, string fmt) { - using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)).Push(Button, color).Push(ButtonActive, color).Push(ButtonHovered, color)) + using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)) + .Push(Button, color) + .Push(ButtonActive, color) + .Push(ButtonHovered, color)) { ImGui.SmallButton(fmt); } @@ -117,39 +86,6 @@ internal static class Gui 0.5f) * vector4.W; } - /// - /// Print out text that can be copied when clicked. - /// - /// The text to show. - /// The text to copy when clicked. - internal static void ClickToCopyText(string text, string? textCopy = null) - { - using (ImRaii.PushColor(Text, new Vector4(0.6f, 0.6f, 0.6f, 1))) - { - textCopy ??= text; - ImGui.TextUnformatted($"{text}"); - } - - if (ImGui.IsItemHovered()) - { - using (ImRaii.Tooltip()) - { - using (ImRaii.PushFont(UiBuilder.IconFont)) - { - ImGui.TextUnformatted(FontAwesomeIcon.Copy.ToIconString()); - } - - ImGui.SameLine(); - ImGui.TextUnformatted($"{textCopy}"); - } - } - - if (ImGui.IsItemClicked()) - { - ImGui.SetClipboardText($"{textCopy}"); - } - } - /// /// Draws a tooltip that changes based on the cursor's x-position within the hovered item. /// @@ -176,4 +112,23 @@ internal static class Gui return true; } + + /// + /// Draws a separator with some padding above and below. + /// + /// Governs whether to pad above, below, or both. + /// The amount of padding. + internal static void PaddedSeparator(uint mask = 0b11, float padding = 5f) + { + if ((mask & 0b10) > 0) + { + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); + } + + ImGui.Separator(); + if ((mask & 0b01) > 0) + { + ImGui.Dummy(new(padding * ImGui.GetIO().FontGlobalScale)); + } + } } diff --git a/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs index cffd676f7..3d28cb836 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Utility/NodeBounds.cs @@ -6,7 +6,7 @@ using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -using static System.Math; +using static System.MathF; using static Dalamud.Interface.ColorHelpers; namespace Dalamud.Interface.Internal.UiDebug2.Utility; @@ -133,7 +133,7 @@ public unsafe struct NodeBounds if (p.Y > Min(p1.Y, p2.Y) && p.Y <= Max(p1.Y, p2.Y) && p.X <= Max(p1.X, p2.X) && - (p1.X.Equals(p2.X) || p.X <= ((p.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y)) + p1.X)) + (p1.X.Equals(p2.X) || p.X <= (((p.Y - p1.Y) * (p2.X - p1.X)) / (p2.Y - p1.Y)) + p1.X)) { inside = !inside; } @@ -144,12 +144,12 @@ public unsafe struct NodeBounds private static Vector2 TransformPoint(Vector2 p, Vector2 o, float r, Vector2 s) { - var cosR = (float)Cos(r); - var sinR = (float)Sin(r); + var cosR = Cos(r); + var sinR = Sin(r); var d = (p - o) * s; return new( - o.X + (d.X * cosR) - (d.Y * sinR), + (o.X + (d.X * cosR)) - (d.Y * sinR), o.Y + (d.X * sinR) + (d.Y * cosR)); } diff --git a/Dalamud/Interface/Utility/ImGuiHelpers.cs b/Dalamud/Interface/Utility/ImGuiHelpers.cs index 8ce7a48d7..8dedae5cd 100644 --- a/Dalamud/Interface/Utility/ImGuiHelpers.cs +++ b/Dalamud/Interface/Utility/ImGuiHelpers.cs @@ -167,17 +167,41 @@ public static class ImGuiHelpers /// /// The text to show. /// The text to copy when clicked. - public static void ClickToCopyText(string text, string? textCopy = null) + /// The color of the text. + public static void ClickToCopyText(string text, string? textCopy = null, Vector4? color = null) { textCopy ??= text; - ImGui.Text($"{text}"); + + using (var col = new ImRaii.Color()) + { + if (color.HasValue) + { + col.Push(ImGuiCol.Text, color.Value); + } + + ImGui.TextUnformatted($"{text}"); + } + if (ImGui.IsItemHovered()) { ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); - if (textCopy != text) ImGui.SetTooltip(textCopy); + + using (ImRaii.Tooltip()) + { + using (ImRaii.PushFont(UiBuilder.IconFont)) + { + ImGui.TextUnformatted(FontAwesomeIcon.Copy.ToIconString()); + } + + ImGui.SameLine(); + ImGui.TextUnformatted(textCopy); + } } - if (ImGui.IsItemClicked()) ImGui.SetClipboardText($"{textCopy}"); + if (ImGui.IsItemClicked()) + { + ImGui.SetClipboardText(textCopy); + } } /// Draws a SeString. From 3100f9134d0be73bd56d16acdb251f8c437a8e5c Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 16:50:08 -0800 Subject: [PATCH 11/14] cs bumppppp --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 81ea903b7..b5240ab1f 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 81ea903b7dc90bd07a38703738fa7f84b1ed0775 +Subproject commit b5240ab1f8b0170a4637f0a3f99baa9f6bd3966b From ec8389a34e49bf6a37d6372c9972d180628b8659 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 20:27:30 -0800 Subject: [PATCH 12/14] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index b5240ab1f..5f4e7bf27 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit b5240ab1f8b0170a4637f0a3f99baa9f6bd3966b +Subproject commit 5f4e7bf271f8144ad72f289890b29351afa248b2 From bdc24ad23edec9cb49288313d31f229d1b53b77d Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 23:10:59 -0800 Subject: [PATCH 13/14] bump cs --- lib/FFXIVClientStructs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FFXIVClientStructs b/lib/FFXIVClientStructs index 5f4e7bf27..679b5353c 160000 --- a/lib/FFXIVClientStructs +++ b/lib/FFXIVClientStructs @@ -1 +1 @@ -Subproject commit 5f4e7bf271f8144ad72f289890b29351afa248b2 +Subproject commit 679b5353cd61baa03b26d95efcac059f8e29b141 From d7abf9fe0d530f591d31b2ba8617f42065733148 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Thu, 14 Nov 2024 23:24:32 -0800 Subject: [PATCH 14/14] Fix Fate aging step; report Tuliyollal taxes - FATE table test needs a zone with FATEs in it. - Use a `byte` rather than an `int` so we don't overflow like crazy. --- .../Windows/SelfTest/AgingSteps/FateTableAgingStep.cs | 8 +++++++- .../Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs index a8fe60aa9..eef986302 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/FateTableAgingStep.cs @@ -9,7 +9,7 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; /// internal class FateTableAgingStep : IAgingStep { - private int index = 0; + private byte index = 0; /// public string Name => "Test FateTable"; @@ -21,6 +21,12 @@ internal class FateTableAgingStep : IAgingStep ImGui.Text("Checking fate table..."); + if (fateTable.Length == 0) + { + ImGui.Text("Go to a zone that has FATEs currently up."); + return SelfTestStepResult.Waiting; + } + if (this.index == fateTable.Length - 1) { return SelfTestStepResult.Pass; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs index 78d43662c..513141fa9 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/MarketBoardAgingStep.cs @@ -169,6 +169,7 @@ internal class MarketBoardAgingStep : IAgingStep ImGui.Text($"Kugane: {this.marketTaxRate.KuganeTax.ToString()}"); ImGui.Text($"Crystarium: {this.marketTaxRate.CrystariumTax.ToString()}"); ImGui.Text($"Sharlayan: {this.marketTaxRate.SharlayanTax.ToString()}"); + ImGui.Text($"Tuliyollal: {this.marketTaxRate.TuliyollalTax.ToString()}"); ImGui.Separator(); if (ImGui.Button("Looks Correct / Skip")) {