refactor: use XivCommon for tooltips

This commit is contained in:
Anna 2021-05-29 01:32:50 -04:00
parent 6acfabc4fc
commit 489f7582a3
3 changed files with 27 additions and 111 deletions

3
GoodMemory/FodyWeavers.xml Executable file
View File

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ILMerge/>
</Weavers>

View File

@ -23,5 +23,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="1.2.1"/> <PackageReference Include="DalamudPackager" Version="1.2.1"/>
<PackageReference Include="Fody" Version="6.5.1" PrivateAssets="all"/>
<PackageReference Include="ILMerge.Fody" Version="1.16.0" PrivateAssets="all"/>
<PackageReference Include="XivCommon" Version="1.6.0"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,74 +1,38 @@
using Dalamud.Hooking; using Dalamud.Plugin;
using Dalamud.Plugin;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using Dalamud.Game.Text.SeStringHandling;
using System.Text; using Dalamud.Game.Text.SeStringHandling.Payloads;
using XivCommon;
using XivCommon.Functions.Tooltips;
namespace GoodMemory { namespace GoodMemory {
public class Plugin : IDalamudPlugin { public class Plugin : IDalamudPlugin {
private bool _disposedValue;
public string Name => "Good Memory"; public string Name => "Good Memory";
public DalamudPluginInterface Interface { get; private set; } = null!; public DalamudPluginInterface Interface { get; private set; } = null!;
private GameFunctions Functions { get; set; } = null!; private GameFunctions Functions { get; set; } = null!;
private readonly IntPtr _alloc = Marshal.AllocHGlobal(4096); private XivCommonBase Common { get; set; } = null!;
private Hook<TooltipDelegate>? _tooltipHook;
private unsafe delegate IntPtr TooltipDelegate(IntPtr a1, uint** a2, byte*** a3);
public void Initialize(DalamudPluginInterface pluginInterface) { public void Initialize(DalamudPluginInterface pluginInterface) {
this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null"); this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null");
this.Functions = new GameFunctions(this); this.Functions = new GameFunctions(this);
this.SetUpHook(); this.Common = new XivCommonBase(pluginInterface, Hooks.Tooltips);
} this.Common.Functions.Tooltips.OnItemTooltip += this.OnItemTooltip;
protected virtual void Dispose(bool disposing) {
if (this._disposedValue) {
return;
}
if (disposing) {
this._tooltipHook?.Dispose();
Marshal.FreeHGlobal(this._alloc);
}
this._disposedValue = true;
} }
public void Dispose() { public void Dispose() {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method this.Common.Functions.Tooltips.OnItemTooltip -= this.OnItemTooltip;
this.Dispose(true); this.Common.Dispose();
GC.SuppressFinalize(this);
} }
private void SetUpHook() { private void OnItemTooltip(ItemTooltip tooltip, ulong itemId) {
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 (!tooltip.Fields.HasFlag(ItemTooltipFields.Description)) {
if (tooltipPtr == IntPtr.Zero) {
throw new ApplicationException("Could not set up tooltip hook because of null pointer");
}
unsafe {
this._tooltipHook = new Hook<TooltipDelegate>(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) {
return; return;
} }
var itemId = this.Interface.Framework.Gui.HoveredItem;
if (itemId > 2_000_000) { if (itemId > 2_000_000) {
return; return;
} }
@ -78,22 +42,11 @@ namespace GoodMemory {
} }
var item = this.Interface.Data.GetExcelSheet<Item>().GetRow((uint) itemId); var item = this.Interface.Data.GetExcelSheet<Item>().GetRow((uint) itemId);
if (item == null) { if (item == null) {
return; return;
} }
// get the pointer to the text var description = tooltip[ItemTooltipString.Description];
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;
// Faded Copies // Faded Copies
if (item.FilterGroup == 12 && item.ItemUICategory.Value?.RowId == 94 && item.LevelItem.Value?.RowId == 1) { if (item.FilterGroup == 12 && item.ItemUICategory.Value?.RowId == 94 && item.LevelItem.Value?.RowId == 1) {
@ -103,8 +56,6 @@ namespace GoodMemory {
.Where(result => result != null) .Where(result => result != null)
.ToArray(); .ToArray();
overwrite = ReadString(start);
foreach (var result in recipeResults) { foreach (var result in recipeResults) {
var resultAction = result.ItemAction?.Value; var resultAction = result.ItemAction?.Value;
if (!ActionTypeExt.IsValidAction(resultAction)) { if (!ActionTypeExt.IsValidAction(resultAction)) {
@ -119,7 +70,7 @@ namespace GoodMemory {
continue; continue;
} }
this.AppendIfAcquired(ref overwrite, result, orch.Name); this.AppendIfAcquired(description, result, orch.Name);
} }
} else { } else {
var action = item.ItemAction?.Value; var action = item.ItemAction?.Value;
@ -128,31 +79,14 @@ namespace GoodMemory {
return; return;
} }
// get the text
overwrite = ReadString(start);
// generate our replacement text // generate our replacement text
this.AppendIfAcquired(ref overwrite, item); this.AppendIfAcquired(description, item);
} }
// write our replacement text into our own managed memory (4096 bytes) tooltip[ItemTooltipString.Description] = description;
WriteString((byte*) this._alloc, overwrite, true);
// overwrite the original pointer with our own
*startPtr = (byte*) this._alloc;
} }
private unsafe IntPtr OnTooltip(IntPtr a1, uint** a2, byte*** a3) { private void AppendIfAcquired(SeString txt, Item item, string? name = null) {
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) {
string yes; string yes;
string no; string no;
string acquired; string acquired;
@ -195,34 +129,10 @@ namespace GoodMemory {
} }
var has = this.Functions.HasAcquired(item) ? yes : no; var has = this.Functions.HasAcquired(item) ? yes : no;
txt = name == null var text = name == null
? $"{txt}\n{acquired}{colon}{has}" ? $"\n{acquired}{colon}{has}"
: $"{txt}\n{acquired}{parenL}{name}{parenR}{colon}{has}"; : $"\n{acquired}{parenL}{name}{parenR}{colon}{has}";
} txt.Payloads.Add(new TextPayload(text));
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;
}
} }
} }
} }