feat: add basic tooltip support
This commit is contained in:
parent
290af584ab
commit
fd44b711a3
13
XivCommon/Functions/Tooltips/ActionTooltip.cs
Executable file
13
XivCommon/Functions/Tooltips/ActionTooltip.cs
Executable file
|
@ -0,0 +1,13 @@
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace XivCommon.Functions.Tooltips {
|
||||
public unsafe class ActionTooltip : BaseTooltip {
|
||||
public ActionTooltip(SeStringManager manager, Tooltips.StringArrayDataSetStringDelegate sadSetString, byte*** pointer) : base(manager, sadSetString, pointer) {
|
||||
}
|
||||
|
||||
public SeString this[ActionTooltipString ats] {
|
||||
get => this[(int) ats];
|
||||
set => this[(int) ats] = value;
|
||||
}
|
||||
}
|
||||
}
|
19
XivCommon/Functions/Tooltips/ActionTooltipString.cs
Executable file
19
XivCommon/Functions/Tooltips/ActionTooltipString.cs
Executable file
|
@ -0,0 +1,19 @@
|
|||
namespace XivCommon.Functions.Tooltips {
|
||||
public enum ActionTooltipString {
|
||||
Name = 0,
|
||||
Type = 1,
|
||||
RangeLabel = 3,
|
||||
Range = 4,
|
||||
RadiusLabel = 5,
|
||||
Radius = 6,
|
||||
CostLabel = 7,
|
||||
Cost = 8,
|
||||
RecastLabel = 9,
|
||||
Recast = 10,
|
||||
CastLabel = 11,
|
||||
Cast = 12,
|
||||
Description = 13,
|
||||
Acquired = 14,
|
||||
Affinity = 15,
|
||||
}
|
||||
}
|
30
XivCommon/Functions/Tooltips/BaseTooltip.cs
Executable file
30
XivCommon/Functions/Tooltips/BaseTooltip.cs
Executable file
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace XivCommon.Functions.Tooltips {
|
||||
public abstract unsafe class BaseTooltip {
|
||||
protected SeStringManager Manager { get; }
|
||||
protected Tooltips.StringArrayDataSetStringDelegate SadSetString { get; }
|
||||
protected readonly byte*** _pointer; // this is StringArrayData* when ClientStructs is updated
|
||||
|
||||
internal BaseTooltip(SeStringManager manager, Tooltips.StringArrayDataSetStringDelegate sadSetString, byte*** pointer) {
|
||||
this.Manager = manager;
|
||||
this.SadSetString = sadSetString;
|
||||
this._pointer = pointer;
|
||||
}
|
||||
|
||||
protected SeString this[int offset] {
|
||||
get {
|
||||
var ptr = *(this._pointer + 4) + offset;
|
||||
return Util.ReadSeString((IntPtr) (*ptr), this.Manager);
|
||||
}
|
||||
set {
|
||||
var encoded = value.Encode().Terminate();
|
||||
|
||||
fixed (byte* encodedPtr = encoded) {
|
||||
this.SadSetString((IntPtr) this._pointer, offset, encodedPtr, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
XivCommon/Functions/Tooltips/ItemTooltip.cs
Executable file
13
XivCommon/Functions/Tooltips/ItemTooltip.cs
Executable file
|
@ -0,0 +1,13 @@
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace XivCommon.Functions.Tooltips {
|
||||
public unsafe class ItemTooltip : BaseTooltip {
|
||||
public ItemTooltip(SeStringManager manager, Tooltips.StringArrayDataSetStringDelegate sadSetString, byte*** pointer) : base(manager, sadSetString, pointer) {
|
||||
}
|
||||
|
||||
public SeString this[ItemTooltipString its] {
|
||||
get => this[(int) its];
|
||||
set => this[(int) its] = value;
|
||||
}
|
||||
}
|
||||
}
|
51
XivCommon/Functions/Tooltips/ItemTooltipString.cs
Executable file
51
XivCommon/Functions/Tooltips/ItemTooltipString.cs
Executable file
|
@ -0,0 +1,51 @@
|
|||
namespace XivCommon.Functions.Tooltips {
|
||||
public enum ItemTooltipString {
|
||||
Name = 0,
|
||||
GlamourName = 1,
|
||||
Type = 2,
|
||||
Description = 13,
|
||||
Quantity = 14,
|
||||
Level = 27,
|
||||
EffectsLabel = 15,
|
||||
Effects = 16,
|
||||
VendorSellPrice = 25,
|
||||
VendorBuyPrice = 63,
|
||||
Crafter = 26,
|
||||
Stat1Label = 4,
|
||||
Stat1 = 7,
|
||||
Stat1Delta = 10,
|
||||
Stat2Label = 5,
|
||||
Stat2 = 8,
|
||||
Stat2Delta = 11,
|
||||
Stat3Label = 6,
|
||||
Stat3 = 9,
|
||||
Stat3Delta = 12,
|
||||
EquipJobs = 22,
|
||||
EquipLevel = 23,
|
||||
Condition = 28,
|
||||
SpriritbondLabel = 29,
|
||||
Spiritbond = 30,
|
||||
RepairLevel = 31,
|
||||
Materials = 32,
|
||||
QuickRepairs = 33,
|
||||
MateriaMelding = 34,
|
||||
AbleToDoShit = 35,
|
||||
BonusesLabel = 36,
|
||||
Bonus1 = 37,
|
||||
Bonus2 = 38,
|
||||
Bonus3 = 39,
|
||||
Bonus4 = 40,
|
||||
MateriaLabel = 52,
|
||||
Materia1 = 53,
|
||||
Materia2 = 54,
|
||||
Materia3 = 55,
|
||||
Materia4 = 56,
|
||||
Materia5 = 57,
|
||||
Materia1Effect = 58,
|
||||
Materia2Effect = 59,
|
||||
Materia3Effect = 60,
|
||||
Materia4Effect = 61,
|
||||
Materia5Effect = 62,
|
||||
ControllerControls = 64,
|
||||
}
|
||||
}
|
118
XivCommon/Functions/Tooltips/Tooltips.cs
Executable file
118
XivCommon/Functions/Tooltips/Tooltips.cs
Executable file
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.Internal.Gui;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
|
||||
namespace XivCommon.Functions.Tooltips {
|
||||
public class Tooltips : IDisposable {
|
||||
private static class Signatures {
|
||||
internal const string ItemGenerateTooltip = "48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 50 48 8B 42 ??";
|
||||
internal const string ActionGenerateTooltip = "E8 ?? ?? ?? ?? 48 8B D5 48 8B CF E8 ?? ?? ?? ?? 41 8D 45 FF 83 F8 01 77 6D";
|
||||
internal const string SadSetString = "E8 ?? ?? ?? ?? F6 47 14 08";
|
||||
}
|
||||
|
||||
public unsafe delegate void StringArrayDataSetStringDelegate(IntPtr self, int index, byte* str, byte updatePtr, byte copyToUi, byte dontSetModified);
|
||||
|
||||
private unsafe delegate IntPtr ItemGenerateTooltipDelegate(IntPtr addon, uint** numberArrayData, byte*** stringArrayData);
|
||||
|
||||
private unsafe delegate IntPtr ActionGenerateTooltipDelegate(IntPtr addon, uint** numberArrayData, byte*** stringArrayData);
|
||||
|
||||
private StringArrayDataSetStringDelegate? SadSetString { get; }
|
||||
private Hook<ItemGenerateTooltipDelegate>? ItemGenerateTooltipHook { get; }
|
||||
private Hook<ActionGenerateTooltipDelegate>? ActionGenerateTooltipHook { get; }
|
||||
|
||||
public delegate void ItemTooltipEventDelegate(ItemTooltip itemTooltip, ulong itemId);
|
||||
|
||||
public delegate void ActionTooltipEventDelegate(ActionTooltip actionTooltip, HoveredAction action);
|
||||
|
||||
public event ItemTooltipEventDelegate? OnItemTooltip;
|
||||
public event ActionTooltipEventDelegate? OnActionTooltip;
|
||||
|
||||
private GameGui GameGui { get; }
|
||||
private SeStringManager SeStringManager { get; }
|
||||
private ItemTooltip? ItemTooltip { get; set; }
|
||||
private ActionTooltip? ActionTooltip { get; set; }
|
||||
|
||||
internal Tooltips(SigScanner scanner, GameGui gui, SeStringManager manager, bool enabled) {
|
||||
this.GameGui = gui;
|
||||
this.SeStringManager = manager;
|
||||
|
||||
if (scanner.TryScanText(Signatures.SadSetString, out var setStringPtr, "Tooltips - StringArrayData::SetString")) {
|
||||
this.SadSetString = Marshal.GetDelegateForFunctionPointer<StringArrayDataSetStringDelegate>(setStringPtr);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scanner.TryScanText(Signatures.ItemGenerateTooltip, out var generateItemPtr, "Tooltips - Items")) {
|
||||
unsafe {
|
||||
this.ItemGenerateTooltipHook = new Hook<ItemGenerateTooltipDelegate>(generateItemPtr, new ItemGenerateTooltipDelegate(this.ItemGenerateTooltipDetour));
|
||||
}
|
||||
|
||||
this.ItemGenerateTooltipHook.Enable();
|
||||
}
|
||||
|
||||
if (scanner.TryScanText(Signatures.ActionGenerateTooltip, out var actionItemPtr, "Tooltips - Actions")) {
|
||||
unsafe {
|
||||
this.ActionGenerateTooltipHook = new Hook<ActionGenerateTooltipDelegate>(actionItemPtr, new ActionGenerateTooltipDelegate(this.ActionGenerateTooltipDetour));
|
||||
}
|
||||
|
||||
this.ActionGenerateTooltipHook.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
this.ActionGenerateTooltipHook?.Dispose();
|
||||
this.ItemGenerateTooltipHook?.Dispose();
|
||||
}
|
||||
|
||||
private unsafe IntPtr ItemGenerateTooltipDetour(IntPtr addon, uint** numberArrayData, byte*** stringArrayData) {
|
||||
try {
|
||||
return this.ItemGenerateTooltipDetourInner(addon, numberArrayData, stringArrayData);
|
||||
} catch (Exception ex) {
|
||||
Logger.LogError(ex, "Exception in item tooltip detour");
|
||||
}
|
||||
|
||||
return this.ItemGenerateTooltipHook!.Original(addon, numberArrayData, stringArrayData);
|
||||
}
|
||||
|
||||
private unsafe IntPtr ItemGenerateTooltipDetourInner(IntPtr addon, uint** numberArrayData, byte*** stringArrayData) {
|
||||
// var v3 = *(numberArrayData + 4);
|
||||
// var v9 = *(v3 + 4);
|
||||
//
|
||||
// if ((v9 & 2) == 0) {
|
||||
// goto Original;
|
||||
// }
|
||||
|
||||
this.ItemTooltip = new ItemTooltip(this.SeStringManager, this.SadSetString!, stringArrayData);
|
||||
|
||||
this.OnItemTooltip?.Invoke(this.ItemTooltip, this.GameGui.HoveredItem);
|
||||
|
||||
return this.ItemGenerateTooltipHook!.Original(addon, numberArrayData, stringArrayData);
|
||||
}
|
||||
|
||||
private unsafe IntPtr ActionGenerateTooltipDetour(IntPtr addon, uint** numberArrayData, byte*** stringArrayData) {
|
||||
try {
|
||||
return this.ActionGenerateTooltipDetourInner(addon, numberArrayData, stringArrayData);
|
||||
} catch (Exception ex) {
|
||||
Logger.LogError(ex, "Exception in action tooltip detour");
|
||||
}
|
||||
|
||||
return this.ActionGenerateTooltipHook!.Original(addon, numberArrayData, stringArrayData);
|
||||
}
|
||||
|
||||
private unsafe IntPtr ActionGenerateTooltipDetourInner(IntPtr addon, uint** numberArrayData, byte*** stringArrayData) {
|
||||
this.ActionTooltip = new ActionTooltip(this.SeStringManager, this.SadSetString!, stringArrayData);
|
||||
|
||||
this.OnActionTooltip?.Invoke(this.ActionTooltip, this.GameGui.HoveredAction);
|
||||
|
||||
return this.ActionGenerateTooltipHook!.Original(addon, numberArrayData, stringArrayData);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
|||
using Dalamud.Plugin;
|
||||
using XivCommon.Functions;
|
||||
using XivCommon.Functions.ContextMenu;
|
||||
using XivCommon.Functions.Tooltips;
|
||||
|
||||
namespace XivCommon {
|
||||
/// <summary>
|
||||
|
@ -54,7 +55,7 @@ namespace XivCommon {
|
|||
public Talk Talk { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Chat bubble functions and events
|
||||
/// Chat bubble functions and events
|
||||
/// </summary>
|
||||
public ChatBubbles ChatBubbles { get; }
|
||||
|
||||
|
@ -63,6 +64,11 @@ namespace XivCommon {
|
|||
/// </summary>
|
||||
public ContextMenu ContextMenu { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Tooltip events
|
||||
/// </summary>
|
||||
public Tooltips Tooltips { get; }
|
||||
|
||||
internal GameFunctions(Hooks hooks, DalamudPluginInterface @interface) {
|
||||
this.Interface = @interface;
|
||||
|
||||
|
@ -79,6 +85,7 @@ namespace XivCommon {
|
|||
this.Talk = new Talk(scanner, seStringManager, hooks.HasFlag(Hooks.Talk));
|
||||
this.ChatBubbles = new ChatBubbles(dalamud, scanner, seStringManager, hooks.HasFlag(Hooks.ChatBubbles));
|
||||
this.ContextMenu = new ContextMenu(this, scanner, @interface.Framework.Gui, @interface.ClientState.ClientLanguage, hooks);
|
||||
this.Tooltips = new Tooltips(scanner, @interface.Framework.Gui, seStringManager, hooks.HasFlag(Hooks.Tooltips));
|
||||
|
||||
if (scanner.TryScanText(Signatures.GetAgentByInternalId, out var byInternalIdPtr, "GetAgentByInternalId")) {
|
||||
this.GetAgentByInternalIdInternal = Marshal.GetDelegateForFunctionPointer<GetAgentByInternalIdDelegate>(byInternalIdPtr);
|
||||
|
@ -91,6 +98,7 @@ namespace XivCommon {
|
|||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() {
|
||||
this.Tooltips.Dispose();
|
||||
this.ContextMenu.Dispose();
|
||||
this.ChatBubbles.Dispose();
|
||||
this.Talk.Dispose();
|
||||
|
@ -112,7 +120,7 @@ namespace XivCommon {
|
|||
/// <returns>Pointer</returns>
|
||||
public IntPtr GetAgentModule() {
|
||||
var uiModule = this.GetUiModule();
|
||||
var getAgentModulePtr = FollowPtrChain(uiModule, new[] {0, 0x110});
|
||||
var getAgentModulePtr = FollowPtrChain(uiModule, new[] { 0, 0x110 });
|
||||
var getAgentModule = Marshal.GetDelegateForFunctionPointer<GetAgentModuleDelegate>(getAgentModulePtr);
|
||||
return getAgentModule(uiModule);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,13 @@ namespace XivCommon {
|
|||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The Tooltips hooks.
|
||||
///
|
||||
/// This hook is used in order to enable the tooltip events.
|
||||
/// </summary>
|
||||
Tooltips = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// The BattleTalk hook.
|
||||
///
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
|
||||
namespace XivCommon {
|
||||
internal static class Util {
|
||||
|
@ -28,6 +28,11 @@ namespace XivCommon {
|
|||
return buf.ToArray();
|
||||
}
|
||||
|
||||
internal static SeString ReadSeString(IntPtr memory, SeStringManager manager) {
|
||||
var terminated = ReadTerminated(memory);
|
||||
return manager.Parse(terminated);
|
||||
}
|
||||
|
||||
internal static void PrintMissingSig(string name) {
|
||||
Logger.LogWarning($"Could not find signature for {name}. This functionality will be disabled.");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user