Implement #10 with fixed payload byte[]

This commit is contained in:
Infi
2024-04-10 23:57:44 +02:00
parent 27e08064af
commit 9f771c37e3
3 changed files with 95 additions and 70 deletions
+2 -2
View File
@@ -710,9 +710,9 @@ internal sealed unsafe class Chat : IDisposable {
return new TellHistoryInfo(name, world, contentId); 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 uName = Utf8String.FromString(name);
var uMessage = Utf8String.FromString(message); var uMessage = Utf8String.FromSequence(message);
var networkModule = GetNetworkModule(Framework.Instance()); var networkModule = GetNetworkModule(Framework.Instance());
var a1 = *(IntPtr*) (networkModule + 8); var a1 = *(IntPtr*) (networkModule + 8);
+7 -2
View File
@@ -654,7 +654,10 @@ public sealed class ChatLogWindow : Window, IUiComponent {
HandleKeybinds(true); 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) { if (_tempChannel is InputChannel.Tell) {
_tellTarget = null; _tellTarget = null;
} }
@@ -742,7 +745,9 @@ public sealed class ChatLogWindow : Window, IUiComponent {
reason = TellReason.Friend; 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) { if (_tempChannel is InputChannel.Tell) {
+86 -66
View File
@@ -14,11 +14,13 @@ using TextPayload = Lumina.Text.Payloads.TextPayload;
namespace ChatTwo.Util; namespace ChatTwo.Util;
internal static class AutoTranslate { internal static class AutoTranslate
{
private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new(); private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new();
private static readonly HashSet<(uint, uint)> ValidEntries = new(); private static readonly HashSet<(uint, uint)> ValidEntries = new();
private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser() { private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser()
{
var sheetName = Any var sheetName = Any
.AtLeastOnceUntil(Lookahead(Char('[').IgnoreResult().Or(End))) .AtLeastOnceUntil(Lookahead(Char('[').IgnoreResult().Or(End)))
.Select(string.Concat) .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 var payloads = str.Payloads
.Select(p => { .Select(p => {
if (p is TextPayload text) { if (p is TextPayload text) {
@@ -89,18 +92,20 @@ internal static class AutoTranslate {
return string.Join("", payloads); return string.Join("", payloads);
} }
private static List<AutoTranslateEntry> AllEntries(IDataManager data) { private static List<AutoTranslateEntry> AllEntries(IDataManager data)
if (Entries.TryGetValue(data.Language, out var entries)) { {
if (Entries.TryGetValue(data.Language, out var entries))
return entries; return entries;
}
var shouldAdd = ValidEntries.Count == 0; var shouldAdd = ValidEntries.Count == 0;
var parser = Parser(); var parser = Parser();
var list = new List<AutoTranslateEntry>(); var list = new List<AutoTranslateEntry>();
foreach (var row in data.GetExcelSheet<Completion>()!) { foreach (var row in data.GetExcelSheet<Completion>()!)
{
var lookup = row.LookupTable.TextValue(); var lookup = row.LookupTable.TextValue();
if (lookup is not ("" or "@")) { if (lookup is not ("" or "@"))
{
var (sheetName, selector) = parser.ParseOrThrow(lookup); var (sheetName, selector) = parser.ParseOrThrow(lookup);
var sheetType = typeof(Completion) var sheetType = typeof(Completion)
.Assembly .Assembly
@@ -114,18 +119,23 @@ internal static class AutoTranslate {
var columns = new List<int>(); var columns = new List<int>();
var rows = new List<Range>(); var rows = new List<Range>();
if (selector.HasValue) { if (selector.HasValue)
{
columns.Clear(); columns.Clear();
rows.Clear(); rows.Clear();
foreach (var part in selector.Value) { foreach (var part in selector.Value)
switch (part) { {
case IndexRange range: { switch (part)
{
case IndexRange range:
{
var start = (int) range.Start; var start = (int) range.Start;
var end = (int) (range.End + 1); var end = (int) (range.End + 1);
rows.Add(start..end); rows.Add(start..end);
break; break;
} }
case SingleRow single: { case SingleRow single:
{
var idx = (int) single.Row; var idx = (int) single.Row;
rows.Add(idx..(idx + 1)); rows.Add(idx..(idx + 1));
break; break;
@@ -137,33 +147,31 @@ internal static class AutoTranslate {
} }
} }
if (columns.Count == 0) { if (columns.Count == 0)
columns.Add(0); columns.Add(0);
}
if (rows.Count == 0) { if (rows.Count == 0)
rows.Add(..); rows.Add(..);
}
var validRows = rowParsers var validRows = rowParsers.Select(p => p.RowId).ToArray();
.Select(parser => parser.RowId) foreach (var range in rows)
.ToArray(); {
foreach (var range in rows) { for (var i = range.Start.Value; i < range.End.Value; i++)
for (var i = range.Start.Value; i < range.End.Value; i++) { {
if (!validRows.Contains((uint) i)) { if (!validRows.Contains((uint) i))
continue; continue;
}
foreach (var col in columns) { foreach (var col in columns)
var rowParser = rowParsers.FirstOrDefault(parser => parser.RowId == i); {
if (rowParser == null) { var rowParser = rowParsers.FirstOrDefault(p => p.RowId == i);
if (rowParser == null)
continue; continue;
}
var rawName = rowParser.ReadColumn<Lumina.Text.SeString>(col)!; var rawName = rowParser.ReadColumn<Lumina.Text.SeString>(col)!;
var name = rawName.ToDalamudString(); var name = rawName.ToDalamudString();
var text = name.TextValue; var text = name.TextValue;
if (text.Length > 0) { if (text.Length > 0)
{
list.Add(new AutoTranslateEntry( list.Add(new AutoTranslateEntry(
row.Group, row.Group,
(uint) i, (uint) i,
@@ -171,14 +179,15 @@ internal static class AutoTranslate {
name name
)); ));
if (shouldAdd) { if (shouldAdd)
ValidEntries.Add((row.Group, (uint) i)); ValidEntries.Add((row.Group, (uint) i));
}
} }
} }
} }
} }
} else if (lookup is not "@") { }
else if (lookup is not "@")
{
var text = row.Text.ToDalamudString(); var text = row.Text.ToDalamudString();
list.Add(new AutoTranslateEntry( list.Add(new AutoTranslateEntry(
row.Group, row.Group,
@@ -187,9 +196,8 @@ internal static class AutoTranslate {
text text
)); ));
if (shouldAdd) { if (shouldAdd)
ValidEntries.Add((row.Group, row.RowId)); ValidEntries.Add((row.Group, row.RowId));
}
} }
} }
@@ -197,21 +205,23 @@ internal static class AutoTranslate {
return list; return list;
} }
internal static List<AutoTranslateEntry> Matching(IDataManager data, string prefix, bool sort) { internal static List<AutoTranslateEntry> Matching(IDataManager data, string prefix, bool sort)
{
var wholeMatches = new List<AutoTranslateEntry>(); var wholeMatches = new List<AutoTranslateEntry>();
var prefixMatches = new List<AutoTranslateEntry>(); var prefixMatches = new List<AutoTranslateEntry>();
var otherMatches = new List<AutoTranslateEntry>(); var otherMatches = new List<AutoTranslateEntry>();
foreach (var entry in AllEntries(data)) { foreach (var entry in AllEntries(data))
if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase)) { {
if (entry.String.Equals(prefix, StringComparison.OrdinalIgnoreCase))
wholeMatches.Add(entry); wholeMatches.Add(entry);
} else if (entry.String.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { else if (entry.String.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
prefixMatches.Add(entry); prefixMatches.Add(entry);
} else if (entry.String.Contains(prefix, StringComparison.OrdinalIgnoreCase)) { else if (entry.String.Contains(prefix, StringComparison.OrdinalIgnoreCase))
otherMatches.Add(entry); otherMatches.Add(entry);
}
} }
if (sort) { if (sort)
{
return wholeMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase) return wholeMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase)
.Concat(prefixMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase)) .Concat(prefixMatches.OrderBy(entry => entry.String, StringComparer.OrdinalIgnoreCase))
.Concat(otherMatches.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)] [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count); private static extern int memcmp(byte[] b1, byte[] b2, UIntPtr count);
internal static void ReplaceWithPayload(IDataManager data, ref byte[] bytes) { internal static void ReplaceWithPayload(IDataManager data, ref byte[] bytes)
var search = Encoding.UTF8.GetBytes("<at:"); {
if (bytes.Length <= search.Length) { var search = "<at:"u8.ToArray();
if (bytes.Length <= search.Length)
return; return;
}
if (ValidEntries.Count == 0) { // populate the list of valid entries
// populate the list of valid entries if (ValidEntries.Count == 0)
AllEntries(data); AllEntries(data);
}
var start = -1; var start = -1;
for (var i = 0; i < bytes.Length; i++) { for (var i = 0; i < bytes.Length; i++)
{
if (start != -1) { if (start != -1) {
if (bytes[i] == '>') { if (bytes[i] == '>')
{
var tag = Encoding.UTF8.GetString(bytes[start..(i + 1)]); var tag = Encoding.UTF8.GetString(bytes[start..(i + 1)]);
var parts = tag[4..^1].Split(',', 2); 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)) var payload = ValidEntries.Contains((group, key))
? new AutoTranslatePayload(group, key).Encode() ? new AutoTranslatePayload(group, key).Encode()
: Array.Empty<byte>(); : Array.Empty<byte>();
var oldBytes = bytes.ToArray(); var oldBytes = bytes.ToArray();
var lengthDiff = payload.Length - (i - start); var lengthDiff = payload.Length - (i - start);
bytes = new byte[oldBytes.Length + lengthDiff]; bytes = new byte[oldBytes.Length + lengthDiff];
@@ -259,57 +272,64 @@ internal static class AutoTranslate {
} }
start = -1; start = -1;
} else { }
else
{
continue; 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; start = i;
}
} }
} }
} }
internal interface ISelectorPart { internal interface ISelectorPart { }
}
internal class SingleRow : ISelectorPart { internal class SingleRow : ISelectorPart
{
public uint Row { get; } public uint Row { get; }
public SingleRow(uint row) { public SingleRow(uint row)
{
Row = row; Row = row;
} }
} }
internal class IndexRange : ISelectorPart { internal class IndexRange : ISelectorPart
{
public uint Start { get; } public uint Start { get; }
public uint End { get; } public uint End { get; }
public IndexRange(uint start, uint end) { public IndexRange(uint start, uint end)
{
Start = start; Start = start;
End = end; End = end;
} }
} }
internal class NounMarker : ISelectorPart { internal class NounMarker : ISelectorPart { }
}
internal class ColumnSpecifier : ISelectorPart { internal class ColumnSpecifier : ISelectorPart
{
public uint Column { get; } public uint Column { get; }
public ColumnSpecifier(uint column) { public ColumnSpecifier(uint column)
{
Column = column; Column = column;
} }
} }
internal class AutoTranslateEntry { internal class AutoTranslateEntry
{
internal uint Group { get; } internal uint Group { get; }
internal uint Row { get; } internal uint Row { get; }
internal string String { get; } internal string String { get; }
internal SeString SeString { 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; Group = group;
Row = row; Row = row;
String = str; String = str;