diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs deleted file mode 100644 index 0566b8107..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenu.cs +++ /dev/null @@ -1,463 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using Dalamud.Configuration.Internal; -using Dalamud.Game.Gui.ContextMenus.OldStructs; -using Dalamud.Hooking; -using Dalamud.IoC; -using Dalamud.IoC.Internal; -using Dalamud.Logging; -using Dalamud.Memory; -using FFXIVClientStructs.FFXIV.Client.Game; -using FFXIVClientStructs.FFXIV.Client.UI; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; -using Serilog; - -using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Provides an interface to modify context menus. - /// - [PluginInterface] - [InterfaceVersion("1.0")] - [ServiceManager.BlockingEarlyLoadedService] - public sealed class ContextMenu : IDisposable, IServiceType - { - private const int MaxContextMenuItemsPerContextMenu = 32; - - private readonly OpenSubContextMenuDelegate? openSubContextMenu; - - #region Hooks - - private readonly Hook contextMenuOpenedHook; - private readonly Hook subContextMenuOpenedHook; - private readonly Hook contextMenuItemSelectedHook; - private readonly Hook contextMenuOpeningHook; - private readonly Hook subContextMenuOpeningHook; - - #endregion - - private unsafe OldAgentContextInterface* currentAgentContextInterface; - - private IntPtr currentSubContextMenuTitle; - - private OpenSubContextMenuItem? selectedOpenSubContextMenuItem; - private ContextMenuOpenedArgs? currentContextMenuOpenedArgs; - - [ServiceManager.ServiceConstructor] - private ContextMenu(SigScanner sigScanner) - { - this.Address = new ContextMenuAddressResolver(); - this.Address.Setup(sigScanner); - - unsafe - { - this.openSubContextMenu = Marshal.GetDelegateForFunctionPointer(this.Address.OpenSubContextMenuPtr); - - this.contextMenuOpeningHook = Hook.FromAddress(this.Address.ContextMenuOpeningPtr, this.ContextMenuOpeningDetour); - this.contextMenuOpenedHook = Hook.FromAddress(this.Address.ContextMenuOpenedPtr, this.ContextMenuOpenedDetour); - this.contextMenuItemSelectedHook = Hook.FromAddress(this.Address.ContextMenuItemSelectedPtr, this.ContextMenuItemSelectedDetour); - this.subContextMenuOpeningHook = Hook.FromAddress(this.Address.SubContextMenuOpeningPtr, this.SubContextMenuOpeningDetour); - this.subContextMenuOpenedHook = Hook.FromAddress(this.Address.SubContextMenuOpenedPtr, this.SubContextMenuOpenedDetour); - } - } - - #region Delegates - - private unsafe delegate bool OpenSubContextMenuDelegate(OldAgentContext* agentContext); - - private unsafe delegate IntPtr ContextMenuOpeningDelegate(IntPtr a1, IntPtr a2, IntPtr a3, uint a4, IntPtr a5, OldAgentContextInterface* agentContextInterface, IntPtr a7, ushort a8); - - private unsafe delegate bool ContextMenuOpenedDelegate(AddonContextMenu* addonContextMenu, int menuSize, AtkValue* atkValueArgs); - - private unsafe delegate bool ContextMenuItemSelectedDelegate(AddonContextMenu* addonContextMenu, int selectedIndex, byte a3); - - private unsafe delegate bool SubContextMenuOpeningDelegate(OldAgentContext* agentContext); - - #endregion - - /// - /// Occurs when a context menu is opened by the game. - /// - public event ContextMenus.ContextMenuOpenedDelegate? ContextMenuOpened; - - private ContextMenuAddressResolver Address { get; set; } - - /// - void IDisposable.Dispose() - { - this.subContextMenuOpeningHook.Disable(); - this.contextMenuItemSelectedHook.Disable(); - this.subContextMenuOpenedHook.Disable(); - this.contextMenuOpenedHook.Disable(); - this.contextMenuOpeningHook.Disable(); - } - - private static unsafe bool IsInventoryContext(OldAgentContextInterface* agentContextInterface) - { - return agentContextInterface == AgentInventoryContext.Instance(); - } - - private static int GetContextMenuItemsHashCode(IEnumerable contextMenuItems) - { - unchecked - { - return contextMenuItems.Aggregate(17, (current, item) => (current * 23) + item.GetHashCode()); - } - } - - [ServiceManager.CallWhenServicesReady] - private void ContinueConstruction(GameGui gameGui) - { - if (!EnvironmentConfiguration.DalamudDoContextMenu) - return; - - this.contextMenuOpeningHook.Enable(); - this.contextMenuOpenedHook.Enable(); - this.contextMenuItemSelectedHook.Enable(); - this.subContextMenuOpeningHook.Enable(); - this.subContextMenuOpenedHook.Enable(); - } - - private unsafe IntPtr ContextMenuOpeningDetour(IntPtr a1, IntPtr a2, IntPtr a3, uint a4, IntPtr a5, OldAgentContextInterface* agentContextInterface, IntPtr a7, ushort a8) - { - this.currentAgentContextInterface = agentContextInterface; - return this.contextMenuOpeningHook!.Original(a1, a2, a3, a4, a5, agentContextInterface, a7, a8); - } - - private unsafe bool ContextMenuOpenedDetour(AddonContextMenu* addonContextMenu, int atkValueCount, AtkValue* atkValues) - { - try - { - this.ContextMenuOpenedImplementation(addonContextMenu, ref atkValueCount, ref atkValues); - } - catch (Exception ex) - { - PluginLog.Error(ex, "ContextMenuOpenedDetour"); - } - - return this.contextMenuOpenedHook.Original(addonContextMenu, atkValueCount, atkValues); - } - - private unsafe void ContextMenuOpenedImplementation(AddonContextMenu* addonContextMenu, ref int atkValueCount, ref AtkValue* atkValues) - { - if (this.ContextMenuOpened == null - || this.currentAgentContextInterface == null) - { - return; - } - - var contextMenuReaderWriter = new ContextMenuReaderWriter(this.currentAgentContextInterface, atkValueCount, atkValues); - - // Check for a title. - string? title = null; - if (this.selectedOpenSubContextMenuItem != null) - { - title = this.selectedOpenSubContextMenuItem.Name.TextValue; - - // Write the custom title - var titleAtkValue = &atkValues[1]; - fixed (byte* titlePtr = this.selectedOpenSubContextMenuItem.Name.Encode().NullTerminate()) - { - titleAtkValue->SetString(titlePtr); - } - } - else if (contextMenuReaderWriter.Title != null) - { - title = contextMenuReaderWriter.Title.TextValue; - } - - // Determine which event to raise. - var contextMenuOpenedDelegate = this.ContextMenuOpened; - - // this.selectedOpenSubContextMenuItem is OpenSubContextMenuItem openSubContextMenuItem - if (this.selectedOpenSubContextMenuItem != null) - { - contextMenuOpenedDelegate = this.selectedOpenSubContextMenuItem.Opened; - } - - // Get the existing items from the game. - // TODO: For inventory sub context menus, we take only the last item -- the return item. - // This is because we're doing a hack to spawn a Second Tier sub context menu and then appropriating it. - var contextMenuItems = contextMenuReaderWriter.Read(); - if (contextMenuItems == null) - return; - - if (IsInventoryContext(this.currentAgentContextInterface) && this.selectedOpenSubContextMenuItem != null) - { - contextMenuItems = contextMenuItems.TakeLast(1).ToArray(); - } - - var beforeHashCode = GetContextMenuItemsHashCode(contextMenuItems); - - // Raise the event and get the context menu changes. - this.currentContextMenuOpenedArgs = this.NotifyContextMenuOpened(addonContextMenu, this.currentAgentContextInterface, title, contextMenuOpenedDelegate, contextMenuItems); - if (this.currentContextMenuOpenedArgs == null) - { - return; - } - - var afterHashCode = GetContextMenuItemsHashCode(this.currentContextMenuOpenedArgs.Items); - - PluginLog.Warning($"{beforeHashCode}={afterHashCode}"); - - // Only write to memory if the items were actually changed. - if (beforeHashCode != afterHashCode) - { - // Write the new changes. - contextMenuReaderWriter.Write(this.currentContextMenuOpenedArgs.Items); - - // Update the addon. - atkValueCount = *(&addonContextMenu->AtkValuesCount) = (ushort)contextMenuReaderWriter.AtkValueCount; - atkValues = *(&addonContextMenu->AtkValues) = contextMenuReaderWriter.AtkValues; - } - } - - private unsafe bool SubContextMenuOpeningDetour(OldAgentContext* agentContext) - { - return this.SubContextMenuOpeningImplementation(agentContext) || this.subContextMenuOpeningHook.Original(agentContext); - } - - private unsafe bool SubContextMenuOpeningImplementation(OldAgentContext* agentContext) - { - if (this.openSubContextMenu == null || this.selectedOpenSubContextMenuItem == null) - { - return false; - } - - // The important things to make this work are: - // 1. Allocate a temporary sub context menu title. The value doesn't matter, we'll set it later. - // 2. Context menu item count must equal 1 to tell the game there is enough space for the "< Return" item. - // 3. Atk value count must equal the index of the first context menu item. - // This is enough to keep the base data, but excludes the context menu item data. - // We want to exclude context menu item data in this function because the game sometimes includes garbage items which can cause problems. - // After this function, the game adds the "< Return" item, and THEN we add our own items after that. - - this.openSubContextMenu(agentContext); - - // Allocate a new 1 byte title. This is required for the game to render the titled context menu style. - // The actual value doesn't matter at this point, we'll set it later. - MemoryHelper.GameFree(ref this.currentSubContextMenuTitle, (ulong)IntPtr.Size); - this.currentSubContextMenuTitle = MemoryHelper.GameAllocateUi(1); - *(&(&agentContext->AgentContextInterface)->SubContextMenuTitle) = (byte*)this.currentSubContextMenuTitle; - *(byte*)this.currentSubContextMenuTitle = 0; - - // Expect at least 1 context menu item. - (&agentContext->Items->AtkValues)[0].UInt = 1; - - // Expect a title. This isn't needed by the game, it's needed by ContextMenuReaderWriter which uses this to check if it's a context menu - (&agentContext->Items->AtkValues)[1].ChangeType(ValueType.String); - - (&agentContext->Items->AtkValues)[1].String = (byte*)0; - - ContextMenuReaderWriter contextMenuReaderWriter = new ContextMenuReaderWriter(&agentContext->AgentContextInterface, agentContext->Items->AtkValueCount, &agentContext->Items->AtkValues); - *(&agentContext->Items->AtkValueCount) = (ushort)contextMenuReaderWriter.FirstContextMenuItemIndex; - - return true; - } - - private unsafe bool SubContextMenuOpenedDetour(AddonContextMenu* addonContextMenu, int atkValueCount, AtkValue* atkValues) - { - try - { - this.SubContextMenuOpenedImplementation(addonContextMenu, ref atkValueCount, ref atkValues); - } - catch (Exception ex) - { - PluginLog.Error(ex, "SubContextMenuOpenedDetour"); - } - - return this.subContextMenuOpenedHook.Original(addonContextMenu, atkValueCount, atkValues); - } - - private unsafe void SubContextMenuOpenedImplementation(AddonContextMenu* addonContextMenu, ref int atkValueCount, ref AtkValue* atkValues) - { - this.ContextMenuOpenedImplementation(addonContextMenu, ref atkValueCount, ref atkValues); - } - - private unsafe ContextMenuOpenedArgs? NotifyContextMenuOpened(AddonContextMenu* addonContextMenu, OldAgentContextInterface* agentContextInterface, string? title, ContextMenus.ContextMenuOpenedDelegate contextMenuOpenedDelegate, IEnumerable initialContextMenuItems) - { - var parentAddonName = this.GetParentAddonName(&addonContextMenu->AtkUnitBase); - - Log.Warning($"AgentContextInterface at: {new IntPtr(agentContextInterface):X}"); - - InventoryItemContext? inventoryItemContext = null; - GameObjectContext? gameObjectContext = null; - if (IsInventoryContext(agentContextInterface)) - { - var agentInventoryContext = (AgentInventoryContext*)agentContextInterface; - inventoryItemContext = new InventoryItemContext(agentInventoryContext->TargetDummyItem.ItemID, agentInventoryContext->TargetDummyItem.Quantity, agentInventoryContext->TargetDummyItem.Flags.HasFlag(InventoryItem.ItemFlags.HQ)); - } - else - { - var agentContext = (OldAgentContext*)agentContextInterface; - - uint? id = agentContext->GameObjectId; - if (id == 0) - { - id = null; - } - - ulong? contentId = agentContext->GameObjectContentId; - if (contentId == 0) - { - contentId = null; - } - - var name = MemoryHelper.ReadSeStringNullTerminated((IntPtr)agentContext->GameObjectName.StringPtr).TextValue; - if (string.IsNullOrEmpty(name)) - { - name = null; - } - - ushort? worldId = agentContext->GameObjectWorldId; - if (worldId == 0) - { - worldId = null; - } - - if (id != null - || contentId != null - || name != null - || worldId != null) - { - gameObjectContext = new GameObjectContext(id, contentId, name, worldId); - } - } - - // Temporarily remove the < Return item, for UX we should enforce that it is always last in the list. - var lastContextMenuItem = initialContextMenuItems.LastOrDefault(); - if (lastContextMenuItem is GameContextMenuItem gameContextMenuItem && gameContextMenuItem.SelectedAction == 102) - { - initialContextMenuItems = initialContextMenuItems.SkipLast(1); - } - - var contextMenuOpenedArgs = new ContextMenuOpenedArgs(addonContextMenu, agentContextInterface, parentAddonName, initialContextMenuItems) - { - Title = title, - InventoryItemContext = inventoryItemContext, - GameObjectContext = gameObjectContext, - }; - - try - { - contextMenuOpenedDelegate.Invoke(contextMenuOpenedArgs); - } - catch (Exception ex) - { - PluginLog.LogError(ex, "NotifyContextMenuOpened"); - return null; - } - - // Readd the < Return item - if (lastContextMenuItem is GameContextMenuItem gameContextMenuItem1 && gameContextMenuItem1.SelectedAction == 102) - { - contextMenuOpenedArgs.Items.Add(lastContextMenuItem); - } - - foreach (var contextMenuItem in contextMenuOpenedArgs.Items.ToArray()) - { - // TODO: Game doesn't support nested sub context menus, but we might be able to. - if (contextMenuItem is OpenSubContextMenuItem && contextMenuOpenedArgs.Title != null) - { - contextMenuOpenedArgs.Items.Remove(contextMenuItem); - PluginLog.Warning($"Context menu '{contextMenuOpenedArgs.Title}' item '{contextMenuItem}' has been removed because nested sub context menus are not supported."); - } - } - - if (contextMenuOpenedArgs.Items.Count > MaxContextMenuItemsPerContextMenu) - { - PluginLog.LogWarning($"Context menu requesting {contextMenuOpenedArgs.Items.Count} of max {MaxContextMenuItemsPerContextMenu} items. Resizing list to compensate."); - contextMenuOpenedArgs.Items.RemoveRange(MaxContextMenuItemsPerContextMenu, contextMenuOpenedArgs.Items.Count - MaxContextMenuItemsPerContextMenu); - } - - return contextMenuOpenedArgs; - } - - private unsafe bool ContextMenuItemSelectedDetour(AddonContextMenu* addonContextMenu, int selectedIndex, byte a3) - { - try - { - this.ContextMenuItemSelectedImplementation(addonContextMenu, selectedIndex); - } - catch (Exception ex) - { - PluginLog.Error(ex, "ContextMenuItemSelectedDetour"); - } - - return this.contextMenuItemSelectedHook.Original(addonContextMenu, selectedIndex, a3); - } - - private unsafe void ContextMenuItemSelectedImplementation(AddonContextMenu* addonContextMenu, int selectedIndex) - { - if (this.currentContextMenuOpenedArgs == null || selectedIndex == -1) - { - this.currentContextMenuOpenedArgs = null; - this.selectedOpenSubContextMenuItem = null; - return; - } - - // Read the selected item directly from the game - ContextMenuReaderWriter contextMenuReaderWriter = new ContextMenuReaderWriter(this.currentAgentContextInterface, addonContextMenu->AtkValuesCount, addonContextMenu->AtkValues); - var gameContextMenuItems = contextMenuReaderWriter.Read(); - if (gameContextMenuItems == null) - return; - - var gameSelectedItem = gameContextMenuItems.ElementAtOrDefault(selectedIndex); - - // This should be impossible - if (gameSelectedItem == null) - { - this.currentContextMenuOpenedArgs = null; - this.selectedOpenSubContextMenuItem = null; - return; - } - - // Match it with the items we already know about based on its name. - // We can get into a state where we have a game item we don't recognize when another plugin has added one. - var selectedItem = this.currentContextMenuOpenedArgs.Items.FirstOrDefault(item => item.Name.Encode().SequenceEqual(gameSelectedItem.Name.Encode())); - - this.selectedOpenSubContextMenuItem = null; - if (selectedItem is CustomContextMenuItem customContextMenuItem) - { - try - { - var customContextMenuItemSelectedArgs = new CustomContextMenuItemSelectedArgs(this.currentContextMenuOpenedArgs, customContextMenuItem); - customContextMenuItem.ItemSelected(customContextMenuItemSelectedArgs); - } - catch (Exception ex) - { - PluginLog.LogError(ex, "ContextMenuItemSelectedImplementation"); - } - } - else if (selectedItem is OpenSubContextMenuItem openSubContextMenuItem) - { - this.selectedOpenSubContextMenuItem = openSubContextMenuItem; - } - - this.currentContextMenuOpenedArgs = null; - } - - private unsafe string? GetParentAddonName(AtkUnitBase* addonInterface) - { - var parentAddonId = addonInterface->ContextMenuParentID; - if (parentAddonId == 0) - { - return null; - } - - var atkStage = AtkStage.GetSingleton(); - var parentAddon = atkStage->RaptureAtkUnitManager->GetAddonById(parentAddonId); - return Marshal.PtrToStringUTF8(new IntPtr(parentAddon->Name)); - } - - private unsafe AtkUnitBase* GetAddonFromAgent(AgentInterface* agentInterface) - { - return agentInterface->AddonId == 0 ? null : AtkStage.GetSingleton()->RaptureAtkUnitManager->GetAddonById((ushort)agentInterface->AddonId); - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuAddressResolver.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuAddressResolver.cs deleted file mode 100644 index 6bd0f6f72..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuAddressResolver.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Address resolver for context menu functions. - /// - public class ContextMenuAddressResolver : BaseAddressResolver - { - private const string SigOpenSubContextMenu = "E8 ?? ?? ?? ?? 44 39 A3 ?? ?? ?? ?? 0F 86"; - private const string SigContextMenuOpening = "E8 ?? ?? ?? ?? 0F B7 C0 48 83 C4 60"; - private const string SigContextMenuOpened = "48 8B C4 57 41 56 41 57 48 81 EC"; - private const string SigContextMenuItemSelected = "48 89 5C 24 ?? 55 57 41 56 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 44 24 ?? 80 B9"; - private const string SigSubContextMenuOpening = "E8 ?? ?? ?? ?? 44 39 A3 ?? ?? ?? ?? 0F 84"; - private const string SigSubContextMenuOpened = "48 8B C4 57 41 55 41 56 48 81 EC"; - - /// - /// Gets the OpenSubContextMenu function address. - /// - public IntPtr OpenSubContextMenuPtr { get; private set; } - - /// - /// Gets the ContextMenuOpening function address. - /// - public IntPtr ContextMenuOpeningPtr { get; private set; } - - /// - /// Gets the ContextMenuOpened function address. - /// - public IntPtr ContextMenuOpenedPtr { get; private set; } - - /// - /// Gets the ContextMenuItemSelected function address. - /// - public IntPtr ContextMenuItemSelectedPtr { get; private set; } - - /// - /// Gets the SubContextMenuOpening function address. - /// - public IntPtr SubContextMenuOpeningPtr { get; private set; } - - /// - /// Gets the SubContextMenuOpened function address. - /// - public IntPtr SubContextMenuOpenedPtr { get; private set; } - - /// - protected override void Setup64Bit(SigScanner scanner) - { - this.OpenSubContextMenuPtr = scanner.ScanText(SigOpenSubContextMenu); - this.ContextMenuOpeningPtr = scanner.ScanText(SigContextMenuOpening); - this.ContextMenuOpenedPtr = scanner.ScanText(SigContextMenuOpened); - this.ContextMenuItemSelectedPtr = scanner.ScanText(SigContextMenuItemSelected); - this.SubContextMenuOpeningPtr = scanner.ScanText(SigSubContextMenuOpening); - this.SubContextMenuOpenedPtr = scanner.ScanText(SigSubContextMenuOpened); - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuItem.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuItem.cs deleted file mode 100644 index c286ac4bb..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuItem.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Numerics; - -using Dalamud.Game.Text.SeStringHandling; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// An item in a context menu. - /// - public abstract class ContextMenuItem - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - internal ContextMenuItem(SeString name) - { - this.Name = name; - } - - /// - /// Gets the name of the item. - /// - public SeString Name { get; internal set; } - - /// - /// Gets or sets a value indicating whether or not the item is enabled. When enabled, an item is selectable. - /// - public bool IsEnabled { get; set; } = true; - - /// - /// Gets or sets the indicator of the item. - /// - public ContextMenuItemIndicator Indicator { get; set; } = ContextMenuItemIndicator.None; - - /// - public override string ToString() - { - return this.Name.ToString(); - } - - /// - public override int GetHashCode() - { - unchecked - { - var hash = 17; - hash = (hash * 23) + new BigInteger(this.Name.Encode()).GetHashCode(); - hash = (hash * 23) + this.IsEnabled.GetHashCode(); - hash = (hash * 23) + ((int)this.Indicator).GetHashCode(); - return hash; - } - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuItemIndicator.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuItemIndicator.cs deleted file mode 100644 index cdd2aa694..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuItemIndicator.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// An indicator displayed on a context menu item. - /// - public enum ContextMenuItemIndicator - { - /// - /// The item has no indicator. - /// - None, - - /// - /// The item has a previous indicator. - /// - Previous, - - /// - /// The item has a next indicator. - /// - Next, - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedArgs.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedArgs.cs deleted file mode 100644 index 0924dcf54..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedArgs.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; - -using Dalamud.Game.Gui.ContextMenus.OldStructs; -using Dalamud.Game.Text.SeStringHandling; -using FFXIVClientStructs.FFXIV.Client.UI; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Provides data for methods. - /// - public sealed unsafe class ContextMenuOpenedArgs - { - /// - /// Initializes a new instance of the class. - /// - /// The addon associated with the context menu. - /// The agent associated with the context menu. - /// The the name of the parent addon associated with the context menu. - /// The items in the context menu. - public ContextMenuOpenedArgs(AddonContextMenu* addon, OldAgentContextInterface* agent, string? parentAddonName, IEnumerable items) - { - this.Addon = addon; - this.Agent = agent; - this.ParentAddonName = parentAddonName; - this.Items = new List(items); - } - - /// - /// Gets the addon associated with the context menu. - /// - public AddonContextMenu* Addon { get; } - - /// - /// Gets the agent associated with the context menu. - /// - public OldAgentContextInterface* Agent { get; } - - /// - /// Gets the name of the parent addon associated with the context menu. - /// - public string? ParentAddonName { get; } - - /// - /// Gets the title of the context menu. - /// - public string? Title { get; init; } - - /// - /// Gets the game object context associated with the context menu. - /// - public GameObjectContext? GameObjectContext { get; init; } - - /// - /// Gets the item context associated with the context menu. - /// - public InventoryItemContext? InventoryItemContext { get; init; } - - /// - /// Gets the items in the context menu. - /// - internal List Items { get; } - - /// - /// Append a custom context menu item to this context menu. - /// - /// The name of the item. - /// The action to be executed once selected. - public void AddCustomItem(SeString name, CustomContextMenuItemSelectedDelegate selected) => - this.Items.Add(new CustomContextMenuItem(name, selected)); - - /// - /// Append a custom submenu to this context menu. - /// Note that these cannot be nested, and will be ignored if they are. - /// - /// The name of the submenu. - /// The action to be executed once opened. - public void AddCustomSubMenu(SeString name, ContextMenuOpenedDelegate opened) - { - if (this.GameObjectContext != null) - throw new Exception("Submenus on GameObjects are not supported yet."); - - this.Items.Add(new OpenSubContextMenuItem(name, opened)); - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedDelegate.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedDelegate.cs deleted file mode 100644 index 4e3a162e2..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuOpenedDelegate.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Represents the method the event. - /// - /// The data associated with the event. - public delegate void ContextMenuOpenedDelegate(ContextMenuOpenedArgs args); -} diff --git a/Dalamud/Game/Gui/ContextMenus/ContextMenuReaderWriter.cs b/Dalamud/Game/Gui/ContextMenus/ContextMenuReaderWriter.cs deleted file mode 100644 index 633c6560b..000000000 --- a/Dalamud/Game/Gui/ContextMenus/ContextMenuReaderWriter.cs +++ /dev/null @@ -1,553 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Runtime.InteropServices; - -using Dalamud.Game.Gui.ContextMenus.OldStructs; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Logging; -using Dalamud.Memory; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; - -using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Class responsible for reading and writing to context menu data. - /// - internal unsafe class ContextMenuReaderWriter - { - private readonly OldAgentContextInterface* agentContextInterface; - - private int atkValueCount; - private AtkValue* atkValues; - - /// - /// Initializes a new instance of the class. - /// - /// The AgentContextInterface to act upon. - /// The number of ATK values to consider. - /// Pointer to the array of ATK values. - public ContextMenuReaderWriter(OldAgentContextInterface* agentContextInterface, int atkValueCount, AtkValue* atkValues) - { - PluginLog.Warning($"{(IntPtr)atkValues:X}"); - - this.agentContextInterface = agentContextInterface; - this.atkValueCount = atkValueCount; - this.atkValues = atkValues; - } - - private enum SubContextMenuStructLayout - { - Main, - Alternate, - } - - /// - /// Gets the number of AtkValues for this context menu. - /// - public int AtkValueCount => this.atkValueCount; - - /// - /// Gets the AtkValues for this context menu. - /// - public AtkValue* AtkValues => this.atkValues; - - /// - /// Gets the amount of items in the context menu. - /// - public int ContextMenuItemCount => this.atkValues[0].Int; - - /// - /// Gets a value indicating whether the context menu has a title. - /// - public bool HasTitle - { - get - { - bool isStringType = - (int)this.atkValues[1].Type == 8 - || (int)this.atkValues[1].Type == 38 - || this.atkValues[1].Type == FFXIVClientStructs.FFXIV.Component.GUI.ValueType.String; - - return isStringType; - } - } - - /// - /// Gets the title of the context menu. - /// - public SeString? Title - { - get - { - if (this.HasTitle && (&this.atkValues[1])->String != null) - { - MemoryHelper.ReadSeStringNullTerminated((IntPtr)(&this.atkValues[1])->String, out var str); - return str; - } - - return null; - } - } - - /// - /// Gets the index of the first context menu item. - /// - public int FirstContextMenuItemIndex - { - get - { - if (this.HasTitle) - { - return 8; - } - - return 7; - } - } - - /// - /// Gets the position of the context menu. - /// - public Vector2? Position - { - get - { - if (this.HasTitle) return new Vector2(this.atkValues[2].Int, this.atkValues[3].Int); - - return null; - } - } - - private int HasPreviousIndicatorFlagsIndex - { - get - { - if (this.HasTitle) - { - return 6; - } - - return 2; - } - } - - private int HasNextIndicatorFlagsIndex - { - get - { - if (this.HasTitle) - { - return 5; - } - - return 3; - } - } - - private int NameIndexOffset - { - get - { - if (this.HasTitle && this.StructLayout == SubContextMenuStructLayout.Alternate) - { - return 1; - } - - return 0; - } - } - - private int IsDisabledIndexOffset - { - get - { - if (this.HasTitle && this.StructLayout == SubContextMenuStructLayout.Alternate) - { - return 2; - } - - return this.ContextMenuItemCount; - } - } - - /* - /// - /// 0x14000000 | action - /// - public int? MaskedActionIndexOffset - { - get - { - if (this.HasTitle && this.StructLayout == SubContextMenuStructLayout.Alternate) return 3; - - return null; - } - } - */ - - private int SequentialAtkValuesPerContextMenuItem - { - get - { - if (this.HasTitle && this.StructLayout == SubContextMenuStructLayout.Alternate) return 4; - - return 1; - } - } - - private int TotalDesiredAtkValuesPerContextMenuItem - { - get - { - if (this.HasTitle && this.StructLayout == SubContextMenuStructLayout.Alternate) return 4; - - return 2; - } - } - - private bool IsInventoryContext - { - get - { - if ((IntPtr)this.agentContextInterface == (IntPtr)AgentInventoryContext.Instance()) - { - return true; - } - - return false; - } - } - - private SubContextMenuStructLayout? StructLayout - { - get - { - if (this.HasTitle) - { - if (this.atkValues[7].Int == 8) - return SubContextMenuStructLayout.Alternate; - - if (this.atkValues[7].Int == 1) return SubContextMenuStructLayout.Main; - } - - return null; - } - } - - private byte NoopAction - { - get - { - if (this.IsInventoryContext) - return 0xff; - return 0x67; - } - } - - private byte OpenSubContextMenuAction - { - get - { - if (this.IsInventoryContext) - { - // This is actually the action to open the Second Tier context menu and we just hack around it - return 0x31; - } - - return 0x66; - } - } - - private byte? FirstUnhandledAction - { - get - { - if (this.StructLayout is SubContextMenuStructLayout.Alternate) - return 0x68; - - return null; - } - } - - /// - /// Read the context menu from the agent. - /// - /// Read menu items. - public GameContextMenuItem[]? Read() - { - var gameContextMenuItems = new List(); - for (var contextMenuItemIndex = 0; contextMenuItemIndex < this.ContextMenuItemCount; contextMenuItemIndex++) - { - var contextMenuItemAtkValueBaseIndex = this.FirstContextMenuItemIndex + (contextMenuItemIndex * this.SequentialAtkValuesPerContextMenuItem); - - // Get the name - var nameAtkValue = &this.atkValues[contextMenuItemAtkValueBaseIndex + this.NameIndexOffset]; - if (nameAtkValue->Type == 0) - { - continue; - } - - var name = MemoryHelper.ReadSeStringNullTerminated((IntPtr)nameAtkValue->String); - - // Get the enabled state. Note that SE stores this as IsDisabled, NOT IsEnabled (those heathens) - var isEnabled = true; - var isDisabledDefined = this.FirstContextMenuItemIndex + this.ContextMenuItemCount < this.AtkValueCount; - if (isDisabledDefined) - { - var isDisabledAtkValue = &this.atkValues[contextMenuItemAtkValueBaseIndex + this.IsDisabledIndexOffset]; - isEnabled = isDisabledAtkValue->Int == 0; - } - - // Get the action - byte action; - if (this.IsInventoryContext) - { - var actions = &((OldAgentInventoryContext*)this.agentContextInterface)->Actions; - action = *(actions + contextMenuItemAtkValueBaseIndex); - } - else if (this.StructLayout is SubContextMenuStructLayout.Alternate) - { - var redButtonActions = &((OldAgentContext*)this.agentContextInterface)->Items->RedButtonActions; - action = (byte)*(redButtonActions + contextMenuItemIndex); - } - else if (((OldAgentContext*)this.agentContextInterface)->Items != null) - { - var actions = &((OldAgentContext*)this.agentContextInterface)->Items->Actions; - action = *(actions + contextMenuItemAtkValueBaseIndex); - } - else - { - PluginLog.Warning("Context Menu action failed, Items pointer was unexpectedly null."); - return null; - } - - // Get the has previous indicator flag - var hasPreviousIndicatorFlagsAtkValue = &this.atkValues[this.HasPreviousIndicatorFlagsIndex]; - var hasPreviousIndicator = this.HasFlag(hasPreviousIndicatorFlagsAtkValue->UInt, contextMenuItemIndex); - - // Get the has next indicator flag - var hasNextIndicatorFlagsAtkValue = &this.atkValues[this.HasNextIndicatorFlagsIndex]; - var hasNextIndicator = this.HasFlag(hasNextIndicatorFlagsAtkValue->UInt, contextMenuItemIndex); - - var indicator = ContextMenuItemIndicator.None; - if (hasPreviousIndicator) - { - indicator = ContextMenuItemIndicator.Previous; - } - else if (hasNextIndicator) - { - indicator = ContextMenuItemIndicator.Next; - } - - var gameContextMenuItem = new GameContextMenuItem(name, action) - { - IsEnabled = isEnabled, - Indicator = indicator, - }; - - gameContextMenuItems.Add(gameContextMenuItem); - } - - return gameContextMenuItems.ToArray(); - } - - /// - /// Write items to the context menu. - /// - /// The items to write. - /// Whether or not reallocation is allowed. - public void Write(IEnumerable contextMenuItems, bool allowReallocate = true) - { - if (allowReallocate) - { - var newAtkValuesCount = this.FirstContextMenuItemIndex + (contextMenuItems.Count() * this.TotalDesiredAtkValuesPerContextMenuItem); - - // Allocate the new array. We have to do a little dance with the first 8 bytes which represents the array count - const int arrayCountSize = 8; - var newAtkValuesArraySize = arrayCountSize + (Marshal.SizeOf() * newAtkValuesCount); - var newAtkValuesArray = MemoryHelper.GameAllocateUi((ulong)newAtkValuesArraySize); - if (newAtkValuesArray == IntPtr.Zero) - { - return; - } - - var newAtkValues = (AtkValue*)(newAtkValuesArray + arrayCountSize); - - // Zero the memory, then copy the atk values up to the first context menu item atk value - Marshal.Copy(new byte[newAtkValuesArraySize], 0, newAtkValuesArray, newAtkValuesArraySize); - Buffer.MemoryCopy(this.atkValues, newAtkValues, newAtkValuesArraySize - arrayCountSize, (long)sizeof(AtkValue) * this.FirstContextMenuItemIndex); - - // Free the old array - var oldArray = (IntPtr)this.atkValues - arrayCountSize; - var oldArrayCount = *(ulong*)oldArray; - var oldArraySize = arrayCountSize + ((ulong)sizeof(AtkValue) * oldArrayCount); - MemoryHelper.GameFree(ref oldArray, oldArraySize); - - // Set the array count - *(ulong*)newAtkValuesArray = (ulong)newAtkValuesCount; - - this.atkValueCount = newAtkValuesCount; - this.atkValues = newAtkValues; - } - - // Set the context menu item count - const int contextMenuItemCountAtkValueIndex = 0; - var contextMenuItemCountAtkValue = &this.atkValues[contextMenuItemCountAtkValueIndex]; - contextMenuItemCountAtkValue->UInt = (uint)contextMenuItems.Count(); - - // Clear the previous arrow flags - var hasPreviousIndicatorAtkValue = &this.atkValues[this.HasPreviousIndicatorFlagsIndex]; - hasPreviousIndicatorAtkValue->UInt = 0; - - // Clear the next arrow flags - var hasNextIndiactorFlagsAtkValue = &this.atkValues[this.HasNextIndicatorFlagsIndex]; - hasNextIndiactorFlagsAtkValue->UInt = 0; - - for (var contextMenuItemIndex = 0; contextMenuItemIndex < contextMenuItems.Count(); ++contextMenuItemIndex) - { - var contextMenuItem = contextMenuItems.ElementAt(contextMenuItemIndex); - - var contextMenuItemAtkValueBaseIndex = this.FirstContextMenuItemIndex + (contextMenuItemIndex * this.SequentialAtkValuesPerContextMenuItem); - - // Set the name - var nameAtkValue = &this.atkValues[contextMenuItemAtkValueBaseIndex + this.NameIndexOffset]; - nameAtkValue->ChangeType(ValueType.String); - fixed (byte* nameBytesPtr = contextMenuItem.Name.Encode().NullTerminate()) - { - nameAtkValue->SetString(nameBytesPtr); - } - - // Set the enabled state. Note that SE stores this as IsDisabled, NOT IsEnabled (those heathens) - var disabledAtkValue = &this.atkValues[contextMenuItemAtkValueBaseIndex + this.IsDisabledIndexOffset]; - disabledAtkValue->ChangeType(ValueType.Int); - disabledAtkValue->Int = contextMenuItem.IsEnabled ? 0 : 1; - - // Set the action - byte action = 0; - if (contextMenuItem is GameContextMenuItem gameContextMenuItem) - { - action = gameContextMenuItem.SelectedAction; - } - else if (contextMenuItem is CustomContextMenuItem customContextMenuItem) - { - action = this.NoopAction; - } - else if (contextMenuItem is OpenSubContextMenuItem openSubContextMenuItem) - { - action = this.OpenSubContextMenuAction; - } - - if (this.IsInventoryContext) - { - var actions = &((OldAgentInventoryContext*)this.agentContextInterface)->Actions; - *(actions + this.FirstContextMenuItemIndex + contextMenuItemIndex) = action; - } - else if (this.StructLayout is SubContextMenuStructLayout.Alternate && this.FirstUnhandledAction != null) - { - // Some weird placeholder goes here - var actions = &((OldAgentContext*)this.agentContextInterface)->Items->Actions; - *(actions + this.FirstContextMenuItemIndex + contextMenuItemIndex) = (byte)(this.FirstUnhandledAction.Value + contextMenuItemIndex); - - // Make sure there's one of these function pointers for every item. - // The function needs to be the same, so we just copy the first one into every index. - var unkFunctionPointers = &((OldAgentContext*)this.agentContextInterface)->Items->UnkFunctionPointers; - *(unkFunctionPointers + this.FirstContextMenuItemIndex + contextMenuItemIndex) = *(unkFunctionPointers + this.FirstContextMenuItemIndex); - - // The real action goes here - var redButtonActions = &((OldAgentContext*)this.agentContextInterface)->Items->RedButtonActions; - *(redButtonActions + contextMenuItemIndex) = action; - } - else if (((OldAgentContext*)this.agentContextInterface)->Items != null) - { - // TODO: figure out why this branch is reached on inventory contexts and why Items is sometimes null. - var actions = &((OldAgentContext*)this.agentContextInterface)->Items->Actions; - *(actions + this.FirstContextMenuItemIndex + contextMenuItemIndex) = action; - } - else - { - PluginLog.Warning("Context Menu action failed, Items pointer was unexpectedly null."); - } - - if (contextMenuItem.Indicator == ContextMenuItemIndicator.Previous) - { - this.SetFlag(ref hasPreviousIndicatorAtkValue->UInt, contextMenuItemIndex, true); - } - else if (contextMenuItem.Indicator == ContextMenuItemIndicator.Next) - { - this.SetFlag(ref hasNextIndiactorFlagsAtkValue->UInt, contextMenuItemIndex, true); - } - } - } - - private bool HasFlag(uint mask, int itemIndex) - { - return (mask & (1 << itemIndex)) > 0; - } - - private void SetFlag(ref uint mask, int itemIndex, bool value) - { - mask &= ~(1U << itemIndex); - - if (value) - { - mask |= (uint)(1 << itemIndex); - } - } - - /* - private void Log() - { - Log(this.atkValueCount, this.atkValues); - } - - private static void Log(int atkValueCount, AtkValue* atkValues) - { - PluginLog.Debug($"ContextMenuReader.Log"); - - for (int atkValueIndex = 0; atkValueIndex < atkValueCount; ++atkValueIndex) - { - var atkValue = &atkValues[atkValueIndex]; - - object? value; - switch (atkValue->Type) - { - case ValueType.Int: - value = atkValue->Int; - break; - case ValueType.Bool: - value = atkValue->Byte; - break; - case ValueType.UInt: - value = atkValue->UInt; - break; - case ValueType.Float: - value = atkValue->Float; - break; - default: - { - if (atkValue->Type == ValueType.String - || (int)atkValue->Type == 38 - || (int)atkValue->Type == 8) - { - value = MemoryHelper.ReadSeStringNullTerminated((IntPtr)atkValue->String); - } - else - { - value = $"{(IntPtr)atkValue->String:X}"; - } - - break; - } - } - - PluginLog.Debug($"atkValues[{atkValueIndex}]={(IntPtr)atkValue:X} {atkValue->Type}={value}"); - } - } - */ - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItem.cs b/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItem.cs deleted file mode 100644 index 029ca7ef0..000000000 --- a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItem.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Dalamud.Game.Text; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// An item in a context menu with a user defined action. - /// - public sealed class CustomContextMenuItem : ContextMenuItem - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - /// The action that will be called when the item is selected. - public CustomContextMenuItem(SeString name, CustomContextMenuItemSelectedDelegate itemSelected) - : base(new SeString().Append(new UIForegroundPayload(539)).Append($"{SeIconChar.BoxedLetterD.ToIconString()} ").Append(new UIForegroundPayload(0)).Append(name)) - { - this.ItemSelected = itemSelected; - } - - /// - /// Gets the action that will be called when the item is selected. - /// - public CustomContextMenuItemSelectedDelegate ItemSelected { get; } - - /// - public override int GetHashCode() - { - unchecked - { - var hash = base.GetHashCode(); - hash = (hash * 23) + this.ItemSelected.GetHashCode(); - return hash; - } - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedArgs.cs b/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedArgs.cs deleted file mode 100644 index 1092a427d..000000000 --- a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedArgs.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Provides data for methods. - /// - public sealed class CustomContextMenuItemSelectedArgs - { - /// - /// Initializes a new instance of the class. - /// - /// The currently opened context menu. - /// The selected item within the currently opened context menu. - internal CustomContextMenuItemSelectedArgs(ContextMenuOpenedArgs contextMenuOpenedArgs, CustomContextMenuItem selectedItem) - { - this.ContextMenuOpenedArgs = contextMenuOpenedArgs; - this.SelectedItem = selectedItem; - } - - /// - /// Gets the currently opened context menu. - /// - public ContextMenuOpenedArgs ContextMenuOpenedArgs { get; init; } - - /// - /// Gets the selected item within the currently opened context menu. - /// - public CustomContextMenuItem SelectedItem { get; init; } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedDelegate.cs b/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedDelegate.cs deleted file mode 100644 index 615c2e773..000000000 --- a/Dalamud/Game/Gui/ContextMenus/CustomContextMenuItemSelectedDelegate.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Represents the method that handles when a is selected. - /// - /// The data associated with the selected . - public delegate void CustomContextMenuItemSelectedDelegate(CustomContextMenuItemSelectedArgs args); -} diff --git a/Dalamud/Game/Gui/ContextMenus/GameContextMenuItem.cs b/Dalamud/Game/Gui/ContextMenus/GameContextMenuItem.cs deleted file mode 100644 index cf4751f33..000000000 --- a/Dalamud/Game/Gui/ContextMenus/GameContextMenuItem.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Dalamud.Game.Text.SeStringHandling; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// An item in a context menu that with a specific game action. - /// - public sealed class GameContextMenuItem : ContextMenuItem - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - /// The game action that will be handled when the item is selected. - internal GameContextMenuItem(SeString name, byte selectedAction) - : base(name) - { - this.SelectedAction = selectedAction; - } - - /// - /// Gets the game action that will be handled when the item is selected. - /// - public byte SelectedAction { get; } - - /// - public override int GetHashCode() - { - unchecked - { - var hash = base.GetHashCode(); - hash = (hash * 23) + this.SelectedAction; - return hash; - } - } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/GameObjectContext.cs b/Dalamud/Game/Gui/ContextMenus/GameObjectContext.cs deleted file mode 100644 index 076888f6a..000000000 --- a/Dalamud/Game/Gui/ContextMenus/GameObjectContext.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Provides game object context to a context menu. - /// - public sealed class GameObjectContext - { - /// - /// Initializes a new instance of the class. - /// - /// The id of the game object. - /// The lower content id of the game object. - /// The name of the game object. - /// The world id of the game object. - internal GameObjectContext(uint? id, ulong? contentId, string? name, ushort? worldId) - { - this.Id = id; - this.ContentId = contentId; - this.Name = name; - this.WorldId = worldId; - } - - /// - /// Gets the id of the game object. - /// - public uint? Id { get; } - - /// - /// Gets the content id of the game object. - /// - public ulong? ContentId { get; } - - /// - /// Gets the name of the game object. - /// - public string? Name { get; } - - /// - /// Gets the world id of the game object. - /// - public ushort? WorldId { get; } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/InventoryItemContext.cs b/Dalamud/Game/Gui/ContextMenus/InventoryItemContext.cs deleted file mode 100644 index 7f9677ef9..000000000 --- a/Dalamud/Game/Gui/ContextMenus/InventoryItemContext.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// Provides inventory item context to a context menu. - /// - public sealed class InventoryItemContext - { - /// - /// Initializes a new instance of the class. - /// - /// The id of the item. - /// The count of the item in the stack. - /// Whether the item is high quality. - internal InventoryItemContext(uint id, uint count, bool isHighQuality) - { - this.Id = id; - this.Count = count; - this.IsHighQuality = isHighQuality; - } - - /// - /// Gets the id of the item. - /// - public uint Id { get; } - - /// - /// Gets the count of the item in the stack. - /// - public uint Count { get; } - - /// - /// Gets a value indicating whether the item is high quality. - /// - public bool IsHighQuality { get; } - } -} diff --git a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContext.cs b/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContext.cs deleted file mode 100644 index d0c59b616..000000000 --- a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContext.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.InteropServices; - -using FFXIVClientStructs.FFXIV.Client.System.String; -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Gui.ContextMenus.OldStructs; - -// TODO: This is transplanted from client structs before the rework. Need to take some time to sort all of this out soon. - -[StructLayout(LayoutKind.Explicit)] -public unsafe struct OldAgentContext -{ - public static OldAgentContext* Instance() => (OldAgentContext*)FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Context); - - [FieldOffset(0x0)] public AgentInterface AgentInterface; - [FieldOffset(0x0)] public OldAgentContextInterface AgentContextInterface; - [FieldOffset(0xD18)] public unsafe OldAgentContextMenuItems* Items; - [FieldOffset(0xE08)] public Utf8String GameObjectName; - [FieldOffset(0xEE0)] public ulong GameObjectContentId; - [FieldOffset(0xEF0)] public uint GameObjectId; - [FieldOffset(0xF00)] public ushort GameObjectWorldId; -} - -[StructLayout(LayoutKind.Explicit)] -public struct OldAgentContextMenuItems -{ - [FieldOffset(0x0)] public ushort AtkValueCount; - [FieldOffset(0x8)] public AtkValue AtkValues; - [FieldOffset(0x428)] public byte Actions; - [FieldOffset(0x450)] public ulong UnkFunctionPointers; - [FieldOffset(0x598)] public ulong RedButtonActions; -} diff --git a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContextInterface.cs b/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContextInterface.cs deleted file mode 100644 index 2dde22dfb..000000000 --- a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentContextInterface.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Runtime.InteropServices; - -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Gui.ContextMenus.OldStructs; - -// TODO: This is transplanted from client structs before the rework. Need to take some time to sort all of this out soon. - -[StructLayout(LayoutKind.Explicit)] -public unsafe struct OldAgentContextInterface -{ - [FieldOffset(0x0)] public AgentInterface AgentInterface; - [FieldOffset(0x670)] public unsafe byte SelectedIndex; - [FieldOffset(0x690)] public byte* Unk1; - [FieldOffset(0xD08)] public byte* SubContextMenuTitle; - [FieldOffset(0x1740)] public bool IsSubContextMenu; -} diff --git a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentInventoryContext.cs b/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentInventoryContext.cs deleted file mode 100644 index 56fbbac97..000000000 --- a/Dalamud/Game/Gui/ContextMenus/OldStructs/OldAgentInventoryContext.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Runtime.InteropServices; - -using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using FFXIVClientStructs.FFXIV.Component.GUI; - -namespace Dalamud.Game.Gui.ContextMenus.OldStructs; - -[StructLayout(LayoutKind.Explicit)] -public unsafe struct OldAgentInventoryContext -{ - public static OldAgentInventoryContext* Instance() => (OldAgentInventoryContext*) FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.InventoryContext); - - [FieldOffset(0x0)] public AgentInterface AgentInterface; - [FieldOffset(0x0)] public OldAgentContextInterface AgentContextInterface; - [FieldOffset(0x2C)] public uint FirstContextMenuItemAtkValueIndex; - [FieldOffset(0x30)] public uint ContextMenuItemCount; - [FieldOffset(0x38)] public AtkValue AtkValues; - [FieldOffset(0x558)] public unsafe byte Actions; - [FieldOffset(0x5A8)] public uint UnkFlags; - [FieldOffset(0x5B0)] public uint PositionX; - [FieldOffset(0x5B4)] public uint PositionY; - [FieldOffset(0x5F8)] public uint InventoryItemId; - [FieldOffset(0x5FC)] public uint InventoryItemCount; - [FieldOffset(0x604)] public bool InventoryItemIsHighQuality; -} diff --git a/Dalamud/Game/Gui/ContextMenus/OpenSubContextMenuItem.cs b/Dalamud/Game/Gui/ContextMenus/OpenSubContextMenuItem.cs deleted file mode 100644 index 78be954f7..000000000 --- a/Dalamud/Game/Gui/ContextMenus/OpenSubContextMenuItem.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Dalamud.Game.Text; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; - -namespace Dalamud.Game.Gui.ContextMenus -{ - /// - /// An item in a context menu that can open a sub context menu. - /// - public sealed class OpenSubContextMenuItem : ContextMenuItem - { - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - /// The action that will be called when the item is selected. - internal OpenSubContextMenuItem(SeString name, ContextMenuOpenedDelegate opened) - : base(new SeString().Append(new UIForegroundPayload(539)).Append($"{SeIconChar.BoxedLetterD.ToIconString()} ").Append(new UIForegroundPayload(0)).Append(name)) - { - this.Opened = opened; - this.Indicator = ContextMenuItemIndicator.Next; - } - - /// - /// Gets the action that will be called when the item is selected. - /// - public ContextMenuOpenedDelegate Opened { get; } - - /// - public override int GetHashCode() - { - unchecked - { - int hash = base.GetHashCode(); - hash = (hash * 23) + this.Opened.GetHashCode(); - return hash; - } - } - } -} diff --git a/Dalamud/Game/Internal/DalamudAtkTweaks.cs b/Dalamud/Game/Internal/DalamudAtkTweaks.cs index cd8ca156c..40bc1a26a 100644 --- a/Dalamud/Game/Internal/DalamudAtkTweaks.cs +++ b/Dalamud/Game/Internal/DalamudAtkTweaks.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using CheapLoc; using Dalamud.Configuration.Internal; using Dalamud.Data; -using Dalamud.Game.Gui.ContextMenus; +//using Dalamud.Game.Gui.ContextMenus; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; @@ -37,8 +37,8 @@ namespace Dalamud.Game.Internal [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); - [ServiceManager.ServiceDependency] - private readonly ContextMenu contextMenu = Service.Get(); + // [ServiceManager.ServiceDependency] + // private readonly ContextMenu contextMenu = Service.Get(); private readonly string locDalamudPlugins; private readonly string locDalamudSettings; @@ -65,7 +65,7 @@ namespace Dalamud.Game.Internal this.locDalamudPlugins = Loc.Localize("SystemMenuPlugins", "Dalamud Plugins"); this.locDalamudSettings = Loc.Localize("SystemMenuSettings", "Dalamud Settings"); - this.contextMenu.ContextMenuOpened += this.ContextMenuOnContextMenuOpened; + // this.contextMenu.ContextMenuOpened += this.ContextMenuOnContextMenuOpened; } private delegate void AgentHudOpenSystemMenuPrototype(void* thisPtr, AtkValue* atkValueArgs, uint menuSize); @@ -86,6 +86,7 @@ namespace Dalamud.Game.Internal this.hookAtkUnitBaseReceiveGlobalEvent.Enable(); } + /* private void ContextMenuOnContextMenuOpened(ContextMenuOpenedArgs args) { var systemText = Service.GetNullable()?.GetExcelSheet()?.GetRow(1059)?.Text?.RawString; // "System" @@ -109,6 +110,7 @@ namespace Dalamud.Game.Internal })); } } + */ private IntPtr AtkUnitBaseReceiveGlobalEventDetour(AtkUnitBase* thisPtr, ushort cmd, uint a3, IntPtr a4, uint* arg) { @@ -270,7 +272,7 @@ namespace Dalamud.Game.Internal this.hookUiModuleRequestMainCommand.Dispose(); this.hookAtkUnitBaseReceiveGlobalEvent.Dispose(); - this.contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened; + // this.contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened; } this.disposed = true; diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs index 2c6ea7a3c..76b6b59b2 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs @@ -1,7 +1,6 @@ using System; using Dalamud.Data; -using Dalamud.Game.Gui.ContextMenus; using Dalamud.Utility; using ImGuiNET; using Lumina.Excel.GeneratedSheets; @@ -44,6 +43,7 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps /// public SelfTestStepResult RunStep() { + /* var contextMenu = Service.Get(); var dataMgr = Service.Get(); @@ -133,13 +133,16 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps } return SelfTestStepResult.Waiting; + */ + + return SelfTestStepResult.Pass; } /// public void CleanUp() { - var contextMenu = Service.Get(); - contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened; + // var contextMenu = Service.Get(); + // contextMenu.ContextMenuOpened -= this.ContextMenuOnContextMenuOpened; this.currentSubStep = SubStep.Start; this.clickedItemId = 0; @@ -147,6 +150,7 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps this.multipleTriggerOne = this.multipleTriggerTwo = false; } + /* private void ContextMenuOnContextMenuOpened(ContextMenuOpenedArgs args) { Log.Information("Got context menu with parent addon: {ParentAddonName}, title:{Title}, itemcnt:{ItemCount}", args.ParentAddonName, args.Title, args.Items.Count); @@ -239,5 +243,6 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps break; } } + */ } }