|
|
|
@ -7,15 +7,15 @@ using System.Linq;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using Dalamud.Game.Text.SeStringHandling;
|
|
|
|
|
using Dalamud.Logging;
|
|
|
|
|
using Dalamud.Memory;
|
|
|
|
|
using Dalamud.Utility.Signatures;
|
|
|
|
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
|
|
|
|
using XIVChatCommon.Message;
|
|
|
|
|
using XIVChatCommon.Message.Server;
|
|
|
|
|
|
|
|
|
|
namespace XIVChatPlugin {
|
|
|
|
|
internal class GameFunctions : IDisposable {
|
|
|
|
|
internal unsafe class GameFunctions : IDisposable {
|
|
|
|
|
private static class Signatures {
|
|
|
|
|
internal const string GetUiModule = "E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0";
|
|
|
|
|
internal const string ProcessChat = "48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9";
|
|
|
|
|
internal const string Input = "80 B9 ?? ?? ?? ?? ?? 0F 9C C0";
|
|
|
|
|
internal const string InputAfk = "E8 ?? ?? ?? ?? 0F 28 74 24 ?? 0F B6 F0";
|
|
|
|
@ -30,14 +30,12 @@ namespace XIVChatPlugin {
|
|
|
|
|
internal const string ChannelNameChange = "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6";
|
|
|
|
|
internal const string XivStringCtor = "E8 ?? ?? ?? ?? 44 2B F7";
|
|
|
|
|
internal const string XivStringDtor = "E8 ?? ?? ?? ?? B0 6E";
|
|
|
|
|
internal const string UiModule = "48 8B 0D ?? ?? ?? ?? 48 8D 54 24 ?? 48 83 C1 10 E8";
|
|
|
|
|
internal const string ColourHandler = "48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ?? 66 85 DB 0F 94 C2 E8 ?? ?? ?? ?? E9";
|
|
|
|
|
internal const string ColourLookup = "48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Plugin Plugin { get; }
|
|
|
|
|
|
|
|
|
|
private delegate IntPtr GetUiModuleDelegate(IntPtr basePtr);
|
|
|
|
|
#region Delegates
|
|
|
|
|
|
|
|
|
|
private delegate void EasierProcessChatBoxDelegate(IntPtr uiModule, IntPtr message, IntPtr unused, byte a4);
|
|
|
|
|
|
|
|
|
@ -63,21 +61,59 @@ namespace XIVChatPlugin {
|
|
|
|
|
|
|
|
|
|
private delegate IntPtr XivStringDtorDelegate(IntPtr memory);
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Hooks
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.Input, DetourName = nameof(IsInputDetour))]
|
|
|
|
|
private readonly Hook<IsInputDelegate>? _isInputHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.InputAfk, DetourName = nameof(IsInputAfkDetour))]
|
|
|
|
|
private readonly Hook<IsInputAfkDelegate>? _isInputAfkHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.FriendList, DetourName = nameof(OnRequestFriendList))]
|
|
|
|
|
private readonly Hook<RequestFriendListDelegate>? _friendListHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.Format, DetourName = nameof(OnFormatFriendList))]
|
|
|
|
|
private readonly Hook<FormatFriendListNameDelegate>? _formatHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.ReceiveChunk, DetourName = nameof(OnReceiveFriendList))]
|
|
|
|
|
private readonly Hook<OnReceiveFriendListChunkDelegate>? _receiveChunkHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.Channel, DetourName = nameof(ChangeChatChannelDetour))]
|
|
|
|
|
private readonly Hook<ChatChannelChangeDelegate>? _chatChannelChangeHook;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.ChannelNameChange, DetourName = nameof(ChangeChatChannelNameDetour))]
|
|
|
|
|
private readonly Hook<ChatChannelChangeNameDelegate>? _chatChannelChangeNameHook;
|
|
|
|
|
|
|
|
|
|
private readonly GetUiModuleDelegate? _getUiModule;
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Functions
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.ProcessChat)]
|
|
|
|
|
private readonly EasierProcessChatBoxDelegate? _easierProcessChatBox;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.GetColour)]
|
|
|
|
|
private readonly GetColourInfoDelegate? _getColourInfo;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.ChannelCommand)]
|
|
|
|
|
private readonly ChannelChangeCommandDelegate? _channelChangeCommand;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.XivStringCtor)]
|
|
|
|
|
private readonly XivStringCtorDelegate? _xivStringCtor;
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.XivStringDtor)]
|
|
|
|
|
private readonly XivStringDtorDelegate? _xivStringDtor;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Pointers
|
|
|
|
|
|
|
|
|
|
[Signature(Signatures.ColourLookup, ScanType = ScanType.StaticAddress)]
|
|
|
|
|
private IntPtr ColourLookup { get; init; }
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public ServerHousingLocation HousingLocation {
|
|
|
|
|
get {
|
|
|
|
|
var info = this.Plugin.Common.Functions.Housing.Location;
|
|
|
|
@ -102,9 +138,6 @@ namespace XIVChatPlugin {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private InputSetters HadInput { get; set; } = InputSetters.None;
|
|
|
|
|
private IntPtr UiModulePtr { get; }
|
|
|
|
|
private IntPtr ColourHandler { get; }
|
|
|
|
|
private IntPtr ColourLookup { get; }
|
|
|
|
|
private IntPtr _friendListManager = IntPtr.Zero;
|
|
|
|
|
private IntPtr _chatManager = IntPtr.Zero;
|
|
|
|
|
private readonly IntPtr _emptyXivString = IntPtr.Zero;
|
|
|
|
@ -119,113 +152,8 @@ namespace XIVChatPlugin {
|
|
|
|
|
|
|
|
|
|
internal GameFunctions(Plugin plugin) {
|
|
|
|
|
this.Plugin = plugin;
|
|
|
|
|
|
|
|
|
|
var getUiModulePtr = this.Plugin.ScanText(Signatures.GetUiModule);
|
|
|
|
|
var easierProcessChatBoxPtr = this.Plugin.ScanText(Signatures.ProcessChat);
|
|
|
|
|
var inputPtr = this.Plugin.ScanText(Signatures.Input);
|
|
|
|
|
var inputAfkPtr = this.Plugin.ScanText(Signatures.InputAfk);
|
|
|
|
|
var friendListPtr = this.Plugin.ScanText(Signatures.FriendList);
|
|
|
|
|
var formatPtr = this.Plugin.ScanText(Signatures.Format);
|
|
|
|
|
var recvChunkPtr = this.Plugin.ScanText(Signatures.ReceiveChunk);
|
|
|
|
|
var getColourPtr = this.Plugin.ScanText(Signatures.GetColour);
|
|
|
|
|
var channelPtr = this.Plugin.ScanText(Signatures.Channel);
|
|
|
|
|
var channelNamePtr = this.Plugin.ScanText(Signatures.ChannelNameChange);
|
|
|
|
|
var channelCommandPtr = this.Plugin.ScanText(Signatures.ChannelCommand);
|
|
|
|
|
var xivStringCtorPtr = this.Plugin.ScanText(Signatures.XivStringCtor);
|
|
|
|
|
var xivStringDtorPtr = this.Plugin.ScanText(Signatures.XivStringDtor);
|
|
|
|
|
|
|
|
|
|
this.UiModulePtr = this.Plugin.GetStaticAddressFromSig(Signatures.UiModule);
|
|
|
|
|
if (this.UiModulePtr == IntPtr.Zero) {
|
|
|
|
|
PluginLog.Warning("Static pointer was null: {0}", nameof(this.UiModulePtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.ColourHandler = this.Plugin.GetStaticAddressFromSig(Signatures.ColourHandler);
|
|
|
|
|
if (this.ColourHandler == IntPtr.Zero) {
|
|
|
|
|
PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourHandler));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.ColourLookup = this.Plugin.GetStaticAddressFromSig(Signatures.ColourLookup);
|
|
|
|
|
if (this.ColourLookup == IntPtr.Zero) {
|
|
|
|
|
PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourLookup));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getUiModulePtr != IntPtr.Zero) {
|
|
|
|
|
this._getUiModule = Marshal.GetDelegateForFunctionPointer<GetUiModuleDelegate>(getUiModulePtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(getUiModulePtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (easierProcessChatBoxPtr != IntPtr.Zero) {
|
|
|
|
|
this._easierProcessChatBox = Marshal.GetDelegateForFunctionPointer<EasierProcessChatBoxDelegate>(easierProcessChatBoxPtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(easierProcessChatBoxPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getColourPtr != IntPtr.Zero) {
|
|
|
|
|
this._getColourInfo = Marshal.GetDelegateForFunctionPointer<GetColourInfoDelegate>(getColourPtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(getColourPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channelCommandPtr != IntPtr.Zero) {
|
|
|
|
|
this._channelChangeCommand = Marshal.GetDelegateForFunctionPointer<ChannelChangeCommandDelegate>(channelCommandPtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(channelCommandPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (xivStringCtorPtr != IntPtr.Zero) {
|
|
|
|
|
this._xivStringCtor = Marshal.GetDelegateForFunctionPointer<XivStringCtorDelegate>(xivStringCtorPtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(xivStringCtorPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (xivStringDtorPtr != IntPtr.Zero) {
|
|
|
|
|
this._xivStringDtor = Marshal.GetDelegateForFunctionPointer<XivStringDtorDelegate>(xivStringDtorPtr);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling function: {0}", nameof(xivStringDtorPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (friendListPtr != IntPtr.Zero) {
|
|
|
|
|
this._friendListHook = new Hook<RequestFriendListDelegate>(friendListPtr, this.OnRequestFriendList);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(friendListPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (formatPtr != IntPtr.Zero) {
|
|
|
|
|
this._formatHook = new Hook<FormatFriendListNameDelegate>(formatPtr, this.OnFormatFriendList);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(formatPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (recvChunkPtr != IntPtr.Zero) {
|
|
|
|
|
this._receiveChunkHook = new Hook<OnReceiveFriendListChunkDelegate>(recvChunkPtr, this.OnReceiveFriendList);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(recvChunkPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channelPtr != IntPtr.Zero) {
|
|
|
|
|
this._chatChannelChangeHook = new Hook<ChatChannelChangeDelegate>(channelPtr, this.ChangeChatChannelDetour);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(channelPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channelNamePtr != IntPtr.Zero) {
|
|
|
|
|
this._chatChannelChangeNameHook = new Hook<ChatChannelChangeNameDelegate>(channelNamePtr, this.ChangeChatChannelNameDetour);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(channelNamePtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inputPtr != IntPtr.Zero) {
|
|
|
|
|
this._isInputHook = new Hook<IsInputDelegate>(inputPtr, this.IsInputDetour);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(inputPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inputAfkPtr != IntPtr.Zero) {
|
|
|
|
|
this._isInputAfkHook = new Hook<IsInputAfkDelegate>(inputAfkPtr, this.IsInputAfkDetour);
|
|
|
|
|
} else {
|
|
|
|
|
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(inputAfkPtr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SignatureHelper.Initialise(this);
|
|
|
|
|
|
|
|
|
|
this._friendListHook?.Enable();
|
|
|
|
|
this._formatHook?.Enable();
|
|
|
|
@ -271,7 +199,7 @@ namespace XIVChatPlugin {
|
|
|
|
|
//
|
|
|
|
|
// If this function would ever return 0, it returns null instead.
|
|
|
|
|
internal uint? GetChannelColour(ChatCode channel) {
|
|
|
|
|
if (this._getColourInfo == null || this.ColourLookup == IntPtr.Zero || this.ColourHandler == IntPtr.Zero) {
|
|
|
|
|
if (this._getColourInfo == null || this.ColourLookup == IntPtr.Zero) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -289,9 +217,11 @@ namespace XIVChatPlugin {
|
|
|
|
|
return channel.DefaultColour();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var lookupResult = (uint) Marshal.ReadInt32(this.ColourLookup, (int) parent * 4);
|
|
|
|
|
var info = this._getColourInfo(Marshal.ReadIntPtr(this.ColourHandler) + 16, lookupResult);
|
|
|
|
|
var rgb = (uint) Marshal.ReadInt32(info, 32) & 0xFFFFFF;
|
|
|
|
|
var framework = (IntPtr) Framework.Instance();
|
|
|
|
|
|
|
|
|
|
var lookupResult = *(uint*) (this.ColourLookup + (int) parent * 4);
|
|
|
|
|
var info = this._getColourInfo(framework + 16, lookupResult);
|
|
|
|
|
var rgb = *(uint*) (info + 32) & 0xFFFFFF;
|
|
|
|
|
|
|
|
|
|
if (rgb == 0) {
|
|
|
|
|
return null;
|
|
|
|
@ -301,23 +231,19 @@ namespace XIVChatPlugin {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void ProcessChatBox(string message) {
|
|
|
|
|
if (this._easierProcessChatBox == null || this._getUiModule == null || this.UiModulePtr == IntPtr.Zero) {
|
|
|
|
|
if (this._easierProcessChatBox == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.HadInput = InputSetters.Normal | InputSetters.Afk;
|
|
|
|
|
|
|
|
|
|
var uiModule = this._getUiModule(Marshal.ReadIntPtr(this.UiModulePtr));
|
|
|
|
|
|
|
|
|
|
if (uiModule == IntPtr.Zero) {
|
|
|
|
|
throw new ArgumentException("pointer was null", nameof(uiModule));
|
|
|
|
|
}
|
|
|
|
|
var uiModule = Framework.Instance()->GetUiModule();
|
|
|
|
|
|
|
|
|
|
using var payload = new ChatPayload(message);
|
|
|
|
|
var mem1 = Marshal.AllocHGlobal(400);
|
|
|
|
|
Marshal.StructureToPtr(payload, mem1, false);
|
|
|
|
|
|
|
|
|
|
this._easierProcessChatBox(uiModule, mem1, IntPtr.Zero, 0);
|
|
|
|
|
this._easierProcessChatBox((IntPtr) uiModule, mem1, IntPtr.Zero, 0);
|
|
|
|
|
|
|
|
|
|
Marshal.FreeHGlobal(mem1);
|
|
|
|
|
}
|
|
|
|
@ -339,7 +265,7 @@ namespace XIVChatPlugin {
|
|
|
|
|
return this._chatChannelChangeHook!.Original(a1, channel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private unsafe IntPtr ChangeChatChannelNameDetour(IntPtr a1) {
|
|
|
|
|
private IntPtr ChangeChatChannelNameDetour(IntPtr a1) {
|
|
|
|
|
// Last ShB patch
|
|
|
|
|
// +0x40 = chat channel (byte or uint?)
|
|
|
|
|
// channel is 17 (maybe 18?) for tells
|
|
|
|
@ -446,7 +372,7 @@ namespace XIVChatPlugin {
|
|
|
|
|
goto Return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Marshal.ReadByte(data + 0xc) != 2 || Marshal.ReadInt16(data + 0x8) != 0) {
|
|
|
|
|
if (*(byte*) (data + 0xc) != 2 || *(ushort*) (data + 0x8) != 0) {
|
|
|
|
|
goto Return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -490,7 +416,7 @@ namespace XIVChatPlugin {
|
|
|
|
|
private readonly ulong unk2;
|
|
|
|
|
|
|
|
|
|
internal ChatPayload(string text) {
|
|
|
|
|
byte[] stringBytes = Encoding.UTF8.GetBytes(text);
|
|
|
|
|
var stringBytes = Encoding.UTF8.GetBytes(text);
|
|
|
|
|
this.textPtr = Marshal.AllocHGlobal(stringBytes.Length + 30);
|
|
|
|
|
Marshal.Copy(stringBytes, 0, this.textPtr, stringBytes.Length);
|
|
|
|
|
Marshal.WriteByte(this.textPtr + stringBytes.Length, 0);
|
|
|
|
@ -534,7 +460,7 @@ namespace XIVChatPlugin {
|
|
|
|
|
private readonly byte[] fc;
|
|
|
|
|
|
|
|
|
|
private static string? HandleString(IEnumerable<byte> bytes) {
|
|
|
|
|
byte[] nonNull = bytes.TakeWhile(b => b != 0).ToArray();
|
|
|
|
|
var nonNull = bytes.TakeWhile(b => b != 0).ToArray();
|
|
|
|
|
return nonNull.Length == 0 ? null : Encoding.UTF8.GetString(nonNull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|