126 lines
4.7 KiB
C#
Executable File
126 lines
4.7 KiB
C#
Executable File
using System;
|
|
using System.Runtime.InteropServices;
|
|
using Dalamud.Game.Text.SeStringHandling;
|
|
using Dalamud.Hooking;
|
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
|
using Siggingway;
|
|
|
|
namespace NominaOcculta {
|
|
internal class GameFunctions : IDisposable {
|
|
private static class Signatures {
|
|
internal const string GenerateName = "E8 ?? ?? ?? ?? 48 8D 8B ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 1B 48 8D 8B ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 8B D0 E8 ?? ?? ?? ?? 48 8B CB 48 8B 7C 24";
|
|
internal const string Utf8StringCtor = "E8 ?? ?? ?? ?? 44 2B F7";
|
|
internal const string Utf8StringDtor = "80 79 21 00 75 12";
|
|
internal const string AtkTextNodeSetText = "E8 ?? ?? ?? ?? 8D 4E 32";
|
|
internal const string LoadExd = "40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 41 0F B6 D9";
|
|
}
|
|
|
|
#region Delegates
|
|
|
|
private delegate IntPtr GenerateNameDelegate(int race, int clan, int gender, IntPtr first, IntPtr last);
|
|
|
|
private delegate IntPtr Utf8StringCtorDelegate(IntPtr memory);
|
|
|
|
private delegate void Utf8StringDtorDelegate(IntPtr memory);
|
|
|
|
private delegate void AtkTextNodeSetTextDelegate(IntPtr node, IntPtr text);
|
|
|
|
private delegate byte LoadExdDelegate(IntPtr a1, string sheetName, byte a3, byte a4);
|
|
|
|
private delegate IntPtr GetExcelModuleDelegate(IntPtr uiModule);
|
|
|
|
#endregion
|
|
|
|
#region Functions
|
|
|
|
[Signature(Signatures.Utf8StringCtor)]
|
|
private Utf8StringCtorDelegate Utf8StringCtor { get; init; } = null!;
|
|
|
|
[Signature(Signatures.Utf8StringDtor)]
|
|
private Utf8StringDtorDelegate Utf8StringDtor { get; init; } = null!;
|
|
|
|
[Signature(Signatures.GenerateName)]
|
|
private GenerateNameDelegate InternalGenerateName { get; init; } = null!;
|
|
|
|
[Signature(Signatures.LoadExd)]
|
|
private LoadExdDelegate LoadExd { get; init; } = null!;
|
|
|
|
#endregion
|
|
|
|
[Signature(Signatures.AtkTextNodeSetText, DetourName = nameof(AtkTextNodeSetTextDetour))]
|
|
private Hook<AtkTextNodeSetTextDelegate> AtkTextNodeSetTextHook { get; init; } = null!;
|
|
|
|
internal delegate void AtkTextNodeSetTextEventDelegate(IntPtr node, IntPtr text, ref SeString? overwrite);
|
|
|
|
internal event AtkTextNodeSetTextEventDelegate? AtkTextNodeSetText;
|
|
|
|
private Plugin Plugin { get; }
|
|
|
|
private IntPtr First { get; }
|
|
private IntPtr Last { get; }
|
|
|
|
internal GameFunctions(Plugin plugin) {
|
|
this.Plugin = plugin;
|
|
|
|
Siggingway.Siggingway.Initialise(this.Plugin.SigScanner, this);
|
|
|
|
this.AtkTextNodeSetTextHook.Enable();
|
|
|
|
this.First = Marshal.AllocHGlobal(128);
|
|
this.Last = Marshal.AllocHGlobal(128);
|
|
|
|
this.Utf8StringCtor(this.First);
|
|
this.Utf8StringCtor(this.Last);
|
|
}
|
|
|
|
public void Dispose() {
|
|
this.Utf8StringDtor(this.Last);
|
|
this.Utf8StringDtor(this.First);
|
|
Marshal.FreeHGlobal(this.Last);
|
|
Marshal.FreeHGlobal(this.First);
|
|
this.AtkTextNodeSetTextHook.Dispose();
|
|
}
|
|
|
|
private unsafe void AtkTextNodeSetTextDetour(IntPtr node, IntPtr text) {
|
|
SeString? overwrite = null;
|
|
this.AtkTextNodeSetText?.Invoke(node, text, ref overwrite);
|
|
|
|
if (overwrite != null) {
|
|
fixed (byte* newText = overwrite.Encode().Terminate()) {
|
|
this.AtkTextNodeSetTextHook.Original(node, (IntPtr) newText);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
this.AtkTextNodeSetTextHook.Original(node, text);
|
|
}
|
|
|
|
public string? GenerateName(int race, int clan, int gender) {
|
|
if (this.InternalGenerateName(race, clan, gender, this.First, this.Last) == IntPtr.Zero) {
|
|
return null;
|
|
}
|
|
|
|
var first = Marshal.PtrToStringUTF8(Marshal.ReadIntPtr(this.First));
|
|
var last = Marshal.PtrToStringUTF8(Marshal.ReadIntPtr(this.Last));
|
|
|
|
if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(last)) {
|
|
return null;
|
|
}
|
|
|
|
return $"{first} {last}";
|
|
}
|
|
|
|
public unsafe void LoadSheet(string name) {
|
|
var ui = (IntPtr) Framework.Instance()->GetUiModule();
|
|
var getExcelModulePtr = *(*(IntPtr**) ui + 5);
|
|
var getExcelModule = Marshal.GetDelegateForFunctionPointer<GetExcelModuleDelegate>(getExcelModulePtr);
|
|
var excelModule = getExcelModule(ui);
|
|
var exdModule = *(IntPtr*) (excelModule + 8);
|
|
var excel = *(IntPtr*) (exdModule + 0x20);
|
|
|
|
this.LoadExd(excel, name, 0, 1);
|
|
}
|
|
}
|
|
}
|