- Check auto translation for commands and execute them instead of sending

- Plugin commands trigger the command helper window now
- Fix auto translation with empty text appearing
- Switch up all dalamud payload usage to ROSS if possible
- Prepare 7.5 changes
- Cleanup
This commit is contained in:
Infi
2026-04-08 21:15:28 +02:00
parent 9f7a6267f6
commit c424311b24
52 changed files with 614 additions and 423 deletions
+68 -41
View File
@@ -3,13 +3,12 @@ using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Utility;
using Lumina.Excel;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
using Pidgin;
using static Pidgin.Parser;
using static Pidgin.Parser<char>;
@@ -157,6 +156,9 @@ internal static class AutoTranslate
}
else if (lookup is not "@")
{
if (row.Text.IsEmpty)
continue;
list.Add(new AutoTranslateEntry(row.Group, row.RowId, row.Text.ToString(), row.GroupTitle.ToString()));
if (shouldAdd)
@@ -242,9 +244,7 @@ internal static class AutoTranslate
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))
{
var payload = ValidEntries.Contains((group, key))
? new AutoTranslatePayload(group, key).Encode()
: [];
var payload = ValidEntries.Contains((group, key)) ? CreateFixedTranslation(group, key) : [];
var oldBytes = bytes.ToArray();
var lengthDiff = payload.Length - (i - start);
@@ -263,56 +263,83 @@ internal static class AutoTranslate
start = i;
}
}
public static bool StartsWithCommand(ref byte[] bytes)
{
var search = "<at:"u8;
if (bytes.Length <= search.Length)
return false;
// populate the list of valid entries
if (ValidEntries.Count == 0)
AllEntries();
for (var i = 0; i < search.Length; i++)
{
if (bytes[i] != search[i])
return false;
}
for (var i = 0; i < bytes.Length; i++)
{
if (bytes[i] != '>')
continue;
var tag = Encoding.UTF8.GetString(bytes[..(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 (!ValidEntries.Contains((group, key)))
return false;
var evaluated = Plugin.Evaluator.Evaluate(new ReadOnlySeString(CreateFixedTranslation(group, key))).ToString();
if (!evaluated.StartsWith('/'))
return false;
bytes = Encoding.UTF8.GetBytes(evaluated);
return true;
}
}
return false;
}
private static byte[] CreateFixedTranslation(uint group, uint key)
{
using var rssb = new RentedSeStringBuilder();
return rssb.Builder
.BeginMacro(MacroCode.Fixed)
.AppendUIntExpression(group - 1)
.AppendUIntExpression(key)
.EndMacro()
.ToArray();
}
}
internal interface ISelectorPart { }
internal class SingleRow : ISelectorPart
internal class SingleRow(uint row) : ISelectorPart
{
public uint Row { get; }
public SingleRow(uint row)
{
Row = row;
}
public uint Row { get; } = row;
}
internal class IndexRange : ISelectorPart
internal class IndexRange(uint start, uint end) : ISelectorPart
{
public uint Start { get; }
public uint End { get; }
public IndexRange(uint start, uint end)
{
Start = start;
End = end;
}
public uint Start { get; } = start;
public uint End { get; } = end;
}
internal class NounMarker : ISelectorPart { }
internal class ColumnSpecifier : ISelectorPart
internal class ColumnSpecifier(uint column) : ISelectorPart
{
public uint Column { get; }
public ColumnSpecifier(uint column)
{
Column = column;
}
public uint Column { get; } = column;
}
internal class AutoTranslateEntry
internal class AutoTranslateEntry(uint group, uint row, string str, string title)
{
internal uint Group { get; }
internal uint Row { get; }
internal string Text { get; }
internal string Title { get; }
public AutoTranslateEntry(uint group, uint row, string str, string title)
{
Group = group;
Row = row;
Text = str;
Title = title;
}
internal uint Group { get; } = group;
internal uint Row { get; } = row;
internal string Text { get; } = str;
internal string Title { get; } = title;
}
+277 -12
View File
@@ -1,8 +1,11 @@
using System.Runtime.CompilerServices;
using ChatTwo.Code;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using System.Text;
using Lumina.Text.Expressions;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
using PayloadType = Dalamud.Game.Text.SeStringHandling.PayloadType;
@@ -10,6 +13,244 @@ namespace ChatTwo.Util;
internal static class ChunkUtil
{
// internal static IEnumerable<Chunk> ToChunks(ReadOnlySeString msg, ChunkSource source, ChatType? defaultColour)
// {
// var chunks = new List<Chunk>();
//
// var italic = false;
// var foreground = new Stack<uint>();
// var glow = new Stack<uint>();
// Payload? link = null;
//
// void Append(string text)
// {
// chunks.Add(new TextChunk(source, link, text)
// {
// FallbackColour = defaultColour,
// Foreground = foreground.Count > 0 ? foreground.Peek() : null,
// Glow = glow.Count > 0 ? glow.Peek() : null,
// Italic = italic,
// });
// }
//
// foreach (var payload in msg)
// {
// if (payload.Type == ReadOnlySePayloadType.Text)
// {
// // We don't want to parse any null string
// var str = payload.ToString();
// var nulIndex = str.IndexOf('\0');
// if (nulIndex > 0)
// str = str[..nulIndex];
// if (string.IsNullOrEmpty(str))
// continue;
//
// Append(str);
// continue;
// }
//
// switch (payload.MacroCode)
// {
// case MacroCode.Italic:
// var newStatus = payload.TryGetExpression(out var expression) && expression.TryGetUInt(out var value) && value == 1;
// italic = newStatus;
// break;
// case MacroCode.Color:
// if (payload.TryGetExpression(out var eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// var color = ColourUtil.ArgbToRgba(eColorVal);
//
// if (color > 0)
// foreground.Push(color);
// else if (foreground.Count > 0) // Push the previous color as we don't want invisible text
// foreground.Push(foreground.Peek());
// }
// }
// break;
// case MacroCode.EdgeColor:
// if (payload.TryGetExpression(out eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// glow.Push(ColourUtil.ArgbToRgba(eColorVal));
// }
// }
// break;
// case MacroCode.ColorType:
// if (!payload.TryGetExpression(out var eColorType) || !eColorType.TryGetUInt(out var eColorTypeVal))
// {
// if (foreground.Count > 0)
// foreground.Pop();
// break;
// }
//
// if (eColorTypeVal == 0)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eColorTypeVal, out var row))
// {
// foreground.Push(row.Dark);
// }
// break;
// case MacroCode.EdgeColorType:
// if (!payload.TryGetExpression(out var eEdgeColor) || !eEdgeColor.TryGetUInt(out var eEdgeColorVal))
// {
// if (glow.Count > 0)
// glow.Pop();
// break;
// }
//
// if (eEdgeColorVal == 0)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eEdgeColorVal, out var row))
// {
// glow.Push(row.Dark);
// }
// break;
// case MacroCode.Fixed:
// if (!payload.TryGetExpression(out var expr1, out var expr2))
// break;
//
// if (expr1.TryGetUInt(out var group) && expr2.TryGetUInt(out var key))
// {
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateBegin));
// using var rssb = new RentedSeStringBuilder();
// var translatePayload = rssb.Builder
// .BeginMacro(MacroCode.Fixed)
// .AppendUIntExpression(group - 1)
// .AppendUIntExpression(key)
// .EndMacro()
// .ToReadOnlySeString();
//
// Append(Plugin.Evaluator.Evaluate(translatePayload).ToString());
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateEnd));
// }
// break;
// case MacroCode.Icon:
// if (payload.TryGetExpression(out var eIcon) && TryResolveInt(eIcon, out var iconVal))
// chunks.Add(new IconChunk(source, link, (BitmapFontIcon)iconVal));
// break;
// case MacroCode.Link:
// if (!payload.TryGetExpression(
// out var linkTypeExpr1,
// out var uintExpr2,
// out var intExpr3,
// out var intExpr4,
// out var strExpr5))
// break;
//
// if (!linkTypeExpr1.TryGetUInt(out var linkType))
// break;
//
// switch ((LinkMacroPayloadType)linkType)
// {
// case LinkMacroPayloadType.Terminator:
// link = null;
// break;
// case LinkMacroPayloadType.MapPosition:
// if (!uintExpr2.TryGetUInt(out var ids))
// break;
//
// if (!intExpr3.TryGetInt(out var rawX))
// break;
//
// if (!intExpr4.TryGetInt(out var rawY))
// break;
//
// var mapId = ids & 0xFF;
// var territoryId = (ids >> 16) & 0xFF;
// break;
// case (LinkMacroPayloadType)Payload.EmbeddedInfoType.DalamudLink - 1:
// if (!uintExpr2.TryGetUInt(out var commandId))
// break;
//
// if (!intExpr3.TryGetInt(out var extra1))
// break;
//
// if (!intExpr4.TryGetInt(out var extra2))
// break;
//
// if (!strExpr5.TryGetString(out var extraStr))
// break;
// break;
// case LinkMacroPayloadType.Quest:
// if (!uintExpr2.TryGetUInt(out var questId))
// break;
// break;
// case LinkMacroPayloadType.Status:
// if (!uintExpr2.TryGetUInt(out var statusId))
// break;
// break;
// case LinkMacroPayloadType.Item:
// if (!uintExpr2.TryGetUInt(out var itemId))
// break;
// break;
// case LinkMacroPayloadType.Character:
// if (!uintExpr2.TryGetUInt(out var flags))
// break;
//
// if (!intExpr3.TryGetUInt(out var worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinder:
// if (!uintExpr2.TryGetUInt(out var listingId))
// break;
//
// // intExpr3 is unused
//
// if (!intExpr4.TryGetUInt(out worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinderNotification:
// // no expr used
// break;
// case LinkMacroPayloadType.Achievement:
// if (!uintExpr2.TryGetUInt(out var achievementId))
// break;
// break;
// }
// break;
// case MacroCode.NonBreakingSpace:
// Append(" ");
// break;
// case PayloadType.Unknown:
// var rawPayload = (RawPayload)payload;
// else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14)
// {
// if (glow.Count > 0)
// {
// glow.Pop();
// }
// else if (rawPayload.Data.Length > 6 && rawPayload.Data[2] == 0x05 && rawPayload.Data[3] == 0xF6)
// {
// var (r, g, b) = (rawPayload.Data[4], rawPayload.Data[5], rawPayload.Data[6]);
// glow.Push(ColourUtil.ComponentsToRgba(r, g, b));
// }
// }
// break;
// }
// }
//
// return chunks;
// }
internal static IEnumerable<Chunk> ToChunks(SeString msg, ChunkSource source, ChatType? defaultColour)
{
var chunks = new List<Chunk>();
@@ -176,22 +417,46 @@ internal static class ChunkUtil
return BitConverter.ToUInt32(numArray, 0);
}
public static unsafe void PrintMemoryArea(nint address, int length)
private static bool TryResolveUInt(in ReadOnlySeExpressionSpan expression, out uint value)
{
var ptr = (byte*)address;
var str = new StringBuilder("\n");
for(var i = 0; i < length; i++)
if (expression.TryGetUInt(out value))
return true;
if (expression.TryGetParameterExpression(out var exprType, out var operand1))
{
str.Append($"{ptr![i]:X02}");
if (!TryResolveUInt(operand1, out var paramIndex))
return false;
if (i == 0)
continue;
if (paramIndex == 0)
return false;
if ((i+1) % 16 == 0)
str.Append('\n');
else if ((i+1) % 4 == 0)
str.Append(' ');
paramIndex--;
if ((ExpressionType)exprType == ExpressionType.GlobalNumber)
{
value = (uint) GlobalParametersCache.GetValue((int)paramIndex);
return true;
}
// return (ExpressionType)exprType switch
// {
// // ExpressionType.LocalNumber => context.TryGetLNum((int)paramIndex, out value), // lnum
// ExpressionType.GlobalNumber => (uint) GlobalParametersCache.GetValue((int)paramIndex), // gnum
// _ => false, // gstr, lstr
// };
}
Plugin.Log.Information(str.ToString());
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryResolveInt(in ReadOnlySeExpressionSpan expression, out int value)
{
if (TryResolveUInt(expression, out var u32))
{
value = (int)u32;
return true;
}
value = 0;
return false;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Numerics;
namespace ChatTwo.Util;
internal static class ColourUtil {
internal static (byte r, byte g, byte b, byte a) RgbaToComponents(uint rgba)
private static (byte r, byte g, byte b, byte a) RgbaToComponents(uint rgba)
{
var r = (byte) ((rgba & 0xFF000000) >> 24);
var g = (byte) ((rgba & 0xFF0000) >> 16);
+3 -3
View File
@@ -25,7 +25,7 @@ public static class DateWidget
private static float LongestMonthWidth;
private static readonly float[] MonthWidths = new float[12];
private static uint LastOpenComboID;
private static uint LastOpenComboId;
public static bool Validate(DateTime minimal, ref DateTime currentMin, ref DateTime currentMax)
{
@@ -109,9 +109,9 @@ public static class DateWidget
// reset date when user right-clicks the date chooser header when the dialog is open
dateOut = DateTime.Now;
}
else if (LastOpenComboID != id)
else if (LastOpenComboId != id)
{
LastOpenComboID = id;
LastOpenComboId = id;
if (dateOut.Year == 1)
dateOut = DateTime.Now;
}
+2 -2
View File
@@ -2,7 +2,7 @@
public class ColorPayload
{
private const byte START_BYTE = 2;
private const byte StartByte = 2;
public bool Enabled;
public uint Color;
@@ -11,7 +11,7 @@ public class ColorPayload
public static ColorPayload? From(byte[] data)
{
using var stream = new MemoryStream(data);
if (stream.ReadByte() != START_BYTE || stream.ReadByte() != 0x13)
if (stream.ReadByte() != StartByte || stream.ReadByte() != 0x13)
return null;
stream.ReadByte(); // skip the length byte;
+28 -120
View File
@@ -183,13 +183,15 @@ internal static class ImGuiUtil
if (id != null)
label += $"##{id}";
Plugin.FontManager.FontAwesome.Push();
var size = new Vector2(0, 0);
if (width > 0)
size.X = width - 2 * ImGui.GetStyle().CellPadding.X;
bool ret;
using (Plugin.FontManager.FontAwesome.Push())
{
var size = Vector2.Zero;
if (width > 0)
size.X = width - 2 * ImGui.GetStyle().CellPadding.X;
var ret = ImGui.Button(label, size);
Plugin.FontManager.FontAwesome.Pop();
ret = ImGui.Button(label, size);
}
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
@@ -200,7 +202,7 @@ internal static class ImGuiUtil
internal static bool OptionCheckbox(ref bool value, string label, string? description = null)
{
var ret = ImGui.Checkbox(label, ref value);
if (description != null)
if (!string.IsNullOrEmpty(description))
HelpText(description);
return ret;
@@ -208,13 +210,9 @@ internal static class ImGuiUtil
internal static void HelpText(string text)
{
var colour = ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled];
using (TextWrapPos())
using (ImRaii.PushColor(ImGuiCol.Text, colour))
{
using (ImRaii.TextWrapPos(0.0f))
using (ImRaii.PushColor(ImGuiCol.Text, ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled]))
ImGui.TextUnformatted(text);
}
}
internal static void WarningText(string text, bool wrap = true)
@@ -222,11 +220,9 @@ internal static class ImGuiUtil
var style = StyleModel.GetConfiguredStyle() ?? StyleModel.GetFromCurrent();
var dalamudOrange = style.BuiltInColors?.DalamudOrange;
using (TextWrapPos(wrap))
using (ImRaii.TextWrapPos(wrap ? 0.0f : ImGui.GetFontSize() * 35.0f))
using (ImRaii.PushColor(ImGuiCol.Text, dalamudOrange ?? Vector4.Zero, dalamudOrange != null))
{
ImGui.TextUnformatted(text);
}
}
internal static ImRaii.IEndObject BeginComboVertical(string label, string previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None)
@@ -267,9 +263,7 @@ internal static class ImGuiUtil
{
using (ImRaii.Tooltip())
using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f))
{
ImGui.TextUnformatted(tooltip);
}
}
public static SingleFontChooserDialog? FontChooser(string label, SingleFontSpec font, bool checkbox, ref bool checkboxValue, Predicate<IFontFamilyId>? exclusion = null, string? preview = null)
@@ -306,20 +300,18 @@ internal static class ImGuiUtil
ImGui.TextUnformatted(label);
ImGui.SetNextItemWidth(-1);
using var combo = ImRaii.Combo($"##{label}", $"{currentSize:###.##}pt");
if (combo)
{
foreach (var size in FontManager.AxisFontSizeList)
if (ImGui.Selectable($"{size:###.##}pt", currentSize.Equals(size)))
currentSize = size;
}
if (!combo.Success)
return;
foreach (var size in FontManager.AxisFontSizeList)
if (ImGui.Selectable($"{size:###.##}pt", currentSize.Equals(size)))
currentSize = size;
}
public static bool Button(string id, FontAwesomeIcon icon, bool disabled)
{
using (ImRaii.Disabled(disabled))
{
return ImGuiComponents.IconButton(id, icon);
}
}
internal static bool CtrlShiftButton(string label, string tooltip = "")
@@ -330,30 +322,12 @@ internal static class ImGuiUtil
using (ImRaii.Disabled(!ctrlShiftHeld))
ret = ImGui.Button(label) && ctrlShiftHeld;
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
if (tooltip.Length != 0 && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
return ret;
}
internal static bool CtrlShiftButtonColored(string label, string tooltip = "")
{
var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
var colorNormal = new Vector4(0.780f, 0.245f, 0.245f, 1.0f);
var colorHovered = new Vector4(0.7f, 0.0f, 0.0f, 1.0f);
using (ImRaii.PushColor(ImGuiCol.Button, colorNormal))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, colorHovered))
{
var ret = ImGui.Button(label) && ctrlShiftHeld;
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
return ret;
}
}
internal static void KeybindInput(string id, ref ConfigKeyBind? keybind)
{
var idUint = ImGui.GetID(id);
@@ -394,19 +368,15 @@ internal static class ImGuiUtil
return;
}
foreach (var vk in Enum.GetValues(typeof(VirtualKey)).Cast<VirtualKey>())
foreach (var vk in Enum.GetValues<VirtualKey>())
{
if (vk is VirtualKey.NO_KEY or VirtualKey.CONTROL or VirtualKey.LCONTROL or VirtualKey.RCONTROL or VirtualKey.SHIFT or VirtualKey.LSHIFT or VirtualKey.RSHIFT or VirtualKey.MENU or VirtualKey.LMENU or VirtualKey.RMENU)
continue;
if (!TryToImGui(vk, out var imKey) || !ImGui.IsKeyPressed(imKey))
if (!vk.TryToImGui(out var imKey) || !ImGui.IsKeyPressed(imKey))
continue;
keybind = new ConfigKeyBind
{
Modifier = currentMods,
Key = vk
};
keybind = new ConfigKeyBind { Modifier = currentMods, Key = vk };
ImGui.GetStateStorage().SetBool(idUint, false);
return;
}
@@ -442,6 +412,12 @@ internal static class ImGuiUtil
}
}
public static void WrappedTextWithColor(Vector4 color, string text)
{
using (ImRaii.PushColor(ImGuiCol.Text, color))
ImGui.TextWrapped(text);
}
public static void CenterText(string text, float indent = 0.0f)
{
indent *= ImGuiHelpers.GlobalScale;
@@ -566,63 +542,6 @@ internal static class ImGuiUtil
return result != 0 || key == VirtualKey.NO_KEY;
}
public struct EndUnconditionally(Action endAction, bool success) : ImRaii.IEndObject
{
public bool Success { get; } = success;
private bool Disposed { get; set; } = false;
private Action EndAction { get; } = endAction;
public void Dispose()
{
if (!Disposed)
{
EndAction();
Disposed = true;
}
}
}
// Use end-function only on success.
private struct EndConditionally(Action endAction, bool success) : ImRaii.IEndObject
{
public bool Success { get; } = success;
private bool Disposed { get; set; } = false;
private Action EndAction { get; } = endAction;
public void Dispose()
{
if (Disposed)
return;
if (Success)
EndAction();
Disposed = true;
}
}
public static ImRaii.IEndObject TextWrapPos()
{
ImGui.PushTextWrapPos();
return new EndUnconditionally(ImGui.PopTextWrapPos, true);
}
public static ImRaii.IEndObject TextWrapPos(bool condition)
{
if (!condition)
return new EndUnconditionally(Nop, false);
ImGui.PushTextWrapPos();
return new EndUnconditionally(ImGui.PopTextWrapPos, true);
}
public static ImRaii.IEndObject Menu(string label)
{
return new EndConditionally(ImGui.EndMenu, ImGui.BeginMenu(label));
}
public static void ChannelSelector(string headerText, Dictionary<ChatType, ChatSource> chatCodes)
{
using var channelNode = ImRaii.TreeNode(headerText);
@@ -696,15 +615,4 @@ internal static class ImGuiUtil
extraChatChannels.Remove(id);
}
}
public static void WrappedTextWithColor(Vector4 color, string text)
{
using (ImRaii.PushColor(ImGuiCol.Text, color))
{
ImGui.TextWrapped(text);
}
}
// Used to avoid pops if condition is false for Push.
private static void Nop() { }
}
+2 -5
View File
@@ -25,13 +25,10 @@ public static class MathUtil
SizeY = Y + Height;
}
public Rectangle(Vector2 pos, Vector2 size)
: this((int) pos.X, (int) pos.Y, (int) size.X, (int) size.Y) { }
public Rectangle(Vector2 pos, Vector2 size) : this((int) pos.X, (int) pos.Y, (int) size.X, (int) size.Y) { }
public override string ToString()
{
return $"X: {X} Y: {Y} Width: {Width} Height: {Height}";
}
=> $"X: {X} Y: {Y} Width: {Width} Height: {Height}";
}
// From: https://stackoverflow.com/a/306379
+26
View File
@@ -0,0 +1,26 @@
using System.Text;
namespace ChatTwo.Util;
public static class MemoryUtil
{
public static unsafe void PrintMemoryArea(nint address, int length)
{
var ptr = (byte*)address;
var str = new StringBuilder("\n");
for(var i = 0; i < length; i++)
{
str.Append($"{ptr![i]:X02}");
if (i == 0)
continue;
if ((i+1) % 16 == 0)
str.Append('\n');
else if ((i+1) % 4 == 0)
str.Append(' ');
}
Plugin.Log.Information(str.ToString());
}
}
+7 -7
View File
@@ -63,18 +63,18 @@ internal class UriPayload(Uri uri) : Payload
/// <exception cref="UriFormatException">
/// If the URI is invalid, or if the scheme is not supported.
/// </exception>
public static UriPayload ResolveURI(string rawURI)
public static UriPayload ResolveUri(string rawUri)
{
ArgumentNullException.ThrowIfNull(rawURI);
ArgumentNullException.ThrowIfNull(rawUri);
// Check for an expected scheme '://', if not add 'https://'
if (ExpectedSchemes.Any(scheme => rawURI.StartsWith($"{scheme}://")))
return new UriPayload(new Uri(rawURI));
if (ExpectedSchemes.Any(scheme => rawUri.StartsWith($"{scheme}://")))
return new UriPayload(new Uri(rawUri));
if (rawURI.Contains("://"))
throw new UriFormatException($"Unsupported scheme in URL: {rawURI}");
if (rawUri.Contains("://"))
throw new UriFormatException($"Unsupported scheme in URL: {rawUri}");
return new UriPayload(new Uri($"{DefaultScheme}://{rawURI}"));
return new UriPayload(new Uri($"{DefaultScheme}://{rawUri}"));
}
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
+2 -1
View File
@@ -10,6 +10,7 @@ internal static class TabsUtil
var channels = new Dictionary<ChatType, ChatSource>();
foreach (var chatType in Enum.GetValues<ChatType>())
channels[chatType] = ChatSourceExt.All;
return channels;
}
@@ -76,7 +77,7 @@ internal static class TabsUtil
[ChatType.MessageBook] = ChatSourceExt.All,
[ChatType.Alarm] = ChatSourceExt.All,
[ChatType.GlamourNotifications] = ChatSourceExt.All,
},
}
};
internal static Tab VanillaEvent => new()
+3 -3
View File
@@ -47,7 +47,7 @@ public static class Tokenizer
new TokenDefinition(TokenType.Whitespace, "\\s", 1),
new TokenDefinition(TokenType.Equals, "=", 1),
new TokenDefinition(TokenType.OpenParenthesis, "\\(", 1),
new TokenDefinition(TokenType.UrlString, URLRegex, 1),
new TokenDefinition(TokenType.UrlString, UrlRegex, 1),
new TokenDefinition(TokenType.StringValue, "\\p{IsBasicLatin}", 2),
new TokenDefinition(TokenType.Leftover, ".", 3)
];
@@ -127,7 +127,7 @@ public static class Tokenizer
private class TokenMatch
{
public TokenType TokenType { get; set; }
public string Value { get; set; }
public required string Value { get; set; }
public int StartIndex { get; set; }
public int EndIndex { get; set; }
public int Precedence { get; set; }
@@ -145,7 +145,7 @@ public static class Tokenizer
/// It matches URLs with www. or https:// prefix, and also matches URLs
/// without a prefix on specific TLDs.
/// </summary>
private static Regex URLRegex = new(
private static readonly Regex UrlRegex = new(
@"(?<URL>((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]*)?)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture
);
+2 -20
View File
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using ChatTwo.Resources;
using ChatTwo.Resources;
using Dalamud.Interface.ImGuiNotification;
namespace ChatTwo.Util;
@@ -11,7 +10,7 @@ public static class WrapperUtil
Plugin.Notification.AddNotification(new Notification { Content = content, Type = type, Minimized = minimized });
}
public static void TryOpenURI(Uri uri)
public static void TryOpenUri(Uri uri)
{
try
{
@@ -24,21 +23,4 @@ public static class WrapperUtil
AddNotification(Language.Context_OpenInBrowserError, NotificationType.Error);
}
}
public static IEnumerable<(T Value, int Index)> WithIndex<T>(this IEnumerable<T> list)
=> list.Select((x, i) => (x, i));
/// <summary> Return the first object fulfilling the predicate or null for structs.</summary>
/// <param name="values"> The enumerable. </param>
/// <param name="predicate"> The predicate. </param>
/// <returns> The first object fulfilling the predicate, or a null-optional. </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static T? FirstOrNull<T>(this IEnumerable<T> values, Func<T, bool> predicate) where T : struct
{
foreach(var val in values)
if (predicate(val))
return val;
return null;
}
}