From a7a3ca3e290c6df396b09d0d27efce0c1928860b Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Tue, 27 Apr 2021 11:24:39 -0400 Subject: [PATCH] feat: add context for context menu --- XivCommon/Functions/ContextMenu.cs | 46 +++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/XivCommon/Functions/ContextMenu.cs b/XivCommon/Functions/ContextMenu.cs index cc2efbe..714354c 100755 --- a/XivCommon/Functions/ContextMenu.cs +++ b/XivCommon/Functions/ContextMenu.cs @@ -37,8 +37,16 @@ namespace XivCommon.Functions { /// private const int MenuActionsOffset = 0x428; + private const int ActorIdOffset = 0xEF0; + private const int ContentIdLowerOffset = 0xEE0; + private const int NoopContextId = 0x67; + /// + /// The delegate that is run when a context menu item is selected. + /// + public delegate void ContextMenuItemSelectedDelegate(IntPtr addon, IntPtr agent, ContextMenuItemSelectedArgs args); + private unsafe delegate byte ContextMenuOpenDelegate(IntPtr addon, int menuSize, AtkValue* atkValueArgs); private delegate IntPtr GetAddonByInternalIdDelegate(IntPtr raptureAtkUnitManager, short id); @@ -47,9 +55,9 @@ namespace XivCommon.Functions { private Hook? ContextMenuOpenHook { get; } - private delegate byte ContextMenuItemSelectedDelegate(IntPtr addon, int index, byte a3); + private delegate byte ContextMenuItemSelectedInternalDelegate(IntPtr addon, int index, byte a3); - private Hook? ContextMenuItemSelectedHook { get; } + private Hook? ContextMenuItemSelectedHook { get; } private unsafe delegate void AtkValueChangeTypeDelegate(AtkValue* thisPtr, ValueType type); @@ -97,7 +105,7 @@ namespace XivCommon.Functions { } if (scanner.TryScanText(Signatures.ContextMenuSelected, out var selectedPtr, "Context Menu selected")) { - this.ContextMenuItemSelectedHook = new Hook(selectedPtr, new ContextMenuItemSelectedDelegate(this.ItemSelectedDetour)); + this.ContextMenuItemSelectedHook = new Hook(selectedPtr, new ContextMenuItemSelectedInternalDelegate(this.ItemSelectedDetour)); this.ContextMenuItemSelectedHook.Enable(); } } @@ -166,7 +174,7 @@ namespace XivCommon.Functions { return this.ContextMenuOpenHook!.Original(addon, menuSize, atkValueArgs); } - private byte ItemSelectedDetour(IntPtr addon, int index, byte a3) { + private unsafe byte ItemSelectedDetour(IntPtr addon, int index, byte a3) { var addonName = this.GetParentAddonName(addon); if (addonName == null) { goto Original; @@ -183,9 +191,13 @@ namespace XivCommon.Functions { goto Original; } + var agent = this.GetContextMenuAgent(); + var actorId = *(uint*) (agent + ActorIdOffset); + var contentIdLower = *(uint*) (agent + ContentIdLowerOffset); + var item = registered[idx]; try { - item.Action(); + item.Action(addon, agent, new ContextMenuItemSelectedArgs(actorId, contentIdLower)); } catch (Exception ex) { PluginLog.LogError(ex, "Exception in custom context menu item"); } @@ -264,14 +276,14 @@ namespace XivCommon.Functions { /// /// The action to perform when this item is clicked. /// - public Action Action { get; } + public ContextMenu.ContextMenuItemSelectedDelegate Action { get; } /// /// Create a new context menu item. /// /// the English name of the item, copied to other languages /// the action to perform on click - public ContextMenuItem(string name, Action action) { + public ContextMenuItem(string name, ContextMenu.ContextMenuItemSelectedDelegate action) { this.NameEnglish = name; this.NameJapanese = name; this.NameFrench = name; @@ -280,4 +292,24 @@ namespace XivCommon.Functions { this.Action = action; } } + + /// + /// Arguments for the context menu item selected delegate. + /// + public class ContextMenuItemSelectedArgs { + /// + /// The actor ID for this context menu. May be invalid (0xE0000000). + /// + public uint ActorId { get; } + + /// + /// The lower half of the content ID of the actor for this context menu. May be zero. + /// + public uint ContentIdLower { get; } + + internal ContextMenuItemSelectedArgs(uint actorId, uint contentIdLower) { + this.ContentIdLower = contentIdLower; + this.ActorId = actorId; + } + } }