diff --git a/GoodMemory/FodyWeavers.xml b/GoodMemory/FodyWeavers.xml new file mode 100755 index 0000000..2dfb1f4 --- /dev/null +++ b/GoodMemory/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + diff --git a/GoodMemory/GoodMemory.csproj b/GoodMemory/GoodMemory.csproj index a16aaa1..9e307cb 100755 --- a/GoodMemory/GoodMemory.csproj +++ b/GoodMemory/GoodMemory.csproj @@ -23,5 +23,8 @@ + + + diff --git a/GoodMemory/Plugin.cs b/GoodMemory/Plugin.cs index c8da990..9e39f07 100644 --- a/GoodMemory/Plugin.cs +++ b/GoodMemory/Plugin.cs @@ -1,74 +1,38 @@ -using Dalamud.Hooking; -using Dalamud.Plugin; +using Dalamud.Plugin; using Lumina.Excel.GeneratedSheets; using System; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; -using System.Text; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Game.Text.SeStringHandling.Payloads; +using XivCommon; +using XivCommon.Functions.Tooltips; namespace GoodMemory { public class Plugin : IDalamudPlugin { - private bool _disposedValue; - public string Name => "Good Memory"; public DalamudPluginInterface Interface { get; private set; } = null!; private GameFunctions Functions { get; set; } = null!; - private readonly IntPtr _alloc = Marshal.AllocHGlobal(4096); - private Hook? _tooltipHook; - - private unsafe delegate IntPtr TooltipDelegate(IntPtr a1, uint** a2, byte*** a3); + private XivCommonBase Common { get; set; } = null!; public void Initialize(DalamudPluginInterface pluginInterface) { this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null"); this.Functions = new GameFunctions(this); - this.SetUpHook(); - } - - protected virtual void Dispose(bool disposing) { - if (this._disposedValue) { - return; - } - - if (disposing) { - this._tooltipHook?.Dispose(); - Marshal.FreeHGlobal(this._alloc); - } - - this._disposedValue = true; + this.Common = new XivCommonBase(pluginInterface, Hooks.Tooltips); + this.Common.Functions.Tooltips.OnItemTooltip += this.OnItemTooltip; } public void Dispose() { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - this.Dispose(true); - GC.SuppressFinalize(this); + this.Common.Functions.Tooltips.OnItemTooltip -= this.OnItemTooltip; + this.Common.Dispose(); } - private void SetUpHook() { - var tooltipPtr = this.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 50 48 8B 42 ??"); - if (tooltipPtr == IntPtr.Zero) { - throw new ApplicationException("Could not set up tooltip hook because of null pointer"); - } - - unsafe { - this._tooltipHook = new Hook(tooltipPtr, new TooltipDelegate(this.OnTooltip)); - } - - this._tooltipHook.Enable(); - } - - private unsafe void TooltipLogic(uint** a2, byte*** a3) { - // this can be replaced with a mid-func hook when reloaded hooks is in dalamud - // but for now, do the same logic the func does and replace the text after - var v3 = *(a2 + 4); - var v9 = *(v3 + 4); - - if ((v9 & 2) == 0) { + private void OnItemTooltip(ItemTooltip tooltip, ulong itemId) { + if (!tooltip.Fields.HasFlag(ItemTooltipFields.Description)) { return; } - var itemId = this.Interface.Framework.Gui.HoveredItem; if (itemId > 2_000_000) { return; } @@ -78,22 +42,11 @@ namespace GoodMemory { } var item = this.Interface.Data.GetExcelSheet().GetRow((uint) itemId); - if (item == null) { return; } - // get the pointer to the text - var startPtr = *(a3 + 4) + 13; - // get the text pointer - var start = *startPtr; - - // work around function being called twice - if (start == (byte*) this._alloc) { - return; - } - - string overwrite; + var description = tooltip[ItemTooltipString.Description]; // Faded Copies if (item.FilterGroup == 12 && item.ItemUICategory.Value?.RowId == 94 && item.LevelItem.Value?.RowId == 1) { @@ -103,8 +56,6 @@ namespace GoodMemory { .Where(result => result != null) .ToArray(); - overwrite = ReadString(start); - foreach (var result in recipeResults) { var resultAction = result.ItemAction?.Value; if (!ActionTypeExt.IsValidAction(resultAction)) { @@ -119,7 +70,7 @@ namespace GoodMemory { continue; } - this.AppendIfAcquired(ref overwrite, result, orch.Name); + this.AppendIfAcquired(description, result, orch.Name); } } else { var action = item.ItemAction?.Value; @@ -128,31 +79,14 @@ namespace GoodMemory { return; } - // get the text - overwrite = ReadString(start); - // generate our replacement text - this.AppendIfAcquired(ref overwrite, item); + this.AppendIfAcquired(description, item); } - // write our replacement text into our own managed memory (4096 bytes) - WriteString((byte*) this._alloc, overwrite, true); - - // overwrite the original pointer with our own - *startPtr = (byte*) this._alloc; + tooltip[ItemTooltipString.Description] = description; } - private unsafe IntPtr OnTooltip(IntPtr a1, uint** a2, byte*** a3) { - try { - this.TooltipLogic(a2, a3); - } catch (Exception ex) { - PluginLog.Error($"Could not modify tooltip:\n{ex.Message}\n{ex.StackTrace}"); - } - - return this._tooltipHook!.Original(a1, a2, a3); - } - - private void AppendIfAcquired(ref string txt, Item item, string? name = null) { + private void AppendIfAcquired(SeString txt, Item item, string? name = null) { string yes; string no; string acquired; @@ -195,34 +129,10 @@ namespace GoodMemory { } var has = this.Functions.HasAcquired(item) ? yes : no; - txt = name == null - ? $"{txt}\n{acquired}{colon}{has}" - : $"{txt}\n{acquired}{parenL}{name}{parenR}{colon}{has}"; - } - - private static unsafe string ReadString(byte* ptr) { - var offset = 0; - while (true) { - var b = *(ptr + offset); - if (b == 0) { - break; - } - - offset += 1; - } - - return Encoding.UTF8.GetString(ptr, offset); - } - - private static unsafe void WriteString(byte* dst, string s, bool finalise = false) { - var bytes = Encoding.UTF8.GetBytes(s); - for (var i = 0; i < bytes.Length; i++) { - *(dst + i) = bytes[i]; - } - - if (finalise) { - *(dst + bytes.Length) = 0; - } + var text = name == null + ? $"\n{acquired}{colon}{has}" + : $"\n{acquired}{parenL}{name}{parenR}{colon}{has}"; + txt.Payloads.Add(new TextPayload(text)); } } }