119 lines
3.2 KiB
C#
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();
|
|
}
|
|
}
|