diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 0a77aef..4e59ad8 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,6 +1,6 @@ - 1.25.5 + 1.26.0 net8.0-windows enable enable diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index a57e78b..43aaca7 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -28,102 +28,36 @@ namespace ChatTwo.GameFunctions; internal sealed unsafe class Chat : IDisposable { // Functions - - // TODO Replace with CS in RaptureShellModule - [Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged ChangeChatChannel = null!; - - // TODO Replace with CS in RaptureShellModule - [Signature("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B 02", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged SetChannelTargetTell = null!; - [Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")] private readonly delegate* unmanaged GetKeybindNative = null!; - // TODO Replace with CS in AcquaintanceModule - [Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1 45 85 C9")] - private readonly delegate* unmanaged GetTellHistory = null!; - [Signature("E8 ?? ?? ?? ?? 48 8D 4D 50 E8 ?? ?? ?? ?? 48 8B 17")] private readonly delegate* unmanaged PrintTellNative = null!; [Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")] private readonly delegate* unmanaged SendTellNative = null!; - // TODO Replace with CS in InfoProxyCrossworldLinkshell - [Signature("E8 ?? ?? ?? ?? 48 8B C8 E8 ?? ?? ?? ?? 45 8D 46 FB")] - private readonly delegate* unmanaged GetCrossLinkshellNameNative = null!; - - // TODO Replace with CS in InfoProxyLinkshell - [Signature("3B 51 10 73 0F 8B C2 48 83 C0 0B")] - private readonly delegate* unmanaged GetLinkshellInfo = null!; - - // TODO Replace with CS in InfoProxyLinkshell - [Signature("E8 ?? ?? ?? ?? 4C 8B C8 44 8D 47 01")] - private readonly delegate* unmanaged GetLinkshellNameNative = null!; - - // TODO Replace with CS virtual function - [Signature("40 56 41 54 41 55 41 57 48 83 EC 28 48 8B 01", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged RotateLinkshellHistoryNative; - - // TODO Replace with CS virtual function - [Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 20 48 8B 01 44 8B F2", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged RotateCrossLinkshellHistoryNative; - - // TODO Replace with CS version on Utf8String + // TODO Replace with CS version after https://github.com/aers/FFXIVClientStructs/pull/911 [Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")] private readonly delegate* unmanaged SanitiseString = null!; - // Hooks - - private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value); - - private delegate nint ChangeChannelNameDelegate(nint agent); - - private delegate void ReplyInSelectedChatModeDelegate(AgentInterface* agent); - - private delegate byte SetChatLogTellTarget(nint a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7); - - private delegate void EurekaContextMenuTellDelegate(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong contentId, ushort param6); - - // Client::UI::AddonChatLog.OnRefresh [Signature("40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA", DetourName = nameof(ChatLogRefreshDetour))] private Hook? ChatLogRefreshHook { get; init; } + private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value); - // TODO Replace with CS version - [Signature("E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6", DetourName = nameof(ChangeChannelNameDetour))] + // TODO Replace all with delegate hooks in API X private Hook? ChangeChannelNameHook { get; init; } + private delegate nint ChangeChannelNameDelegate(nint agent); - // TODO Replace with CS version - [Signature("48 89 5C 24 ?? 57 48 83 EC 30 8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE", DetourName = nameof(ReplyInSelectedChatModeDetour))] private Hook? ReplyInSelectedChatModeHook { get; init; } + private delegate void ReplyInSelectedChatModeDelegate(AgentInterface* agent); - // TODO Replace with CS version - [Signature("E8 ?? ?? ?? ?? 4C 8B 7C 24 ?? EB 34", DetourName = nameof(SetChatLogTellTargetDetour))] private Hook? SetChatLogTellTargetHook { get; init; } + private delegate byte SetChatLogTellTarget(nint a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort a6, byte a7); - // TODO Replace with CS version - [Signature("E8 ?? ?? ?? ?? EB 8A 48 8B 1D", DetourName = nameof(EurekaContextMenuTell))] private Hook? EurekaContextMenuTellHook { get; init; } - - // Offsets - - #pragma warning disable 0649 - - // TODO: Replace with CS version under AgentChatLog - [Signature("8B B9 ?? ?? ?? ?? 48 8B D9 83 FF FE 0F 84", Offset = 2)] - private readonly int? ReplyChannelOffset; - - // TODO: Replace with CS version under RaptureShellModule - [Signature("89 83 ?? ?? ?? ?? 48 8B 01 83 FE 13 7C 05 41 8B D4 EB 03 83 CA FF FF 90", Offset = 2)] - private readonly int? ShellChannelOffset; - - // TODO: Replace with CS version under UiModule - [Signature("4C 8D B6 ?? ?? ?? ?? 41 8B 1E 45 85 E4 74 7A 33 FF 8B EF 66 0F 1F 44 00", Offset = 3)] - private readonly int? LinkshellCycleOffset; - - #pragma warning restore 0649 + private delegate void EurekaContextMenuTellDelegate(RaptureShellModule* param1, Utf8String* playerName, Utf8String* worldName, ushort world, ulong contentId, ushort param6); // Pointers @@ -132,11 +66,11 @@ internal sealed unsafe class Chat : IDisposable // Events + internal event ChatActivatedEventDelegate? Activated; internal delegate void ChatActivatedEventDelegate(ChatActivatedArgs args); - internal event ChatActivatedEventDelegate? Activated; - private Plugin Plugin { get; } + /// /// Holds the current game channel details. /// `TellPlayerName` and `TellWorldId` are only set when the channel is `InputChannel.Tell`. @@ -155,10 +89,18 @@ internal sealed unsafe class Chat : IDisposable Plugin.GameInteropProvider.InitializeFromAttributes(this); ChatLogRefreshHook?.Enable(); - ChangeChannelNameHook?.Enable(); - ReplyInSelectedChatModeHook?.Enable(); - SetChatLogTellTargetHook?.Enable(); - EurekaContextMenuTellHook?.Enable(); + + ChangeChannelNameHook = Plugin.GameInteropProvider.HookFromAddress(AgentChatLog.Addresses.ChangeChannelName.Value, ChangeChannelNameDetour); + ChangeChannelNameHook.Enable(); + + ReplyInSelectedChatModeHook = Plugin.GameInteropProvider.HookFromAddress(RaptureShellModule.Addresses.ReplyInSelectedChatMode.Value, ReplyInSelectedChatModeDetour); + ReplyInSelectedChatModeHook.Enable(); + + SetChatLogTellTargetHook = Plugin.GameInteropProvider.HookFromAddress(RaptureShellModule.Addresses.SetContextTellTarget.Value, SetChatLogTellTargetDetour); + SetChatLogTellTargetHook.Enable(); + + EurekaContextMenuTellHook = Plugin.GameInteropProvider.HookFromAddress(RaptureShellModule.Addresses.SetContextTellTargetInForay.Value, EurekaContextMenuTell); + EurekaContextMenuTellHook.Enable(); Plugin.Framework.Update += InterceptKeybinds; Plugin.ClientState.Login += Login; @@ -181,48 +123,35 @@ internal sealed unsafe class Chat : IDisposable internal string? GetLinkshellName(uint idx) { - var infoProxy = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.LinkShell); - if (infoProxy == nint.Zero) - return null; - - var lsInfo = GetLinkshellInfo(infoProxy, idx); + var instance = InfoProxyLinkShell.Instance(); + var lsInfo = instance->GetLinkshellInfo(idx); if (lsInfo == null) return null; - var utf = GetLinkshellNameNative(infoProxy, *lsInfo); + var utf = instance->GetLinkshellName(lsInfo); return utf == null ? null : MemoryHelper.ReadStringNullTerminated((nint) utf); } internal string? GetCrossLinkshellName(uint idx) { - var infoProxy = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.CrossWorldLinkShell); - if (infoProxy == nint.Zero) - return null; - - var utf = GetCrossLinkshellNameNative(infoProxy, idx); + var utf = InfoProxyCrossWorldLinkShell.Instance()->GetCrossworldLinkshellName(idx); return utf == null ? null : utf->ToString(); } - internal ulong RotateLinkshellHistory(RotateMode mode) + internal static int RotateLinkshellHistory(RotateMode mode) { - if (mode == RotateMode.None && LinkshellCycleOffset != null) - { - // for the branch at 6.08: 5E1680 - var uiModule = (nint) Framework.Instance()->GetUiModule(); - *(int*) (uiModule + LinkshellCycleOffset.Value) = -1; - } + var uiModule = UIModule.Instance(); + if (mode == RotateMode.None) + uiModule->LinkshellCycle = -1; - return RotateLinkshellHistoryInternal(RotateLinkshellHistoryNative, mode); + return RotateLinkshellHistoryInternal(uiModule->RotateLinkshellHistory, mode); } - internal ulong RotateCrossLinkshellHistory(RotateMode mode) => RotateLinkshellHistoryInternal(RotateCrossLinkshellHistoryNative, mode); + internal static int RotateCrossLinkshellHistory(RotateMode mode) => + RotateLinkshellHistoryInternal(UIModule.Instance()->RotateCrossLinkshellHistory, mode); - private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged func, RotateMode mode) + private static int RotateLinkshellHistoryInternal(Func func, RotateMode mode) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (func == null) - return 0; - var idx = mode switch { RotateMode.Forward => 1, @@ -230,8 +159,7 @@ internal sealed unsafe class Chat : IDisposable _ => 0, }; - var uiModule = Framework.Instance()->GetUiModule(); - return func(uiModule, idx); + return func(idx); } // This function looks up a channel's user-defined color. @@ -502,17 +430,7 @@ internal sealed unsafe class Chat : IDisposable if (agent == nint.Zero) return ret; - // E8 ?? ?? ?? ?? 8D 48 F7 - // RaptureShellModule + 0xFD0 - var shellModule = (nint) Framework.Instance()->GetUiModule()->GetRaptureShellModule(); - if (shellModule == nint.Zero) - return ret; - - var channel = (uint) InputChannel.Say; - if (ShellChannelOffset != null) - channel = *(uint*) (shellModule + ShellChannelOffset.Value); - - // var channel = *(uint*) (agent + 0x40); + var channel = (uint) RaptureShellModule.Instance()->ChatType; if (channel is 17 or 18) channel = (uint) InputChannel.Tell; @@ -549,16 +467,14 @@ internal sealed unsafe class Chat : IDisposable private void ReplyInSelectedChatModeDetour(AgentInterface* agent) { - if (ReplyChannelOffset == null) - goto Original; - - var replyMode = *(int*) ((nint) agent + ReplyChannelOffset.Value); + var replyMode = AgentChatLog.Instance()->ReplyChannel; if (replyMode == -2) - goto Original; + { + ReplyInSelectedChatModeHook!.Original(agent); + return; + } SetChannel((InputChannel) replyMode); - - Original: ReplyInSelectedChatModeHook!.Original(agent); } @@ -592,25 +508,18 @@ internal sealed unsafe class Chat : IDisposable PreviousChannel = Channel.Channel; } - if (SetChannelTargetTell != null) - SetChannelTargetTell(param1, playerName, worldName, world, id, param6, 0); - + RaptureShellModule.Instance()->SetTellTargetInForay(playerName, worldName, world, id, param6, false); EurekaContextMenuTellHook!.Original(param1, playerName, worldName, world, id, param6); } - internal ulong GetContentIdForEntry(int index) - { - return Framework.Instance()->GetUiModule()->GetRaptureLogModule()->GetContentIdForLogMessage(index); - } - - internal void SetChannel(InputChannel channel, string? tellTarget = null) + internal static void SetChannel(InputChannel channel, string? tellTarget = null) { // ExtraChat linkshells aren't supported in game so we never want to // call the ChangeChatChannel function with them. // // Callers should call ChatLogWindow.SetChannel() which handles // ExtraChat channels - if (ChangeChatChannel == null || channel.IsExtraChatLinkshell()) + if (channel.IsExtraChatLinkshell()) return; var target = Utf8String.FromString(tellTarget ?? ""); @@ -618,18 +527,15 @@ internal sealed unsafe class Chat : IDisposable if (idx == uint.MaxValue) idx = 0; - ChangeChatChannel(RaptureShellModule.Instance(), (int) channel, idx, target, 1); + RaptureShellModule.Instance()->ChangeChatChannel((int) channel, idx, target, true); target->Dtor(true); } - internal void SetEurekaTellChannel(string name, string worldName, ushort worldId, ulong objectId, ushort param6, byte param7) + internal void SetEurekaTellChannel(string name, string worldName, ushort worldId, ulong objectId, ushort param6, bool param7) { // param6 is 0 for contentId and 1 for objectId // param7 is always 0 ? - if (SetChannelTargetTell == null) - return; - if (!UsesTellTempChannel) { UsesTellTempChannel = true; @@ -639,7 +545,7 @@ internal sealed unsafe class Chat : IDisposable var utfName = Utf8String.FromString(name); var utfWorld = Utf8String.FromString(worldName); - SetChannelTargetTell(RaptureShellModule.Instance(), utfName, utfWorld, worldId, objectId, param6, param7); + RaptureShellModule.Instance()->SetTellTargetInForay(utfName, utfWorld, worldId, objectId, param6, param7); utfName->Dtor(true); utfWorld->Dtor(true); @@ -687,12 +593,10 @@ internal sealed unsafe class Chat : IDisposable internal TellHistoryInfo? GetTellHistoryInfo(int index) { - var acquaintanceModule = Framework.Instance()->GetUiModule()->GetAcquaintanceModule(); - if (acquaintanceModule == null) - return null; + var ptr = AcquaintanceModule.Instance()->GetTellHistory(index); - var ptr = GetTellHistory(acquaintanceModule, index); - if (ptr == nint.Zero) + // TODO does this check work? + if (ptr->ContentId == 0) return null; var name = MemoryHelper.ReadStringNullTerminated(*(nint*) ptr); diff --git a/ChatTwo/GameFunctions/Context.cs b/ChatTwo/GameFunctions/Context.cs index 881ea8b..01a7dbb 100755 --- a/ChatTwo/GameFunctions/Context.cs +++ b/ChatTwo/GameFunctions/Context.cs @@ -1,83 +1,41 @@ using ChatTwo.Util; -using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Info; -using FFXIVClientStructs.FFXIV.Component.GUI; namespace ChatTwo.GameFunctions; internal sealed unsafe class Context { - // TODO: Replace with CS version after https://github.com/aers/FFXIVClientStructs/pull/873 got merged - [Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged InviteToNoviceNetworkNative = null!; - - [Signature("E8 ?? ?? ?? ?? EB 7B 49 8B 06", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged LinkItemNative = null!; - - [Signature("E8 ?? ?? ?? ?? EB 3F 83 F8 FE", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged ItemComparisonNative = null!; - - private Plugin Plugin { get; } - - internal Context(Plugin plugin) + internal static void InviteToNoviceNetwork(string name, ushort world) { - Plugin = plugin; - Plugin.GameInteropProvider.InitializeFromAttributes(this); - } - - internal void InviteToNoviceNetwork(string name, ushort world) - { - if (InviteToNoviceNetworkNative == null) - return; - - // 6.3: 221EFD - var a1 = Plugin.Functions.GetInfoProxyByIndex((InfoProxyId) 0x14); - // can specify content id if we have it, but there's no need fixed (byte* namePtr = name.ToTerminatedBytes()) { - InviteToNoviceNetworkNative(a1, 0, world, namePtr); + InfoProxyNoviceNetwork.Instance()->InviteToNoviceNetwork(0, world, namePtr); } } - // These context menu things come from AgentChatLog.vf0 at the bottom - // 0x10000: item comparison - // 0x10001: try on - // 0x10002: search for item - // 0x10003: link - // 0x10005: copy item name - // 0x10006: search recipes using this material - - internal void TryOn(uint itemId, byte stainId) + internal static void TryOn(uint itemId, byte stainId) { AgentTryon.TryOn(0xFF, itemId, stainId, 0, 0); } - internal void LinkItem(uint itemId) + internal static void LinkItem(uint itemId) { - if (LinkItemNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog); - LinkItemNative(agent, itemId); + AgentChatLog.Instance()->LinkItem(itemId); } - internal void OpenItemComparison(uint itemId) + internal static void OpenItemComparison(uint itemId) { - if (ItemComparisonNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ItemCompare); - ItemComparisonNative(agent, 0x4D, itemId, 0); + AgentItemComp.Instance()->CompareItem(0x4D, itemId, 0); } - internal void SearchForRecipesUsingItem(uint itemId) + internal static void SearchForRecipesUsingItem(uint itemId) { AgentRecipeProductList.Instance()->SearchForRecipesUsingItem(itemId); } - internal void SearchForItem(uint itemId) + internal static void SearchForItem(uint itemId) { Framework.Instance()->GetUiModule()->GetItemFinderModule()->SearchForItem(itemId, true); } diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index d9e3c33..86000e8 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -17,16 +17,6 @@ namespace ChatTwo.GameFunctions; internal unsafe class GameFunctions : IDisposable { - #region Functions - // TODO: Can be replaced with CS version soon - [Signature("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged OpenPartyFinderNative = null!; - - // TODO: Can be replaced with CS version soon - [Signature("E8 ?? ?? ?? ?? EB 20 48 8B 46 28", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged OpenAchievementNative = null!; - #endregion - #region Hooks private delegate nint ResolveTextCommandPlaceholderDelegate(nint a1, byte* placeholderText, byte a3, byte a4); @@ -35,16 +25,12 @@ internal unsafe class GameFunctions : IDisposable #endregion private Plugin Plugin { get; } - internal Party Party { get; } internal Chat Chat { get; } - internal Context Context { get; } internal GameFunctions(Plugin plugin) { Plugin = plugin; - Party = new Party(Plugin); Chat = new Chat(Plugin); - Context = new Context(Plugin); Plugin.GameInteropProvider.InitializeFromAttributes(this); @@ -207,37 +193,27 @@ internal unsafe class GameFunctions : IDisposable } } - internal bool IsMentor() + internal static bool IsMentor() { return PlayerState.Instance()->IsMentor(); } - internal void OpenPartyFinder(uint id) + internal static void OpenPartyFinder(uint id) { - if (OpenPartyFinderNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.LookingForGroup); - if (agent != null) - OpenPartyFinderNative(agent, id); + AgentLookingForGroup.Instance()->OpenListing(id); } - internal void OpenAchievement(uint id) + internal static void OpenAchievement(uint id) { - if (OpenAchievementNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Achievement); - if (agent != null) - OpenAchievementNative(agent, id); + AgentAchievement.Instance()->OpenById(id); } - internal bool IsInInstance() + internal static bool IsInInstance() { return Plugin.Condition[ConditionFlag.BoundByDuty56]; } - internal bool TryOpenAdventurerPlate(ulong playerId) + internal static bool TryOpenAdventurerPlate(ulong playerId) { try { @@ -251,7 +227,7 @@ internal unsafe class GameFunctions : IDisposable } } - internal void ClickNoviceNetworkButton() + internal static void ClickNoviceNetworkButton() { var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog); // case 3 diff --git a/ChatTwo/GameFunctions/Party.cs b/ChatTwo/GameFunctions/Party.cs index 2d80016..cc56211 100755 --- a/ChatTwo/GameFunctions/Party.cs +++ b/ChatTwo/GameFunctions/Party.cs @@ -1,109 +1,46 @@ using ChatTwo.Util; -using Dalamud.Utility.Signatures; -using FFXIVClientStructs.FFXIV.Client.System.Framework; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Info; -using FFXIVClientStructs.FFXIV.Component.GUI; namespace ChatTwo.GameFunctions; internal sealed unsafe class Party { - // TODO: Replace all hooks with CS after https://github.com/aers/FFXIVClientStructs/pull/872 got merged - [Signature("E8 ?? ?? ?? ?? 33 C0 EB 51", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged InviteToPartyNative = null!; - - [Signature("48 83 EC 38 41 B1 09", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged InviteToPartyContentIdNative = null!; - - [Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B 83 ?? ?? ?? ?? 48 85 C0 74 62", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged InviteToPartyInInstanceNative = null!; - - [Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 49 8B 56 20", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged PromoteNative = null!; - - [Signature("E8 ?? ?? ?? ?? EB 66 49 8B 4E 20", Fallibility = Fallibility.Fallible)] - private readonly delegate* unmanaged KickNative = null!; - - private Plugin Plugin { get; } - - internal Party(Plugin plugin) + internal static void InviteSameWorld(string name, ushort world, ulong contentId) { - Plugin = plugin; - Plugin.GameInteropProvider.InitializeFromAttributes(this); - } - - internal void InviteSameWorld(string name, ushort world, ulong contentId) - { - if (InviteToPartyNative == null) - return; - - // 6.11: 214A55 - var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite); - // this only works if target is on the same world fixed (byte* namePtr = name.ToTerminatedBytes()) { - InviteToPartyNative(a1, contentId, namePtr, world); + InfoProxyPartyInvite.Instance()->InviteToParty(contentId, namePtr, world); } } - internal void InviteOtherWorld(ulong contentId) + internal static void InviteOtherWorld(ulong contentId) { - if (InviteToPartyContentIdNative == null) - return; - - // 6.11: 214A55 - var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite); - // third param is world, but it requires a specific world // if they're not on that world, it will fail // pass 0 and it will work on any world EXCEPT for the world the // current player is on if (contentId != 0) - InviteToPartyContentIdNative(a1, contentId, 0); + InfoProxyPartyInvite.Instance()->InviteToPartyContentId(contentId, 0); } - internal void InviteInInstance(ulong contentId) + internal static void InviteInInstance(ulong contentId) { - if (InviteToPartyInInstanceNative == null) - return; - - // 6.11: 214A55 - var a1 = Plugin.Functions.GetInfoProxyByIndex(InfoProxyId.PartyInvite); - - // third param is world, but it requires a specific world - // if they're not on that world, it will fail - // pass 0 and it will work on any world EXCEPT for the world the - // current player is on if (contentId != 0) - InviteToPartyInInstanceNative(a1, contentId); + InfoProxyPartyInvite.Instance()->InviteToPartyInInstance(contentId); } - internal void Kick(string name, ulong contentId) + internal static void Kick(string name, ulong contentId) { - if (KickNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.SocialPartyMember); - if (agent == null) - return; - fixed (byte* namePtr = name.ToTerminatedBytes()) { - KickNative(agent, namePtr, 0, contentId); + AgentPartyMember.Instance()->Kick(namePtr, 0, contentId); } } - internal void Promote(string name, ulong contentId) + internal static void Promote(string name, ulong contentId) { - if (PromoteNative == null) - return; - - var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.SocialPartyMember); - if (agent == null) - return; - fixed (byte* namePtr = name.ToTerminatedBytes()) { - PromoteNative(agent, namePtr, 0, contentId); + AgentPartyMember.Instance()->Promote(namePtr, 0, contentId); } } } diff --git a/ChatTwo/MessageManager.cs b/ChatTwo/MessageManager.cs index 068bbed..55a8452 100644 --- a/ChatTwo/MessageManager.cs +++ b/ChatTwo/MessageManager.cs @@ -8,7 +8,6 @@ using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Interface.Internal.Notifications; using Dalamud.Plugin.Services; -using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.UI.Misc; using Lumina.Excel.GeneratedSheets; @@ -36,11 +35,9 @@ internal class MessageManager : IAsyncDisposable private readonly Thread PendingMessageThread; private readonly CancellationTokenSource PendingThreadCancellationToken = new(); - // TODO: replace with CS version - private unsafe delegate void ContentIdResolverDelegate(RaptureLogModule* agent, ulong contentId, int messageIndex, ushort worldId, ushort chatType); - - [Signature("4C 8B D1 48 8B 89 ?? ?? ?? ?? 48 85 C9", DetourName = nameof(ContentIdResolver))] + // TODO Replace with delegate in API X private Hook? ContentIdResolverHook { get; init; } + private unsafe delegate void ContentIdResolverDelegate(RaptureLogModule* agent, ulong contentId, int messageIndex, ushort worldId, ushort chatType); internal ulong CurrentContentId { @@ -51,17 +48,18 @@ internal class MessageManager : IAsyncDisposable } } - internal MessageManager(Plugin plugin) + internal unsafe MessageManager(Plugin plugin) { Plugin = plugin; - Plugin.GameInteropProvider.InitializeFromAttributes(this); Store = new MessageStore(DatabasePath()); PendingMessageThread = new Thread(() => ProcessPendingMessages(PendingThreadCancellationToken.Token)); PendingMessageThread.Start(); - ContentIdResolverHook?.Enable(); + ContentIdResolverHook = Plugin.GameInteropProvider.HookFromAddress(RaptureLogModule.Addresses.AddMsgSourceEntry.Value, ContentIdResolver); + ContentIdResolverHook.Enable(); + Plugin.ChatGui.ChatMessageUnhandled += ChatMessage; Plugin.Framework.Update += OnFrameworkUpdate; Plugin.ClientState.Logout += Logout; diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index 70224ff..73ff9d0 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -388,13 +388,13 @@ public sealed class PayloadHandler { if (pf.LinkType == DalamudPartyFinderPayload.PartyFinderLinkType.PartyFinderNotification) GameFunctions.GameFunctions.OpenPartyFinder(); else - LogWindow.Plugin.Functions.OpenPartyFinder(pf.ListingId); + GameFunctions.GameFunctions.OpenPartyFinder(pf.ListingId); break; case ChatTwoPartyFinderPayload pf: - LogWindow.Plugin.Functions.OpenPartyFinder(pf.Id); + GameFunctions.GameFunctions.OpenPartyFinder(pf.Id); break; case AchievementPayload achievement: - LogWindow.Plugin.Functions.OpenAchievement(achievement.Id); + GameFunctions.GameFunctions.OpenAchievement(achievement.Id); break; case RawPayload raw: if (Equals(raw, ChunkUtil.PeriodicRecruitmentLink)) @@ -473,21 +473,21 @@ public sealed class PayloadHandler { if (item.EquipSlotCategory.Row != 0) { if (ImGui.Selectable(Language.Context_TryOn)) - LogWindow.Plugin.Functions.Context.TryOn(realItemId, 0); + GameFunctions.Context.TryOn(realItemId, 0); if (ImGui.Selectable(Language.Context_ItemComparison)) - LogWindow.Plugin.Functions.Context.OpenItemComparison(realItemId); + GameFunctions.Context.OpenItemComparison(realItemId); } if (item.ItemSearchCategory.Value?.Category == 3) if (ImGui.Selectable(Language.Context_SearchRecipes)) - LogWindow.Plugin.Functions.Context.SearchForRecipesUsingItem(payload.ItemId); + GameFunctions.Context.SearchForRecipesUsingItem(payload.ItemId); if (ImGui.Selectable(Language.Context_SearchForItem)) - LogWindow.Plugin.Functions.Context.SearchForItem(realItemId); + GameFunctions.Context.SearchForItem(realItemId); if (ImGui.Selectable(Language.Context_Link)) - LogWindow.Plugin.Functions.Context.LinkItem(realItemId); + GameFunctions.Context.LinkItem(realItemId); if (ImGui.Selectable(Language.Context_CopyItemName)) ImGui.SetClipboardText(name.TextValue); @@ -511,7 +511,7 @@ public sealed class PayloadHandler { var realItemId = payload.RawItemId; if (ImGui.Selectable(Language.Context_Link)) - LogWindow.Plugin.Functions.Context.LinkItem(realItemId); + GameFunctions.Context.LinkItem(realItemId); if (ImGui.Selectable(Language.Context_CopyItemName)) ImGui.SetClipboardText(name.TextValue); @@ -555,7 +555,7 @@ public sealed class PayloadHandler { } else if (validContentId) { - LogWindow.Plugin.Functions.Chat.SetEurekaTellChannel(player.PlayerName, world.Name.ToString(), (ushort) world.RowId, chunk.Message!.ContentId, 0, 0); + LogWindow.Plugin.Functions.Chat.SetEurekaTellChannel(player.PlayerName, world.Name.ToString(), (ushort) world.RowId, chunk.Message!.ContentId, 0, false); } LogWindow.Activate = true; @@ -568,7 +568,7 @@ public sealed class PayloadHandler { var isLeader = party.Length == 0 || Plugin.ClientState.LocalContentId == leader; var member = party.FirstOrDefault(member => member.Name.TextValue == player.PlayerName && member.World.Id == world.RowId); var isInParty = member != default; - var inInstance = LogWindow.Plugin.Functions.IsInInstance(); + var inInstance = GameFunctions.GameFunctions.IsInInstance(); var inPartyInstance = TerritorySheet.GetRow(Plugin.ClientState.TerritoryType)?.TerritoryIntendedUse is (41 or 47 or 48 or 52 or 53); if (isLeader) { @@ -577,15 +577,15 @@ public sealed class PayloadHandler { if (inInstance && inPartyInstance) { if (validContentId && ImGui.Selectable(Language.Context_InviteToParty)) - LogWindow.Plugin.Functions.Party.InviteInInstance(chunk.Message!.ContentId); + GameFunctions.Party.InviteInInstance(chunk.Message!.ContentId); } else if (!inInstance && ImGui.BeginMenu(Language.Context_InviteToParty)) { if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld)) - LogWindow.Plugin.Functions.Party.InviteSameWorld(player.PlayerName, (ushort) world.RowId, chunk.Message?.ContentId ?? 0); + GameFunctions.Party.InviteSameWorld(player.PlayerName, (ushort) world.RowId, chunk.Message?.ContentId ?? 0); if (validContentId && ImGui.Selectable(Language.Context_InviteToParty_DifferentWorld)) - LogWindow.Plugin.Functions.Party.InviteOtherWorld(chunk.Message!.ContentId); + GameFunctions.Party.InviteOtherWorld(chunk.Message!.ContentId); ImGui.EndMenu(); } @@ -594,10 +594,10 @@ public sealed class PayloadHandler { if (isInParty && member != null && (!inInstance || (inInstance && inPartyInstance))) { if (ImGui.Selectable(Language.Context_Promote)) - LogWindow.Plugin.Functions.Party.Promote(player.PlayerName, (ulong) member.ContentId); + GameFunctions.Party.Promote(player.PlayerName, (ulong) member.ContentId); if (ImGui.Selectable(Language.Context_KickFromParty)) - LogWindow.Plugin.Functions.Party.Kick(player.PlayerName, (ulong) member.ContentId); + GameFunctions.Party.Kick(player.PlayerName, (ulong) member.ContentId); } } @@ -608,8 +608,8 @@ public sealed class PayloadHandler { if (ImGui.Selectable(Language.Context_AddToBlacklist)) LogWindow.Plugin.Functions.AddToBlacklist(player.PlayerName, (ushort) world.RowId); - if (LogWindow.Plugin.Functions.IsMentor() && ImGui.Selectable(Language.Context_InviteToNoviceNetwork)) - LogWindow.Plugin.Functions.Context.InviteToNoviceNetwork(player.PlayerName, (ushort) world.RowId); + if (GameFunctions.GameFunctions.IsMentor() && ImGui.Selectable(Language.Context_InviteToNoviceNetwork)) + GameFunctions.Context.InviteToNoviceNetwork(player.PlayerName, (ushort) world.RowId); } var inputChannel = chunk.Message?.Code.Type.ToInputChannel(); @@ -623,7 +623,7 @@ public sealed class PayloadHandler { Plugin.TargetManager.Target = obj; if (validContentId && ImGui.Selectable(Language.Context_AdventurerPlate)) - if (!LogWindow.Plugin.Functions.TryOpenAdventurerPlate(chunk.Message!.ContentId)) + if (!GameFunctions.GameFunctions.TryOpenAdventurerPlate(chunk.Message!.ContentId)) WrapperUtil.AddNotification(Language.Context_AdventurerPlateError, NotificationType.Warning); // View Party Finder 0x2E diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 73460ea..8531d88 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -191,12 +191,12 @@ public sealed class ChatLogWindow : Window if (info.Channel is InputChannel.Linkshell1 && info.Rotate != RotateMode.None) { - var idx = Plugin.Functions.Chat.RotateLinkshellHistory(mode); + var idx = GameFunctions.Chat.RotateLinkshellHistory(mode); TempChannel = info.Channel.Value + (uint) idx; } else if (info.Channel is InputChannel.CrossLinkshell1 && info.Rotate != RotateMode.None) { - var idx = Plugin.Functions.Chat.RotateCrossLinkshellHistory(mode); + var idx = GameFunctions.Chat.RotateCrossLinkshellHistory(mode); TempChannel = info.Channel.Value + (uint) idx; } } @@ -665,7 +665,7 @@ public sealed class ChatLogWindow : Window var afterIcon = ImGui.GetCursorPos(); var buttonWidth = afterIcon.X - beforeIcon.X; - var showNovice = Plugin.Config.ShowNoviceNetwork && Plugin.Functions.IsMentor(); + var showNovice = Plugin.Config.ShowNoviceNetwork && GameFunctions.GameFunctions.IsMentor(); var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (showNovice ? 2 : 1); var inputType = TempChannel?.ToChatType() ?? activeTab?.Channel?.ToChatType() ?? Plugin.Functions.Chat.Channel.Channel.ToChatType(); @@ -788,7 +788,7 @@ public sealed class ChatLogWindow : Window ImGui.SameLine(); if (ImGuiUtil.IconButton(FontAwesomeIcon.Leaf)) - Plugin.Functions.ClickNoviceNetworkButton(); + GameFunctions.GameFunctions.ClickNoviceNetworkButton(); } internal void SetChannel(InputChannel? channel) @@ -814,7 +814,7 @@ public sealed class ChatLogWindow : Window return; } - Plugin.Functions.Chat.SetChannel(channel.Value); + GameFunctions.Chat.SetChannel(channel.Value); } private void SendChatBox(Tab? activeTab)