diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj
index d0365a0..f82b67a 100755
--- a/ChatTwo/ChatTwo.csproj
+++ b/ChatTwo/ChatTwo.csproj
@@ -47,13 +47,14 @@
-
+
+
-
-
-
+
+
+
diff --git a/ChatTwo/GameFunctions.cs b/ChatTwo/GameFunctions.cs
index 7e325fc..ebb3404 100755
--- a/ChatTwo/GameFunctions.cs
+++ b/ChatTwo/GameFunctions.cs
@@ -20,72 +20,62 @@ internal unsafe class GameFunctions : IDisposable {
private static class Signatures {
internal const string ChatLogRefresh = "40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA";
internal const string ChangeChannelName = "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6";
- internal const string ChangeChatChannel = "E8 ?? ?? ?? ?? 0F B7 44 37 ??";
- // Context menu
internal const string CurrentChatEntryOffset = "8B 77 ?? 8D 46 01 89 47 14 81 FE ?? ?? ?? ?? 72 03 FF 47";
- internal const string GetContentIdForChatEntry = "4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17";
-
- internal const string Indexer = "E8 ?? ?? ?? ?? 8B FD 8B CD";
- internal const string InviteToParty = "E8 ?? ?? ?? ?? 33 C0 EB 51";
-
- internal const string FriendRequestBool = "40 53 48 83 EC 20 48 8B D9 48 8B 49 10 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B 48 48";
internal const string AgentContextYesNo = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 84 C0 74 3A";
- internal const string InviteToNoviceNetwork = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9";
-
- internal const string TryOn = "E8 ?? ?? ?? ?? EB 35 BA";
- internal const string LinkItem = "E8 ?? ?? ?? ?? EB 7B 49 8B 06";
- internal const string ItemComparison = "E8 ?? ?? ?? ?? EB 3F 83 F8 FE";
- internal const string SearchForRecipesUsingItem = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 41 B4 01";
}
private delegate byte ChatLogRefreshDelegate(IntPtr log, ushort eventId, AtkValue* value);
private delegate IntPtr ChangeChannelNameDelegate(IntPtr agent);
- private delegate IntPtr ChangeChatChannelDelegate(RaptureShellModule* shell, int channel, uint linkshellIdx, Utf8String* tellTarget, byte one);
-
- private delegate ulong GetContentIdForChatEntryDelegate(RaptureLogModule* log, uint index);
-
- private delegate IntPtr InviteToPartyDelegate(IntPtr a1, ulong contentId, byte* playerName, ushort playerWorld);
-
private delegate IntPtr AgentContextYesNoDelegate(AgentInterface* context, uint a2, byte* playerName, ushort playerWorld, uint a5, byte a6);
- private delegate byte InviteToNoviceNetworkDelegate(IntPtr a1, ulong contentId, ushort playerWorld, byte* playerName);
-
internal delegate void ChatActivatedEventDelegate(string? input);
+
+ #region Functions
- private delegate byte TryOnDelegate(uint unknownCanEquip, uint itemBaseId, ulong stainColor, uint itemGlamourId, byte unknownByte);
+ [Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??")]
+ private readonly delegate* unmanaged _changeChatChannel;
- private delegate IntPtr LinkItemDelegate(AgentInterface* agentChatLog, uint itemId);
+ [Signature("4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17")]
+ private readonly delegate* unmanaged _getContentIdForChatEntry;
- private delegate IntPtr ItemComparisonDelegate(AgentInterface* agentItemCompare, ushort a2, uint itemId, byte a4);
+ [Signature("E8 ?? ?? ?? ?? 8B FD 8B CD")]
+ private readonly delegate* unmanaged _indexer;
- private delegate IntPtr SearchForRecipesUsingItemDelegate(IntPtr a1, uint itemId);
+ [Signature("E8 ?? ?? ?? ?? 33 C0 EB 51")]
+ private readonly delegate* unmanaged _inviteToParty;
+ [Signature(("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9"))]
+ private readonly delegate* unmanaged _inviteToNoviceNetwork;
+
+ [Signature("40 53 48 83 EC 20 48 8B D9 48 8B 49 10 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B 48 48")]
+ private readonly delegate* unmanaged _friendRequestBool;
+
+ [Signature("E8 ?? ?? ?? ?? EB 35 BA")]
+ private readonly delegate* unmanaged _tryOn;
+
+ [Signature("E8 ?? ?? ?? ?? EB 7B 49 8B 06")]
+ private readonly delegate* unmanaged _linkItem;
+
+ [Signature("E8 ?? ?? ?? ?? EB 3F 83 F8 FE")]
+ private readonly delegate* unmanaged _itemComparison;
+
+ [Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 41 B4 01")]
+ private readonly delegate* unmanaged _searchForRecipesUsingItem;
+
+ #endregion
+
internal const int HqItemOffset = 1_000_000;
private Plugin Plugin { get; }
private Hook? ChatLogRefreshHook { get; }
private Hook? ChangeChannelNameHook { get; }
- private readonly ChangeChatChannelDelegate? _changeChatChannel;
- private readonly GetContentIdForChatEntryDelegate? _getContentIdForChatEntry;
private readonly int? _currentChatEntryOffset;
-
- private readonly delegate* unmanaged _indexer;
- private readonly InviteToPartyDelegate? _inviteToParty;
-
- private readonly delegate* unmanaged _friendRequestBool;
private readonly AgentContextYesNoDelegate? _agentContextYesNo;
- private readonly InviteToNoviceNetworkDelegate? _inviteToNoviceNetwork;
-
- private readonly TryOnDelegate? _tryOn;
- private readonly LinkItemDelegate? _linkItem;
- private readonly ItemComparisonDelegate? _itemComparison;
- private readonly SearchForRecipesUsingItemDelegate? _searchForRecipesUsingItem;
-
internal event ChatActivatedEventDelegate? ChatActivated;
internal (InputChannel channel, List name) ChatChannel { get; private set; }
@@ -93,6 +83,8 @@ internal unsafe class GameFunctions : IDisposable {
internal GameFunctions(Plugin plugin) {
this.Plugin = plugin;
+ this.Plugin.SigScanner.ScanFunctions(this);
+
if (this.Plugin.SigScanner.TryScanText(Signatures.ChatLogRefresh, out var chatLogPtr)) {
this.ChatLogRefreshHook = new Hook(chatLogPtr, this.ChatLogRefreshDetour);
this.ChatLogRefreshHook.Enable();
@@ -103,54 +95,14 @@ internal unsafe class GameFunctions : IDisposable {
this.ChangeChannelNameHook.Enable();
}
- if (this.Plugin.SigScanner.TryScanText(Signatures.ChangeChatChannel, out var changeChannelPtr)) {
- this._changeChatChannel = Marshal.GetDelegateForFunctionPointer(changeChannelPtr);
- }
-
if (this.Plugin.SigScanner.TryScanText(Signatures.CurrentChatEntryOffset, out var entryOffsetPtr)) {
this._currentChatEntryOffset = *(byte*) (entryOffsetPtr + 2);
}
- if (this.Plugin.SigScanner.TryScanText(Signatures.Indexer, out var indexerPtr)) {
- this._indexer = (delegate* unmanaged) indexerPtr;
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.GetContentIdForChatEntry, out var getContentIdPtr)) {
- this._getContentIdForChatEntry = Marshal.GetDelegateForFunctionPointer(getContentIdPtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.InviteToParty, out var invitePtr)) {
- this._inviteToParty = Marshal.GetDelegateForFunctionPointer(invitePtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.FriendRequestBool, out var frBoolPtr)) {
- this._friendRequestBool = (delegate* unmanaged) frBoolPtr;
- }
-
if (this.Plugin.SigScanner.TryScanText(Signatures.AgentContextYesNo, out var sendFriendRequestPtr)) {
this._agentContextYesNo = Marshal.GetDelegateForFunctionPointer(sendFriendRequestPtr);
}
- if (this.Plugin.SigScanner.TryScanText(Signatures.InviteToNoviceNetwork, out var nnPtr)) {
- this._inviteToNoviceNetwork = Marshal.GetDelegateForFunctionPointer(nnPtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.TryOn, out var tryOnPtr)) {
- this._tryOn = Marshal.GetDelegateForFunctionPointer(tryOnPtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.LinkItem, out var linkPtr)) {
- this._linkItem = Marshal.GetDelegateForFunctionPointer(linkPtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.ItemComparison, out var comparisonPtr)) {
- this._itemComparison = Marshal.GetDelegateForFunctionPointer(comparisonPtr);
- }
-
- if (this.Plugin.SigScanner.TryScanText(Signatures.SearchForRecipesUsingItem, out var searchForRecipesItemPtr)) {
- this._searchForRecipesUsingItem = Marshal.GetDelegateForFunctionPointer(searchForRecipesItemPtr);
- }
-
this.Plugin.ClientState.Login += this.Login;
this.Login(null, null);
}
@@ -185,7 +137,11 @@ internal unsafe class GameFunctions : IDisposable {
}
internal ulong? GetContentIdForChatLogEntry(uint index) {
- return this._getContentIdForChatEntry?.Invoke(Framework.Instance()->GetUiModule()->GetRaptureLogModule(), index);
+ if (this._getContentIdForChatEntry == null) {
+ return null;
+ }
+
+ return this._getContentIdForChatEntry(Framework.Instance()->GetUiModule()->GetRaptureLogModule(), index);
}
internal void InviteToParty(string name, ushort world) {
@@ -414,7 +370,7 @@ internal unsafe class GameFunctions : IDisposable {
return ret;
}
-
+
// These context menu things come from AgentChatLog.vf0 at the bottom
// 0x10000: item comparison
// 0x10001: try on
@@ -425,12 +381,20 @@ internal unsafe class GameFunctions : IDisposable {
internal void TryOn(uint itemId, byte stainId) {
- this._tryOn?.Invoke(0xFF, itemId, stainId, 0, 0);
+ if (this._tryOn == null) {
+ return;
+ }
+
+ this._tryOn(0xFF, itemId, stainId, 0, 0);
}
internal void LinkItem(uint itemId) {
+ if (this._linkItem == null) {
+ return;
+ }
+
var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog);
- this._linkItem?.Invoke(agent, itemId);
+ this._linkItem(agent, itemId);
}
internal void OpenItemComparison(uint itemId) {
@@ -446,7 +410,7 @@ internal unsafe class GameFunctions : IDisposable {
if (this._searchForRecipesUsingItem == null) {
return;
}
-
+
var uiModule = Framework.Instance()->GetUiModule();
var vf35 = (delegate* unmanaged) uiModule->vfunc[35];
var a1 = vf35(uiModule);
diff --git a/ChatTwo/Util/SigFinder.cs b/ChatTwo/Util/SigFinder.cs
new file mode 100755
index 0000000..a24f8b1
--- /dev/null
+++ b/ChatTwo/Util/SigFinder.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using Dalamud.Game;
+using Dalamud.Logging;
+using JetBrains.Annotations;
+
+namespace ChatTwo.Util;
+
+internal static class SigFinder {
+ internal static void ScanFunctions(this SigScanner scanner, object self) {
+ var selfType = self.GetType();
+ var funcs = selfType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+ .Select(field => (field, field.GetCustomAttribute()))
+ .Where(tuple => tuple.Item2 != null);
+ foreach (var (field, attr) in funcs) {
+ if (!scanner.TryScanText(attr!.Signature, out var ptr)) {
+ PluginLog.LogWarning($"Could not find signature for {selfType.Name}.{field.Name}: {attr.Signature}");
+ continue;
+ }
+
+ field.SetValue(self, ptr);
+ }
+ }
+}
+
+[AttributeUsage(AttributeTargets.Field)]
+[MeansImplicitUse(ImplicitUseKindFlags.Assign, ImplicitUseTargetFlags.Itself)]
+internal class SignatureAttribute : Attribute {
+ internal readonly string Signature;
+
+ internal SignatureAttribute(string signature) {
+ this.Signature = signature;
+ }
+}