@ -1,5 +1,4 @@
using System.Runtime.InteropServices ;
using System.Text ;
using System.Text ;
using ChatTwo.Code ;
using ChatTwo.Util ;
using Dalamud.Game.Text.SeStringHandling ;
@ -13,6 +12,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Client.UI.Misc ;
using FFXIVClientStructs.FFXIV.Client.UI.Shell ;
using FFXIVClientStructs.FFXIV.Component.GUI ;
using Siggingway ;
namespace ChatTwo ;
@ -20,64 +20,79 @@ 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 IsMentorA1 = "48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 84 C0 74 71 0F B6 86" ;
internal const string CurrentChatEntryOffset = "8B 77 ?? 8D 46 01 89 47 14 81 FE ?? ?? ?? ?? 72 03 FF 47" ;
internal const string AgentContextYesNo = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 84 C0 74 3A" ;
}
#region Functions
[Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < RaptureShellModule * , int , uint , Utf8String * , byte , void > _changeChatChannel = null ! ;
[Signature("4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < RaptureLogModule * , uint , ulong > _getContentIdForChatEntry = null ! ;
[Signature("E8 ?? ?? ?? ?? 8B FD 8B CD", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < IntPtr , uint , IntPtr > _indexer = null ! ;
[Signature("E8 ?? ?? ?? ?? 33 C0 EB 51", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < IntPtr , ulong , byte * , ushort , void > _inviteToParty = null ! ;
[Signature(("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9"), Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < IntPtr , ulong , ushort , byte * , byte > _inviteToNoviceNetwork = null ! ;
[Signature("40 53 48 83 EC 20 48 8B D9 48 8B 49 10 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B 48 48", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < AgentInterface * , byte > _friendRequestBool = null ! ;
[Signature("E8 ?? ?? ?? ?? EB 35 BA", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < uint , uint , ulong , uint , byte , byte > _tryOn = null ! ;
[Signature("E8 ?? ?? ?? ?? EB 7B 49 8B 06", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < AgentInterface * , uint , void > _linkItem = null ! ;
[Signature("E8 ?? ?? ?? ?? EB 3F 83 F8 FE", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < AgentInterface * , ushort , uint , byte , void > _itemComparison = null ! ;
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 41 B4 01", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < IntPtr , uint , void > _searchForRecipesUsingItem = null ! ;
[Signature("E8 ?? ?? ?? ?? EB 45 45 33 C9", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < void * , uint , byte , void > _searchForItem = null ! ;
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 84 C0 74 3A", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < AgentInterface * , uint , byte * , ushort , uint , byte , void > _agentContextYesNo = null ! ;
[Signature("E8 ?? ?? ?? ?? 84 C0 74 0D B0 02", Fallibility = Fallibility.Fallible)]
private readonly delegate * unmanaged < IntPtr , byte > _isMentor = null ! ;
#endregion
#region Hooks
private delegate byte ChatLogRefreshDelegate ( IntPtr log , ushort eventId , AtkValue * value ) ;
private delegate IntPtr ChangeChannelNameDelegate ( IntPtr agent ) ;
private delegate IntPtr AgentContextYesNoDelegate ( AgentInterface * context , uint a2 , byte * playerName , ushort playerWorld , uint a5 , byte a6 ) ;
[Signature(Signatures.ChatLogRefresh, DetourName = nameof(ChatLogRefreshDetour))]
private Hook < ChatLogRefreshDelegate > ? ChatLogRefreshHook { get ; init ; }
internal delegate void ChatActivatedEventDelegate ( string? input ) ;
#region Functions
[Signature("E8 ?? ?? ?? ?? 0F B7 44 37 ??")]
private readonly delegate * unmanaged < RaptureShellModule * , int , uint , Utf8String * , byte , void > _changeChatChannel ;
[Signature("4C 8B 81 ?? ?? ?? ?? 4D 85 C0 74 17")]
private readonly delegate * unmanaged < RaptureLogModule * , uint , ulong > _getContentIdForChatEntry ;
[Signature("E8 ?? ?? ?? ?? 8B FD 8B CD")]
private readonly delegate * unmanaged < IntPtr , uint , IntPtr > _indexer ;
[Signature("E8 ?? ?? ?? ?? 33 C0 EB 51")]
private readonly delegate * unmanaged < IntPtr , ulong , byte * , ushort , void > _inviteToParty ;
[Signature(("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 48 8B CB E8 ?? ?? ?? ?? 45 33 C9"))]
private readonly delegate * unmanaged < IntPtr , ulong , ushort , byte * , byte > _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 < AgentInterface * , byte > _friendRequestBool ;
[Signature("E8 ?? ?? ?? ?? EB 35 BA")]
private readonly delegate * unmanaged < uint , uint , ulong , uint , byte , byte > _tryOn ;
[Signature("E8 ?? ?? ?? ?? EB 7B 49 8B 06")]
private readonly delegate * unmanaged < AgentInterface * , uint , void > _linkItem ;
[Signature("E8 ?? ?? ?? ?? EB 3F 83 F8 FE")]
private readonly delegate * unmanaged < AgentInterface * , ushort , uint , byte , void > _itemComparison ;
[Signature("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 41 B4 01")]
private readonly delegate * unmanaged < IntPtr , uint , void > _searchForRecipesUsingItem ;
[Signature("E8 ?? ?? ?? ?? EB 45 45 33 C9")]
private readonly delegate * unmanaged < void * , uint , byte , void > _searchForItem ;
[Signature(Signatures.ChangeChannelName, DetourName = nameof(ChangeChannelNameDetour))]
private Hook < ChangeChannelNameDelegate > ? ChangeChannelNameHook { get ; init ; }
#endregion
[Signature(Signatures.CurrentChatEntryOffset, Offset = 2)]
private readonly byte? _currentChatEntryOffset ;
[Signature(Signatures.IsMentorA1, ScanType = ScanType.StaticAddress)]
private readonly IntPtr ? _isMentorA1 ;
internal const int HqItemOffset = 1_000_000 ;
private Plugin Plugin { get ; }
private Hook < ChatLogRefreshDelegate > ? ChatLogRefreshHook { get ; }
private Hook < ChangeChannelNameDelegate > ? ChangeChannelNameHook { get ; }
private readonly int? _currentChatEntryOffset ;
private readonly AgentContextYesNoDelegate ? _agentContextYesNo ;
internal delegate void ChatActivatedEventDelegate ( string? input ) ;
internal event ChatActivatedEventDelegate ? ChatActivated ;
@ -86,25 +101,10 @@ internal unsafe class GameFunctions : IDisposable {
internal GameFunctions ( Plugin plugin ) {
this . Plugin = plugin ;
this . Plugin . SigScanner .ScanFunctions ( this ) ;
Siggingway . Siggingway . Initialise ( this . Plugin . SigScanner , this ) ;
if ( this . Plugin . SigScanner . TryScanText ( Signatures . ChatLogRefresh , out var chatLogPtr ) ) {
this . ChatLogRefreshHook = new Hook < ChatLogRefreshDelegate > ( chatLogPtr , this . ChatLogRefreshDetour ) ;
this . ChatLogRefreshHook . Enable ( ) ;
}
if ( this . Plugin . SigScanner . TryScanText ( Signatures . ChangeChannelName , out var channelNamePtr ) ) {
this . ChangeChannelNameHook = new Hook < ChangeChannelNameDelegate > ( channelNamePtr , this . ChangeChannelNameDetour ) ;
this . ChangeChannelNameHook . Enable ( ) ;
}
if ( this . Plugin . SigScanner . TryScanText ( Signatures . CurrentChatEntryOffset , out var entryOffsetPtr ) ) {
this . _currentChatEntryOffset = * ( byte * ) ( entryOffsetPtr + 2 ) ;
}
if ( this . Plugin . SigScanner . TryScanText ( Signatures . AgentContextYesNo , out var sendFriendRequestPtr ) ) {
this . _agentContextYesNo = Marshal . GetDelegateForFunctionPointer < AgentContextYesNoDelegate > ( sendFriendRequestPtr ) ;
}
this . ChatLogRefreshHook ? . Enable ( ) ;
this . ChangeChannelNameHook ? . Enable ( ) ;
this . Plugin . ClientState . Login + = this . Login ;
this . Login ( null , null ) ;
@ -170,7 +170,7 @@ internal unsafe class GameFunctions : IDisposable {
}
var agent = Framework . Instance ( ) - > GetUiModule ( ) - > GetAgentModule ( ) - > GetAgentByInternalId ( AgentId . Context ) ;
var a6 = this . _friendRequestBool ( agent ) ;
var a6 = this . _friendRequestBool ( agent ) = = 0 ? ( byte ) 1 : ( byte ) 0 ;
fixed ( byte * namePtr = name . ToTerminatedBytes ( ) ) {
// 6.05: 20DA57
@ -229,7 +229,7 @@ internal unsafe class GameFunctions : IDisposable {
}
}
internal void SetChatInteractable ( bool interactable ) {
internal static void SetChatInteractable ( bool interactable ) {
for ( var i = 0 ; i < 4 ; i + + ) {
SetAddonInteractable ( $"ChatLogPanel_{i}" , interactable ) ;
}
@ -249,7 +249,7 @@ internal unsafe class GameFunctions : IDisposable {
return ( * flags & ( 1 < < 22 ) ) = = 0 ;
}
internal void OpenItemTooltip ( uint id ) {
internal static void OpenItemTooltip ( uint id ) {
var atkStage = AtkStage . GetSingleton ( ) ;
var agent = Framework . Instance ( ) - > GetUiModule ( ) - > GetAgentModule ( ) - > GetAgentByInternalId ( AgentId . ItemDetail ) ;
var addon = atkStage - > RaptureAtkUnitManager - > GetAddonByName ( "ItemDetail" ) ;
@ -289,7 +289,7 @@ internal unsafe class GameFunctions : IDisposable {
( * vf5 ) ( addon , 0 , 15 ) ;
}
internal void CloseItemTooltip ( ) {
internal static void CloseItemTooltip ( ) {
// hide addon first to prevent the "addon close" sound
var addon = AtkStage . GetSingleton ( ) - > RaptureAtkUnitManager - > GetAddonByName ( "ItemDetail" ) ;
if ( addon ! = null ) {
@ -382,7 +382,6 @@ internal unsafe class GameFunctions : IDisposable {
// 0x10005: copy item name
// 0x10006: search recipes using this material
internal void TryOn ( uint itemId , byte stainId ) {
if ( this . _tryOn = = null ) {
return ;
@ -428,4 +427,31 @@ internal unsafe class GameFunctions : IDisposable {
var itemFinder = Framework . Instance ( ) - > GetUiModule ( ) - > GetItemFinderModule ( ) ;
this . _searchForItem ( itemFinder , itemId , 1 ) ;
}
internal static void OpenPartyFinder ( ) {
// this whole method: 6.05: 84433A
var lfg = Framework . Instance ( ) - > GetUiModule ( ) - > GetAgentModule ( ) - > GetAgentByInternalId ( AgentId . LookingForGroup ) ;
if ( lfg - > IsAgentActive ( ) ) {
var addonId = lfg - > GetAddonID ( ) ;
var atkModule = Framework . Instance ( ) - > GetUiModule ( ) - > GetRaptureAtkModule ( ) ;
var atkModuleVtbl = ( void * * ) atkModule - > AtkModule . vtbl ;
var vf27 = ( delegate * unmanaged < RaptureAtkModule * , ulong , ulong , byte > ) atkModuleVtbl [ 27 ] ;
vf27 ( atkModule , addonId , 1 ) ;
} else {
// 6.05: 8443DD
if ( * ( uint * ) ( ( IntPtr ) lfg + 0x2AB8 ) > 0 ) {
lfg - > Hide ( ) ;
} else {
lfg - > Show ( ) ;
}
}
}
internal bool IsMentor ( ) {
if ( this . _isMentor = = null | | this . _isMentorA1 = = null | | this . _isMentorA1 . Value = = IntPtr . Zero ) {
return false ;
}
return this . _isMentor ( this . _isMentorA1 . Value ) > 0 ;
}
}