XivCommon/XivCommon/Functions/Tooltips/Tooltips.cs

152 lines
6.1 KiB
C#
Raw Normal View History

2021-05-23 09:43:48 +00:00
using System;
using System.Runtime.InteropServices;
using Dalamud.Game;
2021-08-22 20:46:41 +00:00
using Dalamud.Game.Gui;
2021-05-23 09:43:48 +00:00
using Dalamud.Hooking;
namespace XivCommon.Functions.Tooltips {
/// <summary>
/// The class containing tooltip functionality
/// </summary>
2021-05-23 09:43:48 +00:00
public class Tooltips : IDisposable {
private static class Signatures {
2023-05-25 16:57:41 +00:00
internal const string AgentItemDetailUpdateTooltip = "E8 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 89 AE ?? ?? ?? ?? 48 89 AE";
internal const string AgentActionDetailUpdateTooltip = "E8 ?? ?? ?? ?? EB 68 FF 50 40";
2021-05-23 09:43:48 +00:00
internal const string SadSetString = "E8 ?? ?? ?? ?? F6 47 14 08";
}
2021-12-05 23:20:32 +00:00
// Last checked: 6.0
// E8 ?? ?? ?? ?? EB 68 FF 50 40
private const int AgentActionDetailUpdateFlagOffset = 0x58;
internal unsafe delegate void StringArrayDataSetStringDelegate(IntPtr self, int index, byte* str, byte updatePtr, byte copyToUi, byte dontSetModified);
2021-05-23 09:43:48 +00:00
2022-08-24 04:20:48 +00:00
private unsafe delegate ulong ItemUpdateTooltipDelegate(IntPtr agent, int** numberArrayData, byte*** stringArrayData, float a4);
2021-05-23 09:43:48 +00:00
private unsafe delegate void ActionUpdateTooltipDelegate(IntPtr agent, int** numberArrayData, byte*** stringArrayData);
2021-05-23 09:43:48 +00:00
private StringArrayDataSetStringDelegate? SadSetString { get; }
private Hook<ItemUpdateTooltipDelegate>? ItemUpdateTooltipHook { get; }
private Hook<ActionUpdateTooltipDelegate>? ActionGenerateTooltipHook { get; }
2021-05-23 09:43:48 +00:00
/// <summary>
/// The delegate for item tooltip events.
/// </summary>
2021-05-23 09:43:48 +00:00
public delegate void ItemTooltipEventDelegate(ItemTooltip itemTooltip, ulong itemId);
/// <summary>
/// The tooltip for action tooltip events.
/// </summary>
2021-05-23 09:43:48 +00:00
public delegate void ActionTooltipEventDelegate(ActionTooltip actionTooltip, HoveredAction action);
/// <summary>
/// <para>
/// The event that is fired when an item tooltip is being generated for display.
/// </para>
/// <para>
/// Requires the <see cref="Hooks.Tooltips"/> hook to be enabled.
/// </para>
/// </summary>
2021-05-23 09:43:48 +00:00
public event ItemTooltipEventDelegate? OnItemTooltip;
/// <summary>
/// <para>
/// The event that is fired when an action tooltip is being generated for display.
/// </para>
/// <para>
/// Requires the <see cref="Hooks.Tooltips"/> hook to be enabled.
/// </para>
/// </summary>
2021-05-23 09:43:48 +00:00
public event ActionTooltipEventDelegate? OnActionTooltip;
private GameGui GameGui { get; }
private ItemTooltip? ItemTooltip { get; set; }
private ActionTooltip? ActionTooltip { get; set; }
2021-08-28 19:02:03 +00:00
internal Tooltips(SigScanner scanner, GameGui gui, bool enabled) {
2021-05-23 09:43:48 +00:00
this.GameGui = gui;
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.AgentItemDetailUpdateTooltip, out var updateItemPtr, "Tooltips - Items")) {
2021-05-23 09:43:48 +00:00
unsafe {
2022-08-19 13:47:36 +00:00
this.ItemUpdateTooltipHook = Hook<ItemUpdateTooltipDelegate>.FromAddress(updateItemPtr, this.ItemUpdateTooltipDetour);
2021-05-23 09:43:48 +00:00
}
this.ItemUpdateTooltipHook.Enable();
2021-05-23 09:43:48 +00:00
}
if (scanner.TryScanText(Signatures.AgentActionDetailUpdateTooltip, out var updateActionPtr, "Tooltips - Actions")) {
2021-05-23 09:43:48 +00:00
unsafe {
2022-08-19 13:47:36 +00:00
this.ActionGenerateTooltipHook = Hook<ActionUpdateTooltipDelegate>.FromAddress(updateActionPtr, this.ActionUpdateTooltipDetour);
2021-05-23 09:43:48 +00:00
}
this.ActionGenerateTooltipHook.Enable();
}
}
/// <inheritdoc />
public void Dispose() {
this.ActionGenerateTooltipHook?.Dispose();
this.ItemUpdateTooltipHook?.Dispose();
}
2022-08-24 04:20:48 +00:00
private unsafe ulong ItemUpdateTooltipDetour(IntPtr agent, int** numberArrayData, byte*** stringArrayData, float a4) {
var ret = this.ItemUpdateTooltipHook!.Original(agent, numberArrayData, stringArrayData, a4);
if (ret > 0) {
try {
this.ItemUpdateTooltipDetourInner(numberArrayData, stringArrayData);
} catch (Exception ex) {
Logger.LogError(ex, "Exception in item tooltip detour");
}
}
return ret;
2021-05-23 09:43:48 +00:00
}
private unsafe void ItemUpdateTooltipDetourInner(int** numberArrayData, byte*** stringArrayData) {
2021-08-28 19:02:03 +00:00
this.ItemTooltip = new ItemTooltip(this.SadSetString!, stringArrayData, numberArrayData);
2021-05-23 09:43:48 +00:00
try {
this.OnItemTooltip?.Invoke(this.ItemTooltip, this.GameGui.HoveredItem);
2021-05-23 09:43:48 +00:00
} catch (Exception ex) {
Logger.LogError(ex, "Exception in OnItemTooltip event");
2021-05-23 09:43:48 +00:00
}
}
private unsafe void ActionUpdateTooltipDetour(IntPtr agent, int** numberArrayData, byte*** stringArrayData) {
var flag = *(byte*) (agent + AgentActionDetailUpdateFlagOffset);
this.ActionGenerateTooltipHook!.Original(agent, numberArrayData, stringArrayData);
2021-05-23 09:43:48 +00:00
if (flag == 0) {
return;
}
2021-05-23 09:43:48 +00:00
try {
this.ActionUpdateTooltipDetourInner(numberArrayData, stringArrayData);
2021-05-23 09:43:48 +00:00
} catch (Exception ex) {
Logger.LogError(ex, "Exception in action tooltip detour");
}
}
private unsafe void ActionUpdateTooltipDetourInner(int** numberArrayData, byte*** stringArrayData) {
2021-08-28 19:02:03 +00:00
this.ActionTooltip = new ActionTooltip(this.SadSetString!, stringArrayData, numberArrayData);
2021-05-23 09:43:48 +00:00
try {
this.OnActionTooltip?.Invoke(this.ActionTooltip, this.GameGui.HoveredAction);
} catch (Exception ex) {
Logger.LogError(ex, "Exception in OnActionTooltip event");
}
2021-05-23 09:43:48 +00:00
}
}
}