From c1aeaceb476ee8fa63e9bc54650c62e2ebae2aa6 Mon Sep 17 00:00:00 2001 From: goat Date: Fri, 22 Nov 2019 21:12:51 +0900 Subject: [PATCH] Refactor chat handling --- Dalamud/Game/ChatHandlers.cs | 40 ++++++++++-------- Dalamud/Game/Command/CommandManager.cs | 7 ++-- Dalamud/Game/Internal/Gui/ChatGui.cs | 17 ++++---- Dalamud/Game/Internal/Libc/OwnedStdString.cs | 4 +- Dalamud/Game/Internal/Libc/StdString.cs | 43 +++++++++----------- 5 files changed, 57 insertions(+), 54 deletions(-) diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index dc7f8e6c9..46156579c 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -7,6 +7,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Dalamud.Game.Chat; +using Dalamud.Game.Internal.Libc; using Serilog; namespace Dalamud.Game { @@ -93,8 +94,8 @@ namespace Dalamud.Game { public string LastLink { get; private set; } - private void ChatOnOnChatMessage(XivChatType type, uint senderId, string sender, byte[] rawMessage, - ref string message, ref bool isHandled) { + private void ChatOnOnChatMessage(XivChatType type, uint senderId, ref StdString sender, + ref StdString message, ref bool isHandled) { if (type == XivChatType.Notice && !this.hasSeenLoadingMsg) { this.dalamud.Framework.Gui.Chat.Print($"XIVLauncher in-game addon v{Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version} loaded."); @@ -106,7 +107,7 @@ namespace Dalamud.Game { return; #endif - var matched = this.rmtRegex.IsMatch(message); + var matched = this.rmtRegex.IsMatch(message.Value); if (matched) { // This seems to be a RMT ad - let's not show it Log.Debug("Handled RMT ad"); @@ -114,10 +115,11 @@ namespace Dalamud.Game { return; } - var originalMessage = string.Copy(message); + var messageVal = message.Value; + var senderVal = sender.Value; if (this.dalamud.Configuration.BadWords != null && - this.dalamud.Configuration.BadWords.Any(x => originalMessage.Contains(x))) { + this.dalamud.Configuration.BadWords.Any(x => messageVal.Contains(x))) { // This seems to be in the user block list - let's not show it Log.Debug("Blocklist triggered"); isHandled = true; @@ -128,7 +130,7 @@ namespace Dalamud.Game { { foreach (var regex in retainerSaleRegexes[dalamud.StartInfo.Language]) { - var matchInfo = regex.Match(message); + var matchInfo = regex.Match(message.Value); // we no longer really need to do/validate the item matching since we read the id from the byte array // but we'd be checking the main match anyway @@ -136,7 +138,7 @@ namespace Dalamud.Game { if (!itemInfo.Success) continue; //var itemName = SeString.Parse(itemInfo.Value).Output; - var (itemId, isHQ) = (ValueTuple)(SeString.Parse(rawMessage).Payloads[0].Param1); + var (itemId, isHQ) = (ValueTuple)(SeString.Parse(message.RawData).Payloads[0].Param1); Log.Debug($"Probable retainer sale: {message}, decoded item {itemId}, HQ {isHQ}"); @@ -152,27 +154,31 @@ namespace Dalamud.Game { } - Task.Run(() => this.dalamud.BotManager.ProcessChatMessage(type, originalMessage, sender).GetAwaiter() + Task.Run(() => this.dalamud.BotManager.ProcessChatMessage(type, messageVal, senderVal).GetAwaiter() .GetResult()); if ((this.HandledChatTypeColors.ContainsKey(type) || type == XivChatType.Say || type == XivChatType.Shout || - type == XivChatType.Alliance || type == XivChatType.TellOutgoing || type == XivChatType.Yell) && !message.Contains((char)0x02)) { - var italicsStart = message.IndexOf("*"); - var italicsEnd = message.IndexOf("*", italicsStart + 1); + type == XivChatType.Alliance || type == XivChatType.TellOutgoing || type == XivChatType.Yell) && !message.Value.Contains((char)0x02)) { + var italicsStart = message.Value.IndexOf("*"); + var italicsEnd = message.Value.IndexOf("*", italicsStart + 1); + + var messageString = message.Value; while (italicsEnd != -1) { var it = MakeItalics( - message.Substring(italicsStart, italicsEnd - italicsStart + 1).Replace("*", "")); - message = message.Remove(italicsStart, italicsEnd - italicsStart + 1); - message = message.Insert(italicsStart, it); - italicsStart = message.IndexOf("*"); - italicsEnd = message.IndexOf("*", italicsStart + 1); + messageString.Substring(italicsStart, italicsEnd - italicsStart + 1).Replace("*", "")); + messageString = messageString.Remove(italicsStart, italicsEnd - italicsStart + 1); + messageString = messageString.Insert(italicsStart, it); + italicsStart = messageString.IndexOf("*"); + italicsEnd = messageString.IndexOf("*", italicsStart + 1); } + + message.RawData = Encoding.UTF8.GetBytes(messageString); } - var linkMatch = this.urlRegex.Match(message); + var linkMatch = this.urlRegex.Match(message.Value); if (linkMatch.Value.Length > 0) LastLink = linkMatch.Value; } diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs index 023a0d061..a6ba3aec2 100644 --- a/Dalamud/Game/Command/CommandManager.cs +++ b/Dalamud/Game/Command/CommandManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text.RegularExpressions; using Dalamud.Game.Chat; +using Dalamud.Game.Internal.Libc; using Serilog; namespace Dalamud.Game.Command { @@ -50,10 +51,10 @@ namespace Dalamud.Game.Command { dalamud.Framework.Gui.Chat.OnChatMessage += OnChatMessage; } - private void OnChatMessage(XivChatType type, uint senderId, string sender, byte[] rawMessage, - ref string message, ref bool isHandled) { + private void OnChatMessage(XivChatType type, uint senderId, ref StdString sender, + ref StdString message, ref bool isHandled) { if (type == XivChatType.GatheringSystemMessage && senderId == 0) { - var cmdMatch = this.CommandRegex.Match(message).Groups["command"]; + var cmdMatch = this.CommandRegex.Match(message.Value).Groups["command"]; if (cmdMatch.Success) { // Yes, it's a chat command. var command = cmdMatch.Value; diff --git a/Dalamud/Game/Internal/Gui/ChatGui.cs b/Dalamud/Game/Internal/Gui/ChatGui.cs index a5cbef50c..f886f60b9 100644 --- a/Dalamud/Game/Internal/Gui/ChatGui.cs +++ b/Dalamud/Game/Internal/Gui/ChatGui.cs @@ -5,6 +5,7 @@ using System.Text; using Dalamud.Game.Chat; using Dalamud.Game.Internal.Libc; using Dalamud.Hooking; +using Discord.Rest; using Serilog; namespace Dalamud.Game.Internal.Gui { @@ -14,7 +15,7 @@ namespace Dalamud.Game.Internal.Gui { IntPtr message, uint senderId, IntPtr parameter); - public delegate void OnMessageDelegate(XivChatType type, uint senderId, string sender, byte[] rawMessage, ref string message, + public delegate void OnMessageDelegate(XivChatType type, uint senderId, ref StdString sender, ref StdString message, ref bool isHandled); @@ -84,25 +85,23 @@ namespace Dalamud.Game.Internal.Gui { IntPtr retVal = IntPtr.Zero; try { - ByteWrapper messageBytes = new ByteWrapper(); - var senderName = StdString.ReadFromPointer(pSenderName); - var message = StdString.ReadFromPointer(pMessage, messageBytes); + var message = StdString.ReadFromPointer(pMessage); - Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message)).Replace("-", " ")}] {message} from {senderName}"); + Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(message.RawData).Replace("-", " ")}] {message} from {senderName}"); // Log.Debug($"Got message bytes {BitConverter.ToString(messageBytes.Bytes).Replace("-", " ")}"); - var originalMessage = string.Copy(message); + var originalMessage = message.RawData.Clone(); // Call events var isHandled = false; - OnChatMessage?.Invoke(chattype, senderid, senderName, messageBytes.Bytes, ref message, ref isHandled); + OnChatMessage?.Invoke(chattype, senderid, ref senderName, ref message, ref isHandled); var messagePtr = pMessage; OwnedStdString allocatedString = null; - if (originalMessage != message) { - allocatedString = this.dalamud.Framework.Libc.NewString(Encoding.UTF8.GetBytes(message)); + if (originalMessage != message.RawData) { + allocatedString = this.dalamud.Framework.Libc.NewString(message.RawData); Log.Debug( $"HandlePrintMessageDetour String modified: {originalMessage}({messagePtr}) -> {message}({allocatedString.Address})"); messagePtr = allocatedString.Address; diff --git a/Dalamud/Game/Internal/Libc/OwnedStdString.cs b/Dalamud/Game/Internal/Libc/OwnedStdString.cs index 5efe897ed..6e3b822dc 100644 --- a/Dalamud/Game/Internal/Libc/OwnedStdString.cs +++ b/Dalamud/Game/Internal/Libc/OwnedStdString.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.InteropServices; using Serilog; @@ -63,7 +63,7 @@ namespace Dalamud.Game.Internal.Libc { GC.SuppressFinalize(this); } - public string Read() { + public StdString Read() { return StdString.ReadFromPointer(Address); } } diff --git a/Dalamud/Game/Internal/Libc/StdString.cs b/Dalamud/Game/Internal/Libc/StdString.cs index 6ced3ffa3..c91b26690 100644 --- a/Dalamud/Game/Internal/Libc/StdString.cs +++ b/Dalamud/Game/Internal/Libc/StdString.cs @@ -1,14 +1,17 @@ using System; +using System.Linq; using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; using System.Text; +using Newtonsoft.Json.Linq; using Serilog; namespace Dalamud.Game.Internal.Libc { /// /// Interation with std::string /// - public static class StdString { - public static string ReadFromPointer(IntPtr cstring, ByteWrapper bytes = null) { + public class StdString { + public static StdString ReadFromPointer(IntPtr cstring) { unsafe { if (cstring == IntPtr.Zero) { throw new ArgumentNullException(nameof(cstring)); @@ -18,35 +21,29 @@ namespace Dalamud.Game.Internal.Libc { if (innerAddress == IntPtr.Zero) { throw new NullReferenceException("Inner reference to the cstring is null."); } - - var pInner = (sbyte*) innerAddress.ToPointer(); + var count = 0; // Count the number of chars. String is assumed to be zero-terminated. - while (*(pInner + count) != 0) { + while (Marshal.ReadByte(innerAddress + count) != 0) { count += 1; } - // raw copy if requested, as the string conversion returned from this function is lossy - if (bytes != null) - { - bytes.Bytes = new byte[count]; - for (int i = 0; i < count; i++) - { - bytes.Bytes[i] = (byte)pInner[i]; - } - } - - return new string(pInner, 0, count, Encoding.UTF8); + // raw copy, as UTF8 string conversion is lossy + var rawData = new byte[count]; + Marshal.Copy(innerAddress, rawData, 0, count); + + return new StdString { + RawData = rawData, + Value = Encoding.UTF8.GetString(rawData) + }; } } - } - /// - /// Wrapper so that we can use an optional byte[] as a parameter - /// - public class ByteWrapper - { - public byte[] Bytes { get; set; } = null; + private StdString() { } + + public string Value { get; private set; } + + public byte[] RawData { get; set; } } }