Implement #10 with fixed payload byte[]
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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,31 +196,32 @@ internal static class AutoTranslate {
|
|||||||
text
|
text
|
||||||
));
|
));
|
||||||
|
|
||||||
if (shouldAdd) {
|
if (shouldAdd)
|
||||||
ValidEntries.Add((row.Group, row.RowId));
|
ValidEntries.Add((row.Group, row.RowId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Entries[data.Language] = list;
|
Entries[data.Language] = list;
|
||||||
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user