diff --git a/ChatTwo/ChatTwo.yaml b/ChatTwo/ChatTwo.yaml index 85bd6aa..383a3b4 100755 --- a/ChatTwo/ChatTwo.yaml +++ b/ChatTwo/ChatTwo.yaml @@ -2,9 +2,9 @@ name: Chat 2 author: Infi, Anna punchline: Electric Boogaloo - ♪ A whole new chat, a new fantastic chat window ♪ description: |- - Chat 2 is a complete rewrite of the in-game chat window as a plugin. + Chat 2 is a complete rewrite of the in-game chat window as a plugin. It supports: - + - Unlimited tabs - Tabs that always send to a certain channel - More flexible filtering @@ -15,3 +15,8 @@ description: |- - Screenshot mode (obfuscate names) repo_url: https://github.com/Infiziert90/ChatTwo accepts_feedback: true +tags: + - Social + - UI + - Chat + - Replacement diff --git a/ChatTwo/Code/ChatCode.cs b/ChatTwo/Code/ChatCode.cs index c1ecffd..029c06d 100755 --- a/ChatTwo/Code/ChatCode.cs +++ b/ChatTwo/Code/ChatCode.cs @@ -2,7 +2,8 @@ using LiteDB; namespace ChatTwo.Code; -internal class ChatCode { +internal class ChatCode +{ private const ushort Clear7 = ~(~0 << 7); internal ushort Raw { get; } @@ -12,7 +13,8 @@ internal class ChatCode { internal ChatSource Target { get; } private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((Raw >> shift) & 0xF)); - internal ChatCode(ushort raw) { + internal ChatCode(ushort raw) + { Raw = raw; Type = (ChatType) (Raw & Clear7); Source = SourceFrom(11); @@ -20,14 +22,16 @@ internal class ChatCode { } [BsonCtor] - public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) { + public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) + { Raw = raw; Type = type; Source = source; Target = target; } - internal ChatType Parent() => Type switch { + internal ChatType Parent() => Type switch + { ChatType.Say => ChatType.Say, ChatType.GmSay => ChatType.Say, ChatType.Shout => ChatType.Shout, @@ -84,8 +88,10 @@ internal class ChatCode { _ => Type, }; - internal bool IsBattle() { - switch (Type) { + internal bool IsBattle() + { + switch (Type) + { case ChatType.Damage: case ChatType.Miss: case ChatType.Action: diff --git a/ChatTwo/Code/ChatSource.cs b/ChatTwo/Code/ChatSource.cs index 1e2966b..456bfa6 100755 --- a/ChatTwo/Code/ChatSource.cs +++ b/ChatTwo/Code/ChatSource.cs @@ -2,7 +2,8 @@ namespace ChatTwo.Code; [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] [Flags] -internal enum ChatSource : ushort { +internal enum ChatSource : ushort +{ Self = 2, PartyMember = 4, AllianceMember = 8, diff --git a/ChatTwo/Code/ChatSourceExt.cs b/ChatTwo/Code/ChatSourceExt.cs index 7bdd18e..d7b84e2 100755 --- a/ChatTwo/Code/ChatSourceExt.cs +++ b/ChatTwo/Code/ChatSourceExt.cs @@ -2,7 +2,8 @@ using ChatTwo.Resources; namespace ChatTwo.Code; -internal static class ChatSourceExt { +internal static class ChatSourceExt +{ internal const ChatSource All = ChatSource.Self | ChatSource.PartyMember @@ -16,7 +17,8 @@ internal static class ChatSourceExt { | ChatSource.AlliancePet | ChatSource.OtherPet; - internal static string Name(this ChatSource source) => source switch { + internal static string Name(this ChatSource source) => source switch + { ChatSource.Self => Language.ChatSource_Self, ChatSource.PartyMember => Language.ChatSource_PartyMember, ChatSource.AllianceMember => Language.ChatSource_AllianceMember, diff --git a/ChatTwo/Code/ChatType.cs b/ChatTwo/Code/ChatType.cs index 89e0e4b..28d836f 100755 --- a/ChatTwo/Code/ChatType.cs +++ b/ChatTwo/Code/ChatType.cs @@ -1,7 +1,8 @@ namespace ChatTwo.Code; [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] -internal enum ChatType : ushort { +internal enum ChatType : ushort +{ Debug = 1, Urgent = 2, Notice = 3, @@ -84,7 +85,7 @@ internal enum ChatType : ushort { CrossLinkshell6 = 105, CrossLinkshell7 = 106, CrossLinkshell8 = 107, - + // Custom types: ExtraChatLinkshell1 = 1001, ExtraChatLinkshell2 = 1002, diff --git a/ChatTwo/Code/ChatTypeExt.cs b/ChatTwo/Code/ChatTypeExt.cs index 8b94d50..96b6347 100755 --- a/ChatTwo/Code/ChatTypeExt.cs +++ b/ChatTwo/Code/ChatTypeExt.cs @@ -3,14 +3,19 @@ using ChatTwo.Util; namespace ChatTwo.Code; -internal static class ChatTypeExt { - internal static IEnumerable<(string, ChatType[])> SortOrder => new[] { - (Language.Options_Tabs_ChannelTypes_Special, new[] { +internal static class ChatTypeExt +{ + internal static IEnumerable<(string, ChatType[])> SortOrder => new[] + { + (Language.Options_Tabs_ChannelTypes_Special, + [ ChatType.Debug, ChatType.Urgent, - ChatType.Notice, - }), - (Language.Options_Tabs_ChannelTypes_Chat, new[] { + ChatType.Notice + ]), + + (Language.Options_Tabs_ChannelTypes_Chat, + [ ChatType.Say, ChatType.Yell, ChatType.Shout, @@ -39,9 +44,11 @@ internal static class ChatTypeExt { ChatType.Linkshell8, ChatType.NoviceNetwork, ChatType.StandardEmote, - ChatType.CustomEmote, - }), - (Language.Options_Tabs_ChannelTypes_Battle, new[] { + ChatType.CustomEmote + ]), + + (Language.Options_Tabs_ChannelTypes_Battle, new[] + { ChatType.Damage, ChatType.Miss, ChatType.Action, @@ -52,7 +59,9 @@ internal static class ChatTypeExt { ChatType.GainDebuff, ChatType.LoseDebuff, }), - (Language.Options_Tabs_ChannelTypes_Announcements, new[] { + + (Language.Options_Tabs_ChannelTypes_Announcements, new[] + { ChatType.System, ChatType.BattleSystem, ChatType.GatheringSystem, @@ -80,8 +89,10 @@ internal static class ChatTypeExt { }), }; - internal static string Name(this ChatType type) { - return type switch { + internal static string Name(this ChatType type) + { + return type switch + { ChatType.Debug => Language.ChatType_Debug, ChatType.Urgent => Language.ChatType_Urgent, ChatType.Notice => Language.ChatType_Notice, @@ -174,8 +185,10 @@ internal static class ChatTypeExt { }; } - internal static uint? DefaultColour(this ChatType type) { - switch (type) { + internal static uint? DefaultColour(this ChatType type) + { + switch (type) + { case ChatType.Debug: return ColourUtil.ComponentsToRgba(204, 204, 204); case ChatType.Urgent: @@ -292,7 +305,8 @@ internal static class ChatTypeExt { } } - internal static InputChannel? ToInputChannel(this ChatType type) => type switch { + internal static InputChannel? ToInputChannel(this ChatType type) => type switch + { ChatType.TellOutgoing => InputChannel.Tell, ChatType.Say => InputChannel.Say, ChatType.Party => InputChannel.Party, @@ -321,7 +335,8 @@ internal static class ChatTypeExt { _ => null, }; - internal static bool IsGm(this ChatType type) => type switch { + internal static bool IsGm(this ChatType type) => type switch + { ChatType.GmTell => true, ChatType.GmSay => true, ChatType.GmShout => true, @@ -340,7 +355,8 @@ internal static class ChatTypeExt { _ => false, }; - internal static bool HasSource(this ChatType type) => type switch { + internal static bool HasSource(this ChatType type) => type switch + { // Battle ChatType.Damage => true, ChatType.Miss => true, diff --git a/ChatTwo/Code/InputChannel.cs b/ChatTwo/Code/InputChannel.cs index 31b3f2b..bdf856d 100755 --- a/ChatTwo/Code/InputChannel.cs +++ b/ChatTwo/Code/InputChannel.cs @@ -1,6 +1,7 @@ namespace ChatTwo.Code; -internal enum InputChannel : uint { +internal enum InputChannel : uint +{ Tell = 0, Say = 1, Party = 2, @@ -29,7 +30,7 @@ internal enum InputChannel : uint { Linkshell6 = 24, Linkshell7 = 25, Linkshell8 = 26, - + // Custom channels: ExtraChatLinkshell1 = 1001, ExtraChatLinkshell2 = 1002, diff --git a/ChatTwo/Code/InputChannelExt.cs b/ChatTwo/Code/InputChannelExt.cs index 906e878..57a5733 100755 --- a/ChatTwo/Code/InputChannelExt.cs +++ b/ChatTwo/Code/InputChannelExt.cs @@ -3,8 +3,10 @@ using Lumina.Excel.GeneratedSheets; namespace ChatTwo.Code; -internal static class InputChannelExt { - internal static ChatType ToChatType(this InputChannel input) => input switch { +internal static class InputChannelExt +{ + internal static ChatType ToChatType(this InputChannel input) => input switch + { InputChannel.Tell => ChatType.TellOutgoing, InputChannel.Say => ChatType.Say, InputChannel.Party => ChatType.Party, @@ -41,7 +43,8 @@ internal static class InputChannelExt { _ => throw new ArgumentOutOfRangeException(nameof(input), input, null), }; - public static uint LinkshellIndex(this InputChannel channel) => channel switch { + public static uint LinkshellIndex(this InputChannel channel) => channel switch + { InputChannel.Linkshell1 => 0, InputChannel.Linkshell2 => 1, InputChannel.Linkshell3 => 2, @@ -69,7 +72,8 @@ internal static class InputChannelExt { _ => uint.MaxValue, }; - public static string Prefix(this InputChannel channel) => channel switch { + public static string Prefix(this InputChannel channel) => channel switch + { InputChannel.Tell => "/tell", InputChannel.Say => "/say", InputChannel.Party => "/party", @@ -106,52 +110,47 @@ internal static class InputChannelExt { _ => "", }; - public static IEnumerable? TextCommands(this InputChannel channel, IDataManager data) { - var ids = channel switch { - InputChannel.Tell => new uint[] { 104, 118 }, - InputChannel.Say => new uint[] { 102 }, - InputChannel.Party => new uint[] { 105 }, - InputChannel.Alliance => new uint[] { 119 }, - InputChannel.Yell => new uint[] { 117 }, - InputChannel.Shout => new uint[] { 103 }, - InputChannel.FreeCompany => new uint[] { 115 }, - InputChannel.PvpTeam => new uint[] { 91 }, - InputChannel.NoviceNetwork => new uint[] { 101 }, - InputChannel.CrossLinkshell1 => new uint[] { 13 }, - InputChannel.CrossLinkshell2 => new uint[] { 14 }, - InputChannel.CrossLinkshell3 => new uint[] { 15 }, - InputChannel.CrossLinkshell4 => new uint[] { 16 }, - InputChannel.CrossLinkshell5 => new uint[] { 17 }, - InputChannel.CrossLinkshell6 => new uint[] { 18 }, - InputChannel.CrossLinkshell7 => new uint[] { 19 }, - InputChannel.CrossLinkshell8 => new uint[] { 20 }, - InputChannel.Linkshell1 => new uint[] { 107 }, - InputChannel.Linkshell2 => new uint[] { 108 }, - InputChannel.Linkshell3 => new uint[] { 109 }, - InputChannel.Linkshell4 => new uint[] { 110 }, - InputChannel.Linkshell5 => new uint[] { 111 }, - InputChannel.Linkshell6 => new uint[] { 112 }, - InputChannel.Linkshell7 => new uint[] { 113 }, - InputChannel.Linkshell8 => new uint[] { 114 }, + public static IEnumerable? TextCommands(this InputChannel channel, IDataManager data) + { + var ids = channel switch + { + InputChannel.Tell => [104, 118], + InputChannel.Say => [102], + InputChannel.Party => [105], + InputChannel.Alliance => [119], + InputChannel.Yell => [117], + InputChannel.Shout => [103], + InputChannel.FreeCompany => [115], + InputChannel.PvpTeam => [91], + InputChannel.NoviceNetwork => [101], + InputChannel.CrossLinkshell1 => [13], + InputChannel.CrossLinkshell2 => [14], + InputChannel.CrossLinkshell3 => [15], + InputChannel.CrossLinkshell4 => [16], + InputChannel.CrossLinkshell5 => [17], + InputChannel.CrossLinkshell6 => [18], + InputChannel.CrossLinkshell7 => [19], + InputChannel.CrossLinkshell8 => [20], + InputChannel.Linkshell1 => [107], + InputChannel.Linkshell2 => [108], + InputChannel.Linkshell3 => [109], + InputChannel.Linkshell4 => [110], + InputChannel.Linkshell5 => [111], + InputChannel.Linkshell6 => [112], + InputChannel.Linkshell7 => [113], + InputChannel.Linkshell8 => [114], _ => Array.Empty(), }; - if (ids.Length == 0) { + if (ids.Length == 0) return null; - } var cmds = data.GetExcelSheet(); - if (cmds == null) { - return null; - } - - return ids - .Select(id => cmds.GetRow(id)) - .Where(id => id != null) - .Cast(); + return cmds == null ? null : ids.Select(id => cmds.GetRow(id)).Where(id => id != null).Cast(); } - internal static bool IsLinkshell(this InputChannel channel) => channel switch { + internal static bool IsLinkshell(this InputChannel channel) => channel switch + { InputChannel.Linkshell1 => true, InputChannel.Linkshell2 => true, InputChannel.Linkshell3 => true, @@ -163,7 +162,8 @@ internal static class InputChannelExt { _ => false, }; - internal static bool IsCrossLinkshell(this InputChannel channel) => channel switch { + internal static bool IsCrossLinkshell(this InputChannel channel) => channel switch + { InputChannel.CrossLinkshell1 => true, InputChannel.CrossLinkshell2 => true, InputChannel.CrossLinkshell3 => true, @@ -174,8 +174,9 @@ internal static class InputChannelExt { InputChannel.CrossLinkshell8 => true, _ => false, }; - - internal static bool IsExtraChatLinkshell(this InputChannel channel) => channel switch { + + internal static bool IsExtraChatLinkshell(this InputChannel channel) => channel switch + { InputChannel.ExtraChatLinkshell1 => true, InputChannel.ExtraChatLinkshell2 => true, InputChannel.ExtraChatLinkshell3 => true, diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 1f98352..b1487f0 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -36,9 +36,6 @@ internal sealed unsafe class Chat : IDisposable { [Signature("E8 ?? ?? ?? ?? 48 8D 4D A0 8B F8")] private readonly delegate* unmanaged GetKeybindNative = null!; - [Signature("E8 ?? ?? ?? ?? 48 3B F0 74 35")] - private readonly delegate* unmanaged GetFocus = null!; - [Signature("44 8B 89 ?? ?? ?? ?? 4C 8B C1 45 85 C9")] private readonly delegate* unmanaged GetTellHistory = null!; @@ -72,6 +69,10 @@ internal sealed unsafe class Chat : IDisposable { [Signature("E8 ?? ?? ?? ?? EB 0A 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8D")] private readonly delegate* unmanaged SanitiseString = null!; + // Currently Unused + [Signature("E8 ?? ?? ?? ?? 48 3B F0 74 35")] + private readonly delegate* unmanaged GetFocus = null!; + // Hooks private delegate byte ChatLogRefreshDelegate(IntPtr log, ushort eventId, AtkValue* value); @@ -155,7 +156,8 @@ internal sealed unsafe class Chat : IDisposable { internal bool UsesTellTempChannel { get; set; } internal InputChannel? PreviousChannel { get; private set; } - internal Chat(Plugin plugin) { + internal Chat(Plugin plugin) + { Plugin = plugin; Plugin.GameInteropProvider.InitializeFromAttributes(this); @@ -203,21 +205,21 @@ internal sealed unsafe class Chat : IDisposable { } internal string? GetCrossLinkshellName(uint idx) { - if (CrossLinkshellInfoProxyIdx is not { } proxyIdx) { + if (CrossLinkshellInfoProxyIdx is not { } proxyIdx) return null; - } var infoProxy = Plugin.Functions.GetInfoProxyByIndex(proxyIdx); - if (infoProxy == IntPtr.Zero) { + if (infoProxy == IntPtr.Zero) return null; - } var utf = GetCrossLinkshellNameNative(infoProxy, idx); return utf == null ? null : utf->ToString(); } - internal ulong RotateLinkshellHistory(RotateMode mode) { - if (mode == RotateMode.None && LinkshellCycleOffset != null) { + internal ulong RotateLinkshellHistory(RotateMode mode) + { + if (mode == RotateMode.None && LinkshellCycleOffset != null) + { // for the branch at 6.08: 5E1680 var uiModule = (IntPtr) Framework.Instance()->GetUiModule(); *(int*) (uiModule + LinkshellCycleOffset.Value) = -1; @@ -230,11 +232,11 @@ internal sealed unsafe class Chat : IDisposable { private static ulong RotateLinkshellHistoryInternal(delegate* unmanaged func, RotateMode mode) { // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (func == null) { + if (func == null) return 0; - } - var idx = mode switch { + var idx = mode switch + { RotateMode.Forward => 1, RotateMode.Reverse => -1, _ => 0, @@ -248,9 +250,8 @@ internal sealed unsafe class Chat : IDisposable { // // If this function would ever return 0, it returns null instead. internal uint? GetChannelColour(ChatType type) { - if (GetColourInfo == null || ColourLookup == IntPtr.Zero) { + if (GetColourInfo == null || ColourLookup == IntPtr.Zero) return null; - } // Colours are retrieved by looking up their code in a lookup table. Some codes share a colour, so they're lumped into a parent code here. // Only codes >= 10 (say) have configurable colours. @@ -272,9 +273,8 @@ internal sealed unsafe class Chat : IDisposable { var info = GetColourInfo(framework + 16, lookupResult); var rgb = *(uint*) (info + 32) & 0xFFFFFF; - if (rgb == 0) { + if (rgb == 0) return null; - } return 0xFF | (rgb << 8); } @@ -350,154 +350,161 @@ internal sealed unsafe class Chat : IDisposable { private int _graceFrames; - private void CheckFocus() { - void Decrement() { - if (_graceFrames > 0) { + private void CheckFocus() + { + void Decrement() + { + if (_graceFrames > 0) _graceFrames -= 1; - } else { + else _inputFocused = false; - } } // 6.08: CB8F27 var isTextInputActivePtr = *(bool**) ((IntPtr) AtkStage.GetSingleton() + 0x28) + 0x188E; - if (isTextInputActivePtr == null) { + if (isTextInputActivePtr == null) + { Decrement(); return; } - if (*isTextInputActivePtr) { + if (*isTextInputActivePtr) + { _inputFocused = true; _graceFrames = 60; - } else { + } + else + { Decrement(); } } - private void UpdateKeybinds() { - foreach (var name in KeybindsToIntercept.Keys) { + private void UpdateKeybinds() + { + foreach (var name in KeybindsToIntercept.Keys) + { var keybind = GetKeybind(name); - if (keybind is null) { + if (keybind is null) continue; - } _keybinds[name] = keybind; } } - private void InterceptKeybinds(IFramework framework1) { + private void InterceptKeybinds(IFramework framework1) + { CheckFocus(); UpdateKeybinds(); - if (_inputFocused) { + if (_inputFocused) return; - } var modifierState = (ModifierFlag) 0; - foreach (var modifier in Enum.GetValues()) { + foreach (var modifier in Enum.GetValues()) + { var modifierKey = GetKeyForModifier(modifier); - if (modifierKey != VirtualKey.NO_KEY && Plugin.KeyState[modifierKey]) { + if (modifierKey != VirtualKey.NO_KEY && Plugin.KeyState[modifierKey]) modifierState |= modifier; - } } var turnedOff = new Dictionary(); - foreach (var toIntercept in KeybindsToIntercept.Keys) { - if (!Keybinds.TryGetValue(toIntercept, out var keybind)) { + foreach (var toIntercept in KeybindsToIntercept.Keys) + { + if (!Keybinds.TryGetValue(toIntercept, out var keybind)) continue; - } - void Intercept(VirtualKey key, ModifierFlag modifier) { - if (!Plugin.KeyState.IsVirtualKeyValid(key)) { + void Intercept(VirtualKey key, ModifierFlag modifier) + { + if (!Plugin.KeyState.IsVirtualKeyValid(key)) return; - } - var modifierPressed = Plugin.Config.KeybindMode switch { + var modifierPressed = Plugin.Config.KeybindMode switch + { KeybindMode.Strict => modifier == modifierState, KeybindMode.Flexible => modifierState.HasFlag(modifier), _ => false, }; - if (!modifierPressed) { - return; - } - if (!Plugin.KeyState[key]) { + if (!modifierPressed) + return; + + if (!Plugin.KeyState[key]) return; - } var bits = BitOperations.PopCount((uint) modifier); - if (!turnedOff.TryGetValue(key, out var previousBits) || previousBits.Item1 < bits) { + if (!turnedOff.TryGetValue(key, out var previousBits) || previousBits.Item1 < bits) turnedOff[key] = ((uint) bits, toIntercept); - } } Intercept(keybind.Key1, keybind.Modifier1); Intercept(keybind.Key2, keybind.Modifier2); } - foreach (var (key, (_, keybind)) in turnedOff) { + foreach (var (key, (_, keybind)) in turnedOff) + { Plugin.KeyState[key] = false; - - if (!KeybindsToIntercept.TryGetValue(keybind, out var info)) { + if (!KeybindsToIntercept.TryGetValue(keybind, out var info)) continue; - } - try { - Activated?.Invoke(new ChatActivatedArgs(info) { - TellReason = TellReason.Reply, - }); - } catch (Exception ex) { + try + { + Activated?.Invoke(new ChatActivatedArgs(info) { TellReason = TellReason.Reply, }); + } + catch (Exception ex) + { Plugin.Log.Error(ex, "Error in chat Activated event"); } } } private void Login() { - if (ChangeChannelNameHook == null) { + if (ChangeChannelNameHook == null) return; - } var agent = Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.ChatLog); - if (agent == null) { + if (agent == null) return; - } ChangeChannelNameDetour((IntPtr) agent); } - private byte ChatLogRefreshDetour(IntPtr log, ushort eventId, AtkValue* value) { - if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C)) { + private byte ChatLogRefreshDetour(IntPtr log, ushort eventId, AtkValue* value) + { + if (eventId != 0x31 || value == null || value->UInt is not (0x05 or 0x0C)) return ChatLogRefreshHook!.Original(log, eventId, value); - } string? input = null; - if (Plugin.GameConfig.TryGet(UiControlOption.DirectChat, out bool option) && option) { - if (CurrentCharacter != null) { + if (Plugin.GameConfig.TryGet(UiControlOption.DirectChat, out bool option) && option) + { + if (CurrentCharacter != null) + { // FIXME: this whole system sucks var c = *CurrentCharacter; - if (c != '\0' && !char.IsControl(c)) { + if (c != '\0' && !char.IsControl(c)) input = c.ToString(); - } } } string? addIfNotPresent = null; var str = value + 2; - if (str != null && ((int) str->Type & 0xF) == (int) ValueType.String && str->String != null) { + if (str != null && ((int) str->Type & 0xF) == (int) ValueType.String && str->String != null) + { var add = MemoryHelper.ReadStringNullTerminated((IntPtr) str->String); - if (add.Length > 0) { + if (add.Length > 0) addIfNotPresent = add; - } } - try { + try + { var args = new ChatActivatedArgs(new ChannelSwitchInfo(null)) { AddIfNotPresent = addIfNotPresent, Input = input, }; Activated?.Invoke(args); - } catch (Exception ex) { + } + catch (Exception ex) + { Plugin.Log.Error(ex, "Error in chat Activated event"); } @@ -505,66 +512,60 @@ internal sealed unsafe class Chat : IDisposable { return 1; } - private IntPtr ChangeChannelNameDetour(IntPtr agent) { + private IntPtr ChangeChannelNameDetour(IntPtr agent) + { // Last ShB patch // +0x40 = chat channel (byte or uint?) // channel is 17 (maybe 18?) for tells // +0x48 = pointer to channel name string var ret = ChangeChannelNameHook!.Original(agent); - if (agent == IntPtr.Zero) { + if (agent == IntPtr.Zero) return ret; - } // E8 ?? ?? ?? ?? 8D 48 F7 // RaptureShellModule + 0xFD0 var shellModule = (IntPtr) Framework.Instance()->GetUiModule()->GetRaptureShellModule(); - if (shellModule == IntPtr.Zero) { + if (shellModule == IntPtr.Zero) return ret; - } var channel = 0u; - if (ShellChannelOffset != null) { + if (ShellChannelOffset != null) channel = *(uint*) (shellModule + ShellChannelOffset.Value); - } // var channel = *(uint*) (agent + 0x40); - if (channel is 17 or 18) { + if (channel is 17 or 18) channel = 0; - } SeString? name = null; var namePtrPtr = (byte**) (agent + 0x48); - if (namePtrPtr != null) { + if (namePtrPtr != null) + { var namePtr = *namePtrPtr; name = MemoryHelper.ReadSeStringNullTerminated((IntPtr) namePtr); - if (name.Payloads.Count == 0) { + if (name.Payloads.Count == 0) name = null; - } } - if (name == null) { + if (name == null) return ret; - } var nameChunks = ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(); - if (nameChunks.Count > 0 && nameChunks[0] is TextChunk text) { + if (nameChunks.Count > 0 && nameChunks[0] is TextChunk text) text.Content = text.Content.TrimStart('\uE01E').TrimStart(); - } Channel = ((InputChannel) channel, nameChunks); return ret; } - private void ReplyInSelectedChatModeDetour(AgentInterface* agent) { - if (ReplyChannelOffset == null) { + private void ReplyInSelectedChatModeDetour(AgentInterface* agent) + { + if (ReplyChannelOffset == null) goto Original; - } var replyMode = *(int*) ((IntPtr) agent + ReplyChannelOffset.Value); - if (replyMode == -2) { + if (replyMode == -2) goto Original; - } SetChannel((InputChannel) replyMode); @@ -572,15 +573,21 @@ internal sealed unsafe class Chat : IDisposable { ReplyInSelectedChatModeHook!.Original(agent); } - private byte SetChatLogTellTargetDetour(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort reason, byte a7) { - if (name != null) { - try { + private byte SetChatLogTellTargetDetour(IntPtr a1, Utf8String* name, Utf8String* a3, ushort world, ulong contentId, ushort reason, byte a7) + { + if (name != null) + { + try + { var target = new TellTarget(name->ToString(), world, contentId, (TellReason) reason); - Activated?.Invoke(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell)) { + Activated?.Invoke(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell)) + { TellReason = (TellReason) reason, TellTarget = target, }); - } catch (Exception ex) { + } + catch (Exception ex) + { Plugin.Log.Error(ex, "Error in chat Activated event"); } } @@ -602,15 +609,16 @@ internal sealed unsafe class Chat : IDisposable { EurekaContextMenuTellHook!.Original(param1, playerName, worldName, world, id, param6); } - internal ulong? GetContentIdForEntry(uint index) { - if (GetContentIdForChatEntry == null) { + internal ulong? GetContentIdForEntry(uint index) + { + if (GetContentIdForChatEntry == null) return null; - } return GetContentIdForChatEntry(Framework.Instance()->GetUiModule()->GetRaptureLogModule(), index); } - internal void SetChannel(InputChannel channel, string? tellTarget = null) { + internal 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. // @@ -651,23 +659,23 @@ internal sealed unsafe class Chat : IDisposable { utfWorld->Dtor(true); } - private static VirtualKey GetKeyForModifier(ModifierFlag modifierFlag) => modifierFlag switch { + private static VirtualKey GetKeyForModifier(ModifierFlag modifierFlag) => modifierFlag switch + { ModifierFlag.Shift => VirtualKey.SHIFT, ModifierFlag.Ctrl => VirtualKey.CONTROL, ModifierFlag.Alt => VirtualKey.MENU, _ => VirtualKey.NO_KEY, }; - private Keybind? GetKeybind(string id) { + private Keybind? GetKeybind(string id) + { var agent = (IntPtr) Framework.Instance()->GetUiModule()->GetAgentModule()->GetAgentByInternalId(AgentId.Configkey); - if (agent == IntPtr.Zero) { + if (agent == IntPtr.Zero) return null; - } var a1 = *(void**) (agent + 0x78); - if (a1 == null) { + if (a1 == null) return null; - } var outData = stackalloc byte[32]; var idString = Utf8String.FromString(id); @@ -675,16 +683,15 @@ internal sealed unsafe class Chat : IDisposable { idString->Dtor(true); var key1 = (VirtualKey) outData[0]; - if (key1 is VirtualKey.F23) { + if (key1 is VirtualKey.F23) key1 = VirtualKey.OEM_2; - } var key2 = (VirtualKey) outData[2]; - if (key2 is VirtualKey.F23) { + if (key2 is VirtualKey.F23) key2 = VirtualKey.OEM_2; - } - return new Keybind { + return new Keybind + { Key1 = key1, Modifier1 = (ModifierFlag) outData[1], Key2 = key2, @@ -692,16 +699,15 @@ internal sealed unsafe class Chat : IDisposable { }; } - internal TellHistoryInfo? GetTellHistoryInfo(int index) { + internal TellHistoryInfo? GetTellHistoryInfo(int index) + { var acquaintanceModule = Framework.Instance()->GetUiModule()->GetAcquaintanceModule(); - if (acquaintanceModule == null) { + if (acquaintanceModule == null) return null; - } var ptr = GetTellHistory(acquaintanceModule, index); - if (ptr == IntPtr.Zero) { + if (ptr == IntPtr.Zero) return null; - } var name = MemoryHelper.ReadStringNullTerminated(*(IntPtr*) ptr); var world = *(ushort*) (ptr + 0xD0); @@ -710,7 +716,8 @@ internal sealed unsafe class Chat : IDisposable { return new TellHistoryInfo(name, world, contentId); } - internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message) { + internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message) + { var uName = Utf8String.FromString(name); var uMessage = Utf8String.FromSequence(message); @@ -725,7 +732,8 @@ internal sealed unsafe class Chat : IDisposable { uMessage->Dtor(true); } - internal bool IsCharValid(char c) { + internal bool IsCharValid(char c) + { var uC = Utf8String.FromString(c.ToString()); SanitiseString(uC, 0x27F, IntPtr.Zero);