From 9f771c37e356b4374169e3ce02c6f9e2063dd60b Mon Sep 17 00:00:00 2001 From: Infi Date: Wed, 10 Apr 2024 23:57:44 +0200 Subject: [PATCH] Implement #10 with fixed payload byte[] --- ChatTwo/GameFunctions/Chat.cs | 4 +- ChatTwo/Ui/ChatLogWindow.cs | 9 +- ChatTwo/Util/AutoTranslate.cs | 152 +++++++++++++++++++--------------- 3 files changed, 95 insertions(+), 70 deletions(-) diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 141a11b..1f98352 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -710,9 +710,9 @@ internal sealed unsafe class Chat : IDisposable { return new TellHistoryInfo(name, world, contentId); } - internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, string message) { + internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message) { var uName = Utf8String.FromString(name); - var uMessage = Utf8String.FromString(message); + var uMessage = Utf8String.FromSequence(message); var networkModule = GetNetworkModule(Framework.Instance()); var a1 = *(IntPtr*) (networkModule + 8); diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 493da0e..b1c9f04 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -654,7 +654,10 @@ public sealed class ChatLogWindow : Window, IUiComponent { HandleKeybinds(true); } - if (!Activate && !ImGui.IsItemActive()) { + // If we're currently using a temporary channel, switch back to the + // original channel when the input loses focus *unless* the + // auto-translate popup is open. + if (_tempChannel != null && !Activate && !ImGui.IsItemActive() && _autoCompleteInfo == null) { if (_tempChannel is InputChannel.Tell) { _tellTarget = null; } @@ -742,7 +745,9 @@ public sealed class ChatLogWindow : Window, IUiComponent { reason = TellReason.Friend; } - Plugin.Functions.Chat.SendTell(reason, target.ContentId, target.Name, (ushort) world.RowId, trimmed); + var tellBytes = Encoding.UTF8.GetBytes(trimmed); + AutoTranslate.ReplaceWithPayload(Plugin.DataManager, ref tellBytes); + Plugin.Functions.Chat.SendTell(reason, target.ContentId, target.Name, (ushort) world.RowId, tellBytes); } if (_tempChannel is InputChannel.Tell) { diff --git a/ChatTwo/Util/AutoTranslate.cs b/ChatTwo/Util/AutoTranslate.cs index 556521e..cfe0085 100644 --- a/ChatTwo/Util/AutoTranslate.cs +++ b/ChatTwo/Util/AutoTranslate.cs @@ -14,11 +14,13 @@ using TextPayload = Lumina.Text.Payloads.TextPayload; namespace ChatTwo.Util; -internal static class AutoTranslate { +internal static class AutoTranslate +{ private static readonly Dictionary> Entries = new(); private static readonly HashSet<(uint, uint)> ValidEntries = new(); - private static Parser> selector)> Parser() { + private static Parser> selector)> Parser() + { var sheetName = Any .AtLeastOnceUntil(Lookahead(Char('[').IgnoreResult().Or(End))) .Select(string.Concat) @@ -60,7 +62,8 @@ internal static class AutoTranslate { ); } - private static string TextValue(this Lumina.Text.SeString str) { + private static string TextValue(this Lumina.Text.SeString str) + { var payloads = str.Payloads .Select(p => { if (p is TextPayload text) { @@ -89,18 +92,20 @@ internal static class AutoTranslate { return string.Join("", payloads); } - private static List AllEntries(IDataManager data) { - if (Entries.TryGetValue(data.Language, out var entries)) { + private static List AllEntries(IDataManager data) + { + if (Entries.TryGetValue(data.Language, out var entries)) return entries; - } var shouldAdd = ValidEntries.Count == 0; var parser = Parser(); var list = new List(); - foreach (var row in data.GetExcelSheet()!) { + foreach (var row in data.GetExcelSheet()!) + { var lookup = row.LookupTable.TextValue(); - if (lookup is not ("" or "@")) { + if (lookup is not ("" or "@")) + { var (sheetName, selector) = parser.ParseOrThrow(lookup); var sheetType = typeof(Completion) .Assembly @@ -114,18 +119,23 @@ internal static class AutoTranslate { var columns = new List(); var rows = new List(); - if (selector.HasValue) { + if (selector.HasValue) + { columns.Clear(); rows.Clear(); - foreach (var part in selector.Value) { - switch (part) { - case IndexRange range: { + foreach (var part in selector.Value) + { + switch (part) + { + case IndexRange range: + { var start = (int) range.Start; var end = (int) (range.End + 1); rows.Add(start..end); break; } - case SingleRow single: { + case SingleRow single: + { var idx = (int) single.Row; rows.Add(idx..(idx + 1)); break; @@ -137,33 +147,31 @@ internal static class AutoTranslate { } } - if (columns.Count == 0) { + if (columns.Count == 0) columns.Add(0); - } - if (rows.Count == 0) { + if (rows.Count == 0) rows.Add(..); - } - var validRows = rowParsers - .Select(parser => parser.RowId) - .ToArray(); - foreach (var range in rows) { - for (var i = range.Start.Value; i < range.End.Value; i++) { - if (!validRows.Contains((uint) i)) { + var validRows = rowParsers.Select(p => p.RowId).ToArray(); + foreach (var range in rows) + { + for (var i = range.Start.Value; i < range.End.Value; i++) + { + if (!validRows.Contains((uint) i)) continue; - } - foreach (var col in columns) { - var rowParser = rowParsers.FirstOrDefault(parser => parser.RowId == i); - if (rowParser == null) { + foreach (var col in columns) + { + var rowParser = rowParsers.FirstOrDefault(p => p.RowId == i); + if (rowParser == null) continue; - } var rawName = rowParser.ReadColumn(col)!; var name = rawName.ToDalamudString(); var text = name.TextValue; - if (text.Length > 0) { + if (text.Length > 0) + { list.Add(new AutoTranslateEntry( row.Group, (uint) i, @@ -171,14 +179,15 @@ internal static class AutoTranslate { name )); - if (shouldAdd) { + if (shouldAdd) ValidEntries.Add((row.Group, (uint) i)); - } } } } } - } else if (lookup is not "@") { + } + else if (lookup is not "@") + { var text = row.Text.ToDalamudString(); list.Add(new AutoTranslateEntry( row.Group, @@ -187,9 +196,8 @@ internal static class AutoTranslate { text )); - if (shouldAdd) { + if (shouldAdd) ValidEntries.Add((row.Group, row.RowId)); - } } } @@ -197,21 +205,23 @@ internal static class AutoTranslate { return list; } - internal static List Matching(IDataManager data, string prefix, bool sort) { + internal static List Matching(IDataManager data, string prefix, bool sort) + { var wholeMatches = new List(); var prefixMatches = new List(); var otherMatches = new List(); - foreach (var entry in AllEntries(data)) { - if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase)) { + foreach (var entry in AllEntries(data)) + { + if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase)) wholeMatches.Add(entry); - } else if (entry.String.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { + else if (entry.String.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) prefixMatches.Add(entry); - } else if (entry.String.Contains(prefix, StringComparison.OrdinalIgnoreCase)) { + else if (entry.String.Contains(prefix, StringComparison.OrdinalIgnoreCase)) otherMatches.Add(entry); - } } - if (sort) { + if (sort) + { return wholeMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase) .Concat(prefixMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase)) .Concat(otherMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase)) @@ -227,27 +237,30 @@ internal static class AutoTranslate { [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); - internal static void ReplaceWithPayload(IDataManager data, ref byte[] bytes) { - var search = Encoding.UTF8.GetBytes("') { + if (bytes[i] == '>') + { var tag = Encoding.UTF8.GetString(bytes[start..(i + 1)]); var parts = tag[4..^1].Split(',', 2); - if (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key)) { + if (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key)) + { var payload = ValidEntries.Contains((group, key)) ? new AutoTranslatePayload(group, key).Encode() : Array.Empty(); + var oldBytes = bytes.ToArray(); var lengthDiff = payload.Length - (i - start); bytes = new byte[oldBytes.Length + lengthDiff]; @@ -259,57 +272,64 @@ internal static class AutoTranslate { } start = -1; - } else { + } + else + { continue; } } - if (i + search.Length < bytes.Length && memcmp(bytes[i..], search, (UIntPtr) search.Length) == 0) { + if (i + search.Length < bytes.Length && memcmp(bytes[i..], search, (UIntPtr) search.Length) == 0) start = i; - } } } } -internal interface ISelectorPart { -} +internal interface ISelectorPart { } -internal class SingleRow : ISelectorPart { +internal class SingleRow : ISelectorPart +{ public uint Row { get; } - public SingleRow(uint row) { + public SingleRow(uint row) + { Row = row; } } -internal class IndexRange : ISelectorPart { +internal class IndexRange : ISelectorPart +{ public uint Start { get; } public uint End { get; } - public IndexRange(uint start, uint end) { + public IndexRange(uint start, uint end) + { Start = start; End = end; } } -internal class NounMarker : ISelectorPart { -} +internal class NounMarker : ISelectorPart { } -internal class ColumnSpecifier : ISelectorPart { +internal class ColumnSpecifier : ISelectorPart +{ public uint Column { get; } - public ColumnSpecifier(uint column) { + public ColumnSpecifier(uint column) + { Column = column; } } -internal class AutoTranslateEntry { +internal class AutoTranslateEntry +{ internal uint Group { get; } internal uint Row { get; } internal string String { get; } internal SeString SeString { get; } - public AutoTranslateEntry(uint group, uint row, string str, SeString seStr) { + public AutoTranslateEntry(uint group, uint row, string str, SeString seStr) + { Group = group; Row = row; String = str;