qstbak/QuestionableCompanion/QuestionableCompanion.Services/MemoryHelper.cs
2025-12-04 04:39:08 +10:00

119 lines
3.2 KiB
C#

using System;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI;
namespace QuestionableCompanion.Services;
public class MemoryHelper : IDisposable
{
public unsafe delegate void RidePillionDelegate(BattleChara* target, int seatIndex);
private readonly IPluginLog log;
private Hook<RidePillionDelegate>? ridePillionHook;
private const string RidePillionSignature = "48 85 C9 0F 84 ?? ?? ?? ?? 48 89 6C 24 ?? 56 48 83 EC";
public RidePillionDelegate? RidePillion { get; private set; }
public unsafe MemoryHelper(IPluginLog log, IGameInteropProvider gameInterop)
{
this.log = log;
try
{
ridePillionHook = gameInterop.HookFromSignature<RidePillionDelegate>("48 85 C9 0F 84 ?? ?? ?? ?? 48 89 6C 24 ?? 56 48 83 EC", RidePillionDetour);
if (ridePillionHook != null && ridePillionHook.Address != IntPtr.Zero)
{
log.Information($"[MemoryHelper] RidePillion function found at 0x{ridePillionHook.Address:X}");
RidePillion = ridePillionHook.Original;
log.Information("[MemoryHelper] RidePillion function initialized successfully");
}
else
{
log.Warning("[MemoryHelper] RidePillion function not found - will fall back to commands");
}
}
catch (Exception ex)
{
log.Error("[MemoryHelper] Error initializing RidePillion: " + ex.Message);
}
}
private unsafe void RidePillionDetour(BattleChara* target, int seatIndex)
{
ridePillionHook?.Original(target, seatIndex);
}
public unsafe bool ExecuteRidePillion(BattleChara* target, int seatIndex = 10)
{
if (RidePillion == null)
{
log.Warning("[MemoryHelper] RidePillion function not available");
return false;
}
if (target == null)
{
log.Error("[MemoryHelper] RidePillion target is null");
return false;
}
try
{
log.Information($"[MemoryHelper] Executing RidePillion on target (seat {seatIndex})");
RidePillion(target, seatIndex);
return true;
}
catch (Exception ex)
{
log.Error("[MemoryHelper] RidePillion execution error: " + ex.Message);
return false;
}
}
public unsafe bool SendChatMessage(string message)
{
try
{
UIModule* uiModule = UIModule.Instance();
if (uiModule == null)
{
log.Error("[MemoryHelper] UIModule is null!");
return false;
}
byte[] bytes = Encoding.UTF8.GetBytes(message);
nint mem1 = Marshal.AllocHGlobal(400);
nint mem2 = Marshal.AllocHGlobal(bytes.Length + 30);
try
{
Marshal.Copy(bytes, 0, mem2, bytes.Length);
Marshal.WriteByte(mem2 + bytes.Length, 0);
Marshal.WriteInt64(mem1, ((IntPtr)mem2).ToInt64());
Marshal.WriteInt64(mem1 + 8, 64L);
Marshal.WriteInt64(mem1 + 8 + 8, bytes.Length + 1);
Marshal.WriteInt64(mem1 + 8 + 8 + 8, 0L);
uiModule->ProcessChatBoxEntry((Utf8String*)mem1);
log.Information("[MemoryHelper] Chat message sent: " + message);
return true;
}
finally
{
Marshal.FreeHGlobal(mem1);
Marshal.FreeHGlobal(mem2);
}
}
catch (Exception ex)
{
log.Error("[MemoryHelper] SendChatMessage error: " + ex.Message);
return false;
}
}
public void Dispose()
{
ridePillionHook?.Dispose();
}
}