diff --git a/ChatTwo/ChatTwo.csproj b/ChatTwo/ChatTwo.csproj index 6e7b896..3cb0275 100755 --- a/ChatTwo/ChatTwo.csproj +++ b/ChatTwo/ChatTwo.csproj @@ -1,6 +1,6 @@ - + - 1.34.4 + 1.34.5 enable enable true @@ -53,10 +53,10 @@ - - - - + + + + diff --git a/ChatTwo/Code/ChatSource.cs b/ChatTwo/Code/ChatSource.cs index d087bcf..37b8ed5 100755 --- a/ChatTwo/Code/ChatSource.cs +++ b/ChatTwo/Code/ChatSource.cs @@ -1,6 +1,5 @@ namespace ChatTwo.Code; -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] [Flags] public enum ChatSource : ushort { diff --git a/ChatTwo/Code/ChatSourceExt.cs b/ChatTwo/Code/ChatSourceExt.cs index d7b84e2..d6df7b0 100755 --- a/ChatTwo/Code/ChatSourceExt.cs +++ b/ChatTwo/Code/ChatSourceExt.cs @@ -5,17 +5,10 @@ namespace ChatTwo.Code; internal static class ChatSourceExt { internal const ChatSource All = - ChatSource.Self - | ChatSource.PartyMember - | ChatSource.AllianceMember - | ChatSource.Other - | ChatSource.EngagedEnemy - | ChatSource.UnengagedEnemy - | ChatSource.FriendlyNpc - | ChatSource.SelfPet - | ChatSource.PartyPet - | ChatSource.AlliancePet - | ChatSource.OtherPet; + ChatSource.Self | ChatSource.PartyMember | ChatSource.AllianceMember | + ChatSource.Other | ChatSource.EngagedEnemy | ChatSource.UnengagedEnemy | + ChatSource.FriendlyNpc | ChatSource.SelfPet | ChatSource.PartyPet | + ChatSource.AlliancePet | ChatSource.OtherPet; internal static string Name(this ChatSource source) => source switch { diff --git a/ChatTwo/Code/ChatType.cs b/ChatTwo/Code/ChatType.cs index 18cca11..727d722 100755 --- a/ChatTwo/Code/ChatType.cs +++ b/ChatTwo/Code/ChatType.cs @@ -1,6 +1,5 @@ namespace ChatTwo.Code; -[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] public enum ChatType : ushort { Debug = 1, diff --git a/ChatTwo/Code/ChatTypeExt.cs b/ChatTwo/Code/ChatTypeExt.cs index 615f64a..616b192 100755 --- a/ChatTwo/Code/ChatTypeExt.cs +++ b/ChatTwo/Code/ChatTypeExt.cs @@ -6,14 +6,9 @@ namespace ChatTwo.Code; internal static class ChatTypeExt { - internal static IEnumerable<(string, ChatType[])> SortOrder => new[] - { - (Language.Options_Tabs_ChannelTypes_Special, - [ - ChatType.Debug, - ChatType.Urgent, - ChatType.Notice - ]), + internal static IEnumerable<(string, ChatType[])> SortOrder => + [ + (Language.Options_Tabs_ChannelTypes_Special, [ChatType.Debug, ChatType.Urgent, ChatType.Notice]), (Language.Options_Tabs_ChannelTypes_Chat, [ @@ -48,8 +43,8 @@ internal static class ChatTypeExt ChatType.CustomEmote ]), - (Language.Options_Tabs_ChannelTypes_Battle, new[] - { + (Language.Options_Tabs_ChannelTypes_Battle, + [ ChatType.Damage, ChatType.Miss, ChatType.Action, @@ -58,11 +53,11 @@ internal static class ChatTypeExt ChatType.GainBuff, ChatType.LoseBuff, ChatType.GainDebuff, - ChatType.LoseDebuff, - }), + ChatType.LoseDebuff + ]), - (Language.Options_Tabs_ChannelTypes_Announcements, new[] - { + (Language.Options_Tabs_ChannelTypes_Announcements, + [ ChatType.System, ChatType.BattleSystem, ChatType.GatheringSystem, @@ -87,11 +82,11 @@ internal static class ChatTypeExt ChatType.Orchestrion, ChatType.MessageBook, ChatType.Alarm, - ChatType.GlamourNotifications, - }), + ChatType.GlamourNotifications + ]) // Note: ExtraChat linkshells are handled separately in the tab settings // UI. - }; + ]; internal static string Name(this ChatType type) { diff --git a/ChatTwo/Code/InputChannelExt.cs b/ChatTwo/Code/InputChannelExt.cs index 00f32fe..f5a2555 100755 --- a/ChatTwo/Code/InputChannelExt.cs +++ b/ChatTwo/Code/InputChannelExt.cs @@ -1,4 +1,3 @@ -using Dalamud.Plugin.Services; using Lumina.Excel.Sheets; namespace ChatTwo.Code; @@ -108,11 +107,10 @@ internal static class InputChannelExt InputChannel.ExtraChatLinkshell6 => "/ecl6", InputChannel.ExtraChatLinkshell7 => "/ecl7", InputChannel.ExtraChatLinkshell8 => "/ecl8", - InputChannel.Invalid => "/e", _ => "/e", }; - public static IEnumerable? TextCommands(this InputChannel channel, IDataManager data) + public static IEnumerable? TextCommands(this InputChannel channel) { var ids = channel switch { @@ -147,8 +145,7 @@ internal static class InputChannelExt if (ids.Length == 0) return null; - var cmds = data.GetExcelSheet(); - return ids.Where(id => cmds.HasRow(id)).Select(id => cmds.GetRow(id)); + return ids.Where(id => Sheets.TextCommandSheet.HasRow(id)).Select(id => Sheets.TextCommandSheet.GetRow(id)); } internal static bool IsLinkshell(this InputChannel channel) => channel switch diff --git a/ChatTwo/Commands.cs b/ChatTwo/Commands.cs index bb91514..a3b6386 100755 --- a/ChatTwo/Commands.cs +++ b/ChatTwo/Commands.cs @@ -4,13 +4,7 @@ namespace ChatTwo; internal sealed class Commands : IDisposable { - private Plugin Plugin { get; } - private Dictionary Registered { get; } = new(); - - internal Commands(Plugin plugin) - { - Plugin = plugin; - } + private readonly Dictionary Registered = []; public void Dispose() { @@ -47,7 +41,8 @@ internal sealed class Commands : IDisposable return Registered[name]; } - private void Invoke(string command, string arguments) { + private void Invoke(string command, string arguments) + { if (!Registered.TryGetValue(command, out var wrapper)) { Plugin.Log.Warning($"Missing registration for command {command}"); diff --git a/ChatTwo/Configuration.cs b/ChatTwo/Configuration.cs index 3417780..8c6201c 100755 --- a/ChatTwo/Configuration.cs +++ b/ChatTwo/Configuration.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Concurrent; using ChatTwo.Code; using ChatTwo.GameFunctions.Types; using ChatTwo.Resources; diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 2be3a10..a8f442f 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -478,12 +478,20 @@ internal sealed unsafe class Chat : IDisposable internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message, string rawText) { + if (contentId == 0) + { + Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error); + Plugin.Log.Warning("Tried to send a tell with ContentId being 0, sorry this is an internal error."); + return; + } + var uName = Utf8String.FromString(name); var uMessage = Utf8String.FromSequence(message); var encoded = Utf8String.FromUtf8String(PronounModule.Instance()->ProcessString(uMessage, true)); var decoded = EncodeMessage(rawText); AutoTranslate.ReplaceWithPayload(ref decoded); + using var decodedUtf8String = new Utf8String(decoded); var logModule = RaptureLogModule.Instance(); @@ -493,25 +501,11 @@ internal sealed unsafe class Chat : IDisposable if (reason == TellReason.Direct) reason = TellReason.Friend; - if (contentId == 0) - { - encoded->Dtor(true); - uName->Dtor(true); - uMessage->Dtor(true); - - Plugin.Log.Warning("Tried to send a tell with content id being 0"); - return; - } - var ok = SendTellNative(networkModule, contentId, homeWorld, uName, encoded, (ushort) reason, homeWorld); if (ok == 1) - { PrintTellNative(logModule, 33, uName, &decodedUtf8String, 0, contentId, homeWorld, 255, 0, 0); - } else - { Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error); - } encoded->Dtor(true); uName->Dtor(true); diff --git a/ChatTwo/GameFunctions/ChatBox.cs b/ChatTwo/GameFunctions/ChatBox.cs index a6f9b8c..86168da 100644 --- a/ChatTwo/GameFunctions/ChatBox.cs +++ b/ChatTwo/GameFunctions/ChatBox.cs @@ -4,7 +4,6 @@ using FFXIVClientStructs.FFXIV.Client.UI; namespace ChatTwo.GameFunctions; -// From: https://git.anna.lgbt/anna/XivCommon/src/branch/main/XivCommon/Functions/Chat.cs public unsafe class ChatBox { public static void SendMessageUnsafe(byte[] message) diff --git a/ChatTwo/GameFunctions/Context.cs b/ChatTwo/GameFunctions/Context.cs index fd3afde..aa64a5d 100755 --- a/ChatTwo/GameFunctions/Context.cs +++ b/ChatTwo/GameFunctions/Context.cs @@ -15,7 +15,7 @@ internal sealed unsafe class Context internal static void TryOn(uint itemId, byte stainId) { - AgentTryon.TryOn(0xFF, itemId, stainId, 0, 0); + AgentTryon.TryOn(0xFF, itemId, stainId); } internal static void LinkItem(uint itemId) @@ -40,6 +40,6 @@ internal sealed unsafe class Context internal static void SearchForItem(uint itemId) { - ItemFinderModule.Instance()->SearchForItem(itemId, true); + ItemFinderModule.Instance()->SearchForItem(itemId); } } diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index 272f65d..fdf9ee2 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -2,7 +2,6 @@ using System.Globalization; using System.Runtime.InteropServices; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Game.Text.SeStringHandling.Payloads; using Dalamud.Hooking; using Dalamud.Memory; using Dalamud.Utility; @@ -15,7 +14,6 @@ using FFXIVClientStructs.FFXIV.Client.UI.Info; using FFXIVClientStructs.FFXIV.Component.GUI; using Lumina.Excel; using Lumina.Excel.Sheets; - using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; namespace ChatTwo.GameFunctions; @@ -75,10 +73,9 @@ internal unsafe class GameFunctions : IDisposable private void ListCommand(string name, ushort world, string commandName) { - var row = Plugin.DataManager.GetExcelSheet().GetRow(world); + var worldRow = Sheets.WorldSheet.GetRow(world); - var worldName = row.Name.ExtractText(); - ReplacementName = $"{name}@{worldName}"; + ReplacementName = $"{name}@{worldRow.Name.ToString()}"; ChatBox.SendMessage($"/{commandName} add {Placeholder}"); } @@ -191,7 +188,7 @@ internal unsafe class GameFunctions : IDisposable internal static void OpenQuestLog(RowRef quest) { - var splits = quest.Value.Id.ExtractText().Split("_"); + var splits = quest.Value.Id.ToString().Split("_"); if (splits.Length != 2) { Plugin.ChatGui.Print("QuestId is wrongly formatted"); diff --git a/ChatTwo/GameFunctions/Types/TellTarget.cs b/ChatTwo/GameFunctions/Types/TellTarget.cs index 9d03fb4..894bf05 100755 --- a/ChatTwo/GameFunctions/Types/TellTarget.cs +++ b/ChatTwo/GameFunctions/Types/TellTarget.cs @@ -17,6 +17,6 @@ internal sealed class TellTarget public bool IsSet() => Name.Length > 0 && World > 0; - public string ToWorldString() => Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ExtractText() : string.Empty; + public string ToWorldString() => Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ToString() : string.Empty; public string ToTargetString() => $"{Name}@{ToWorldString()}"; } diff --git a/ChatTwo/Http/ServerCore.cs b/ChatTwo/Http/ServerCore.cs index c4b7b9c..8891ea0 100644 --- a/ChatTwo/Http/ServerCore.cs +++ b/ChatTwo/Http/ServerCore.cs @@ -1,5 +1,4 @@ using ChatTwo.Http.MessageProtocol; -using ChatTwo.Util; using Dalamud.Plugin.Services; namespace ChatTwo.Http; @@ -25,7 +24,7 @@ public class ServerCore : IAsyncDisposable private void FrameworkUpdate(IFramework _) { - foreach (var (tab, idx) in Plugin.Config.Tabs.WithIndex()) + foreach (var (idx, tab) in Plugin.Config.Tabs.Index()) { if (tab.Unread == tab.LastSendUnread) continue; diff --git a/ChatTwo/Ipc/TypingIpc.cs b/ChatTwo/Ipc/TypingIpc.cs index 73799c0..4cd3a3c 100644 --- a/ChatTwo/Ipc/TypingIpc.cs +++ b/ChatTwo/Ipc/TypingIpc.cs @@ -28,19 +28,16 @@ internal sealed class TypingIpc : IDisposable private ChatInputState BuildState() { var log = Plugin.ChatLogWindow; - var chat = log.Chat ?? string.Empty; - var hasText = !string.IsNullOrWhiteSpace(chat); - var usedChannel = Plugin.CurrentTab?.CurrentChannel; - var inputChannel = usedChannel is not null - ? (usedChannel.UseTempChannel ? usedChannel.TempChannel : usedChannel.Channel) - : InputChannel.Invalid; + + var usedChannel = Plugin.CurrentTab.CurrentChannel; + var inputChannel = usedChannel.UseTempChannel ? usedChannel.TempChannel : usedChannel.Channel; var channelType = inputChannel.ToChatType(); return (InputVisible: !log.IsHidden, InputFocused: log.InputFocused, - HasText: hasText, - IsTyping: log.InputFocused && hasText, - TextLength: chat.Length, + HasText: log.Chat.Length > 0, + IsTyping: log is { InputFocused: true, Chat.Length: > 0 }, + TextLength: log.Chat.Length, ChannelType: channelType); } diff --git a/ChatTwo/Message.cs b/ChatTwo/Message.cs index f59e909..7a53b81 100755 --- a/ChatTwo/Message.cs +++ b/ChatTwo/Message.cs @@ -7,7 +7,6 @@ using System.Text.RegularExpressions; using Dalamud.Game.Text; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI.Agent; -using Lumina.Excel.Sheets; namespace ChatTwo; @@ -234,7 +233,7 @@ internal partial class Message AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, sentenceBuilder.Append(!wordUsed ? word : "").ToString())); try { - AddChunkWithMessage(text.NewWithStyle(chunk.Source, UriPayload.ResolveURI(token.Value), token.Value)); + AddChunkWithMessage(text.NewWithStyle(chunk.Source, UriPayload.ResolveUri(token.Value), token.Value)); } catch (UriFormatException) { @@ -326,8 +325,8 @@ internal partial class Message }; var name = kind != ItemKind.EventItem - ? Plugin.DataManager.GetExcelSheet().GetRow(item.ItemId).Name.ToString() - : Plugin.DataManager.GetExcelSheet().GetRow(item.ItemId).Name.ToString(); + ? Sheets.ItemSheet.GetRow(item.ItemId).Name.ToString() + : Sheets.EventItemSheet.GetRow(item.ItemId).Name.ToString(); var link = new ItemPayload(item.ItemId, kind, $"{SeIconChar.LinkMarker.ToIconChar()}{name}"); AddChunkWithMessage(text.NewWithStyle(chunk.Source, link, link.DisplayName ?? "Unknown")); @@ -341,7 +340,7 @@ internal partial class Message continue; } - var nameValue = statusRow.Name.ToDalamudString().TextValue; + var nameValue = statusRow.Name.ToString(); var content = statusRow.StatusCategory switch { 1 => $"{SeIconChar.Buff.ToIconString()}{nameValue}", diff --git a/ChatTwo/MessageManager.cs b/ChatTwo/MessageManager.cs index 3292550..c1300ea 100644 --- a/ChatTwo/MessageManager.cs +++ b/ChatTwo/MessageManager.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Diagnostics; +using System.Text; using ChatTwo.Code; using ChatTwo.Resources; using ChatTwo.Util; @@ -10,7 +11,9 @@ using Dalamud.Interface.ImGuiNotification; using Dalamud.Plugin.Services; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI.Misc; -using Lumina.Excel.Sheets; +using Lumina.Text.Expressions; +using Lumina.Text.Payloads; +using Lumina.Text.ReadOnly; namespace ChatTwo; @@ -21,7 +24,7 @@ internal class MessageManager : IAsyncDisposable private Plugin Plugin { get; } internal MessageStore Store { get; } - private Dictionary Formats { get; } = new(); + private Dictionary Formats { get; } = []; private ulong LastContentId { get; set; } // Messages go into the PendingSync queue first, which will be consumed one @@ -31,8 +34,8 @@ internal class MessageManager : IAsyncDisposable // After that, the message is enqueued in the PendingAsync queue, which will // be consumed in a separate thread and perform more processing (emotes, // URLs) as well as inserting the message into the database. - private Queue PendingSync { get; } = new(); - private ConcurrentQueue PendingAsync { get; } = new(); + private Queue PendingSync { get; } = []; + private ConcurrentQueue PendingAsync { get; } = []; private readonly Thread PendingMessageThread; private readonly CancellationTokenSource PendingThreadCancellationToken = new(); @@ -298,36 +301,38 @@ internal class MessageManager : IAsyncDisposable } } - private NameFormatting? FormatFor(ChatType type) + private NameFormatting FormatFor(ChatType type) { if (Formats.TryGetValue(type, out var cached)) return cached; - var logKind = Plugin.DataManager.GetExcelSheet().GetRow((ushort)type); - var format = logKind.Format.ToDalamudString(); - - static bool IsStringParam(Payload payload, byte num) + var formats = Sheets.LogKindSheet.GetRow((uint)type).Format.ToList(); + static bool IsStringParam(ReadOnlySePayload payload, byte num) { - var data = payload.Encode(); - return data.Length >= 5 && data[1] == 0x29 && data[4] == num + 1; + if (payload.MacroCode != MacroCode.String) + return false; + + return payload.TryGetExpression(out var expr1) + && expr1.TryGetParameterExpression(out var expressionType, out var operand) + && expressionType == (byte)ExpressionType.LocalString + && operand.TryGetInt(out var lstrIndex) + && lstrIndex == num; } - var firstStringParam = format.Payloads.FindIndex(payload => IsStringParam(payload, 1)); - var secondStringParam = format.Payloads.FindIndex(payload => IsStringParam(payload, 2)); + var firstStringParam = formats.FindIndex(payload => IsStringParam(payload, 1)); + var secondStringParam = formats.FindIndex(payload => IsStringParam(payload, 2)); if (firstStringParam == -1 || secondStringParam == -1) return NameFormatting.Empty(); - var before = format.Payloads + var before = formats .GetRange(0, firstStringParam) - .Where(payload => payload is ITextProvider) - .Cast() - .Select(text => text.Text); - var after = format.Payloads + .Where(payload => payload.Type == ReadOnlySePayloadType.Text) + .Select(text => Encoding.UTF8.GetString(text.Body.Span)); + var after = formats .GetRange(firstStringParam + 1, secondStringParam - firstStringParam) - .Where(payload => payload is ITextProvider) - .Cast() - .Select(text => text.Text); + .Where(payload => payload.Type == ReadOnlySePayloadType.Text) + .Select(text => Encoding.UTF8.GetString(text.Body.Span)); // Can't use `ToString()` as it defaults to macro var nameFormatting = NameFormatting.Of(string.Join("", before), string.Join("", after)); Formats[type] = nameFormatting; diff --git a/ChatTwo/PayloadHandler.cs b/ChatTwo/PayloadHandler.cs index 52f4031..41b1df2 100755 --- a/ChatTwo/PayloadHandler.cs +++ b/ChatTwo/PayloadHandler.cs @@ -20,7 +20,6 @@ using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; using Dalamud.Bindings.ImGui; using Lumina.Excel.Sheets; - using Action = System.Action; using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload; using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload; @@ -39,7 +38,7 @@ public sealed class PayloadHandler public uint HoverCounter; public uint LastHoverCounter; - private const uint PopupSfx = 1u; + private const uint PopupSfx = 1; internal PayloadHandler(ChatLogWindow logWindow) { @@ -110,7 +109,7 @@ public sealed class PayloadHandler var contentId = chunk.Message?.ContentId ?? 0; var sender = chunk.Message?.Sender.Select(c => c.Link).FirstOrDefault(p => p is PlayerPayload) as PlayerPayload; - using var menu = ImGuiUtil.Menu(Language.Context_Integrations); + using var menu = ImRaii.Menu(Language.Context_Integrations); if (!menu.Success) return; @@ -146,7 +145,7 @@ public sealed class PayloadHandler // // It makes it much more convenient in the majority of cases to // copy the message content without having to open a submenu. - menu = ImGuiUtil.Menu(Plugin.PluginName); + menu = ImRaii.Menu(Plugin.PluginName); if (!menu.Success) return; } @@ -240,7 +239,7 @@ public sealed class PayloadHandler DoHover(() => HoverItem(item), hoverSize); break; case UriPayload uri: - DoHover(() => HoverURI(uri), hoverSize); + DoHover(() => HoverUri(uri), hoverSize); break; } } @@ -250,16 +249,14 @@ public sealed class PayloadHandler ImGui.SetNextWindowSize(new Vector2(width, -1f)); using (ImRaii.Tooltip()) - using (ImGuiUtil.TextWrapPos()) + using (ImRaii.TextWrapPos(0.0f)) using (ImRaii.PushColor(ImGuiCol.Text, LogWindow.DefaultText)) - { inside(); - } } public unsafe void MoveTooltip(AddonEvent type, AddonArgs args) { - // Only move if user has "Next to Cursor" option selected + // Only move if the user has the "Next to Cursor" option selected if (!Plugin.GameConfig.TryGet(UiControlOption.DetailTrackingType, out uint selected) || selected != 0) return; @@ -352,7 +349,7 @@ public sealed class PayloadHandler InlineIcon(icon); var builder = new SeStringBuilder(); - var nameValue = status.Status.Value.Name.ToDalamudString().TextValue; + var nameValue = status.Status.Value.Name.ToString(); switch (status.Status.Value.StatusCategory) { case 1: @@ -364,14 +361,13 @@ public sealed class PayloadHandler default: builder.AddUiForeground(nameValue, 1); break; - }; + } var name = ChunkUtil.ToChunks(builder.BuiltString, ChunkSource.None, null); LogWindow.DrawChunks(name.ToList()); ImGui.Separator(); - var descString = status.Status.Value.Description.ToDalamudString(); - var desc = ChunkUtil.ToChunks(descString, ChunkSource.None, null); + var desc = ChunkUtil.ToChunks(status.Status.Value.Description.ToDalamudString(), ChunkSource.None, null); LogWindow.DrawChunks(desc.ToList()); } @@ -383,7 +379,9 @@ public sealed class PayloadHandler return; } - item.Item.TryGetValue(out Item resolvedItem); + if (!item.Item.TryGetValue(out Item resolvedItem)) + return; + if (Plugin.TextureProvider.GetFromGameIcon(new GameIconLookup(resolvedItem.Icon, item.IsHQ)).GetWrapOrDefault() is { } icon) InlineIcon(icon); @@ -413,7 +411,7 @@ public sealed class PayloadHandler LogWindow.DrawChunks(ChunkUtil.ToChunks(itemHelpRow.Description.ToDalamudString(), ChunkSource.None, null).ToList()); } - private void HoverURI(UriPayload uri) + private void HoverUri(UriPayload uri) { ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority)); ImGuiUtil.WarningText(Language.Context_URLWarning); @@ -449,7 +447,7 @@ public sealed class PayloadHandler GameFunctions.GameFunctions.OpenPartyFinder(); break; case UriPayload uri: - WrapperUtil.TryOpenURI(uri.Uri); + WrapperUtil.TryOpenUri(uri.Uri); break; default: RightClickPayload(chunk, payload); @@ -552,8 +550,7 @@ public sealed class PayloadHandler if (Plugin.TextureProvider.GetFromGameIcon(new GameIconLookup(item.Icon)).GetWrapOrDefault() is { } icon) InlineIcon(icon); - var name = item.Name.ToDalamudString(); - LogWindow.DrawChunks(ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(), false); + LogWindow.DrawChunks(ChunkUtil.ToChunks(item.Name.ToDalamudString(), ChunkSource.None, null).ToList(), false); ImGui.Separator(); var realItemId = payload.RawItemId; @@ -561,12 +558,13 @@ public sealed class PayloadHandler GameFunctions.Context.LinkItem(realItemId); if (ImGui.Selectable(Language.Context_CopyItemName)) - ImGui.SetClipboardText(name.TextValue); + ImGui.SetClipboardText(item.Name.ToString()); } private void DrawPlayerPopup(Chunk chunk, PlayerPayload player) { // Possible that GMs return a null payload + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (player == null) return; @@ -627,7 +625,7 @@ public sealed class PayloadHandler } else if (!inInstance) { - using var menu = ImGuiUtil.Menu(Language.Context_InviteToParty); + using var menu = ImRaii.Menu(Language.Context_InviteToParty); if (menu.Success) { if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld)) @@ -653,7 +651,7 @@ public sealed class PayloadHandler if (!isFriend && ImGui.Selectable(Language.Context_SendFriendRequest)) LogWindow.Plugin.Functions.SendFriendRequest(player.PlayerName, (ushort) world.RowId); - using (var menuBlockFunctions = ImGuiUtil.Menu(Language.Context_BlockFunctions)) + using (var menuBlockFunctions = ImRaii.Menu(Language.Context_BlockFunctions)) { if (menuBlockFunctions.Success) { @@ -718,7 +716,7 @@ public sealed class PayloadHandler ImGui.Separator(); if (ImGui.Selectable(Language.Context_OpenInBrowser)) - WrapperUtil.TryOpenURI(uri.Uri); + WrapperUtil.TryOpenUri(uri.Uri); if (ImGui.Selectable(Language.Context_CopyLink)) { @@ -733,7 +731,7 @@ public sealed class PayloadHandler InlineIcon(icon); var builder = new SeStringBuilder(); - var nameValue = status.Status.Value.Name.ToDalamudString().TextValue; + var nameValue = status.Status.Value.Name.ToString(); switch (status.Status.Value.StatusCategory) { case 1: diff --git a/ChatTwo/Plugin.cs b/ChatTwo/Plugin.cs index dddc032..442741d 100755 --- a/ChatTwo/Plugin.cs +++ b/ChatTwo/Plugin.cs @@ -7,7 +7,6 @@ using ChatTwo.Resources; using ChatTwo.Ui; using ChatTwo.Util; using Dalamud.Game.ClientState.Conditions; -using Dalamud.Game.ClientState.Objects; using Dalamud.Interface.Windowing; using Dalamud.IoC; using Dalamud.Plugin; @@ -40,6 +39,7 @@ public sealed class Plugin : IDalamudPlugin [PluginService] internal static INotificationManager Notification { get; private set; } = null!; [PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!; [PluginService] internal static IPlayerState PlayerState { get; private set; } = null!; + [PluginService] internal static ISeStringEvaluator Evaluator { get; private set; } = null!; internal static Configuration Config = null!; @@ -96,7 +96,7 @@ public sealed class Plugin : IDalamudPlugin // Functions calls this in its ctor if the player is already logged in ServerCore = new ServerCore(this); - Commands = new Commands(this); + Commands = new Commands(); Functions = new GameFunctions.GameFunctions(this); Ipc = new IpcManager(); TypingIpc = new TypingIpc(this); @@ -202,7 +202,7 @@ public sealed class Plugin : IDalamudPlugin if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas]) { ChatLogWindow.FinalizeFrame(); - TypingIpc?.Update(); + TypingIpc.Update(); return; } @@ -212,12 +212,10 @@ public sealed class Plugin : IDalamudPlugin ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text]; using ((Config.FontsEnabled ? FontManager.RegularFont : FontManager.Axis).Push()) - { WindowSystem.Draw(); - } ChatLogWindow.FinalizeFrame(); - TypingIpc?.Update(); + TypingIpc.Update(); } internal void SaveConfig() diff --git a/ChatTwo/Resources/Language.de.resx b/ChatTwo/Resources/Language.de.resx index c2a7312..cad22e2 100644 --- a/ChatTwo/Resources/Language.de.resx +++ b/ChatTwo/Resources/Language.de.resx @@ -402,7 +402,7 @@ Sie wurden gewarnt. {0} ist ein Projekt, dessen Ziel es ist, den Spielchat zu verbessern und neu zu gestalten. - Drücken Sie auf den linken Knopf, um bei der Übersetzung von {0} zu helfen. + Helfen Sie bei der Übersetzung: Übersetzer diff --git a/ChatTwo/Resources/Language.fr.resx b/ChatTwo/Resources/Language.fr.resx index 8cb8755..4382358 100644 --- a/ChatTwo/Resources/Language.fr.resx +++ b/ChatTwo/Resources/Language.fr.resx @@ -401,7 +401,7 @@ {0} est un projet visant à recréer complètement le chat en jeu et à l'améliorer. - Cliquez sur le bouton à gauche pour nous aider à traduire {0}. + Help to translate: Traducteurs @@ -542,7 +542,7 @@ Opacité - Use different hide condition than main window + Utiliser une condition de masquage différente de la fenêtre principale Activer les fontes personnalisées @@ -680,7 +680,7 @@ Inviter dans l'équipe - Block Functions + Fonctions de blocage Même monde @@ -701,10 +701,10 @@ Ajouter à la liste noire - Register to Mute List + Ajouter à la Liste Muette - Add to Term Filters + Ajouter aux filtres des termes Inviter dans le réseau des novices @@ -842,7 +842,7 @@ Notifications d'alarme - Glamour Notifications + Notification de Glamour Écho @@ -965,10 +965,10 @@ Remplace les messages identiques consécutifs par un compteur ajouté à la première instance des dits messages. - Keep unique links seperate + Garder les liens uniques séparés - Don't collapse messages if they link to different things with the same text. + Ne pas réduire les messages s'ils sont liés à des choses différentes avec le même texte. Canaux d'ExtraChat diff --git a/ChatTwo/Resources/Language.ja.resx b/ChatTwo/Resources/Language.ja.resx index 423e1ed..e9494fb 100644 --- a/ChatTwo/Resources/Language.ja.resx +++ b/ChatTwo/Resources/Language.ja.resx @@ -827,7 +827,7 @@ 回復 - + Beneficial effects granted Detrimental effects inflicted @@ -893,10 +893,10 @@ PT募集通知 - + Sign Messages for PC Targets - + Random Number Messages ビギナーチャンネルアナウンス diff --git a/ChatTwo/Resources/Language.nl.resx b/ChatTwo/Resources/Language.nl.resx index 34922a3..21ee50f 100644 --- a/ChatTwo/Resources/Language.nl.resx +++ b/ChatTwo/Resources/Language.nl.resx @@ -401,7 +401,7 @@ {0} is een project om de in-game chat volledig opnieuw te maken en nog beter te maken. - Klik op de knop links om {0} te helpen vertalen. + Help to translate: Vertalers diff --git a/ChatTwo/Resources/Language.ro.resx b/ChatTwo/Resources/Language.ro.resx index 28fa0d2..e54073a 100644 --- a/ChatTwo/Resources/Language.ro.resx +++ b/ChatTwo/Resources/Language.ro.resx @@ -401,7 +401,7 @@ {0} este un proiect care recreează complet chat-ul din joc si îl îmbunătățește. - Apasă butonul din stânga pentru a contribui la traducerea {0}. + Help to translate: Traducători diff --git a/ChatTwo/Resources/Language.ru.resx b/ChatTwo/Resources/Language.ru.resx index 7c11a63..6d4a727 100644 --- a/ChatTwo/Resources/Language.ru.resx +++ b/ChatTwo/Resources/Language.ru.resx @@ -401,7 +401,7 @@ {0} - проект, полностью пересоздаёт внутриигровой чат и делает его еще лучше. - Нажмите кнопку слева, чтобы помочь перевести {0}. + Help to translate: Переводчики diff --git a/ChatTwo/Resources/Language.sv.resx b/ChatTwo/Resources/Language.sv.resx index f300ff1..33f9692 100644 --- a/ChatTwo/Resources/Language.sv.resx +++ b/ChatTwo/Resources/Language.sv.resx @@ -395,13 +395,13 @@ Ny flik - Om {0} + Om {0} är ett projekt gjort för att fullt återskapa spelets ursprungliga chattfönster och göra det ännu bättre. - Klicka på knappen till vänster för att hjälpa med översättningen av {0}. + Help to translate: Översättare diff --git a/ChatTwo/Resources/Language.zh-Hant.resx b/ChatTwo/Resources/Language.zh-Hant.resx index 1fdab46..2cdcc14 100644 --- a/ChatTwo/Resources/Language.zh-Hant.resx +++ b/ChatTwo/Resources/Language.zh-Hant.resx @@ -402,7 +402,7 @@ 繁體中文是為了不開補全功能也能與其他玩家正常顯示溝通所翻譯及修改。 - 點選左邊的按鈕來幇助翻譯 {0}。 + Help to translate: 翻譯人員 diff --git a/ChatTwo/Sheets.cs b/ChatTwo/Sheets.cs index 0954191..3823ce0 100644 --- a/ChatTwo/Sheets.cs +++ b/ChatTwo/Sheets.cs @@ -8,6 +8,8 @@ public static class Sheets public static readonly ExcelSheet ItemSheet; public static readonly ExcelSheet WorldSheet; public static readonly ExcelSheet StatusSheet; + public static readonly ExcelSheet UIColorSheet; + public static readonly ExcelSheet LogKindSheet; public static readonly ExcelSheet LogFilterSheet; public static readonly ExcelSheet EventItemSheet; public static readonly ExcelSheet CompletionSheet; @@ -20,8 +22,10 @@ public static class Sheets ItemSheet = Plugin.DataManager.GetExcelSheet(); WorldSheet = Plugin.DataManager.GetExcelSheet(); StatusSheet = Plugin.DataManager.GetExcelSheet(); - EventItemSheet = Plugin.DataManager.GetExcelSheet(); + UIColorSheet = Plugin.DataManager.GetExcelSheet(); + LogKindSheet = Plugin.DataManager.GetExcelSheet(); LogFilterSheet = Plugin.DataManager.GetExcelSheet(); + EventItemSheet = Plugin.DataManager.GetExcelSheet(); CompletionSheet = Plugin.DataManager.GetExcelSheet(); TerritorySheet = Plugin.DataManager.GetExcelSheet(); TextCommandSheet = Plugin.DataManager.GetExcelSheet(); diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index b54c695..c74e4ba 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -20,6 +20,7 @@ using Dalamud.Memory; using FFXIVClientStructs.FFXIV.Client.UI; using Dalamud.Bindings.ImGui; using Lumina.Excel.Sheets; +using Lumina.Extensions; namespace ChatTwo.Ui; @@ -68,7 +69,7 @@ public sealed class ChatLogWindow : Window public PayloadHandler PayloadHandler { get; } internal Lender HandlerLender { get; } private Dictionary TextCommandChannels { get; } = new(); - private HashSet AllCommands { get; } = []; + private Dictionary AllCommands { get; } = []; private const uint ChatOpenSfx = 35u; private const uint ChatCloseSfx = 3u; @@ -238,7 +239,7 @@ public sealed class ChatLogWindow : Window private bool IsValidCommand(string command) { - return Plugin.CommandManager.Commands.ContainsKey(command) || AllCommands.Contains(command); + return Plugin.CommandManager.Commands.ContainsKey(command) || AllCommands.ContainsKey(command); } private void ClearLog(string command, string arguments) @@ -288,7 +289,7 @@ public sealed class ChatLogWindow : Window foreach (var input in Enum.GetValues()) { - var commands = input.TextCommands(Plugin.DataManager); + var commands = input.TextCommands(); if (commands == null) continue; @@ -297,11 +298,8 @@ public sealed class ChatLogWindow : Window AddTextCommandChannel(command, type); } - if (Sheets.TextCommandSheet.HasRow(116)) - { - var echo = Sheets.TextCommandSheet.GetRow(116); - AddTextCommandChannel(echo, ChatType.Echo); - } + if (Sheets.TextCommandSheet.TryGetRow(116, out var row)) + AddTextCommandChannel(row, ChatType.Echo); } private void AddTextCommandChannel(TextCommand command, ChatType type) @@ -314,19 +312,20 @@ public sealed class ChatLogWindow : Window private void SetUpAllCommands() { - if (Plugin.DataManager.GetExcelSheet() is not { } commands) - return; - - var commandNames = commands.SelectMany(cmd => new[] + foreach (var command in Sheets.TextCommandSheet) { - cmd.Command.ExtractText(), - cmd.ShortCommand.ExtractText(), - cmd.Alias.ExtractText(), - cmd.ShortAlias.ExtractText(), - }); + if (!command.Command.IsEmpty) + AllCommands.TryAdd(command.Command.ToString(), command); - foreach (var command in commandNames) - AllCommands.Add(command); + if (!command.ShortCommand.IsEmpty) + AllCommands.TryAdd(command.ShortCommand.ToString(), command); + + if (!command.Alias.IsEmpty) + AllCommands.TryAdd(command.Alias.ToString(), command); + + if (!command.ShortAlias.IsEmpty) + AllCommands.TryAdd(command.ShortAlias.ToString(), command); + } } private void AddBacklog(string message) @@ -736,7 +735,7 @@ public sealed class ChatLogWindow : Window if (!channel.IsValid()) continue; - var name = Sheets.LogFilterSheet.FirstOrNull(row => row.LogKind == (byte) channel.ToChatType())?.Name.ExtractText() ?? channel.ToChatType().Name(); + var name = Sheets.LogFilterSheet.FirstOrNull(row => row.LogKind == (byte) channel.ToChatType())?.Name.ToString() ?? channel.ToChatType().Name(); if (channel.IsLinkshell()) { var lsName = Plugin.Functions.Chat.GetLinkshellName(channel.LinkshellIndex()); @@ -933,13 +932,19 @@ public sealed class ChatLogWindow : Window AddBacklog(trimmed); InputBacklogIdx = -1; + if (HasTranslationCommand(trimmed)) + { + activeTab.CurrentChannel.ResetTempChannel(); + Chat = string.Empty; + return; + } + if (TellSpecial) { var tellBytes = Encoding.UTF8.GetBytes(trimmed); AutoTranslate.ReplaceWithPayload(ref tellBytes); Plugin.Functions.Chat.SendTellUsingCommandInner(tellBytes); - TellSpecial = false; activeTab.CurrentChannel.ResetTempChannel(); @@ -1000,6 +1005,18 @@ public sealed class ChatLogWindow : Window Chat = string.Empty; } + private bool HasTranslationCommand(string trimmed) + { + var messageBytes = Encoding.UTF8.GetBytes(trimmed); + if (AutoTranslate.StartsWithCommand(ref messageBytes)) + { + ChatBox.SendMessageUnsafe(messageBytes); + return true; + } + + return false; + } + internal void UserHide() { CurrentHideState = HideState.User; @@ -1492,10 +1509,11 @@ public sealed class ChatLogWindow : Window var button = (i + 1) % 10; var text = string.Format(Language.AutoTranslate_Completion_Key, button); var size = ImGui.CalcTextSize(text); + ImGui.SameLine(ImGui.GetContentRegionAvail().X - size.X); - ImGui.PushStyleColor(ImGuiCol.Text, *ImGui.GetStyleColorVec4(ImGuiCol.TextDisabled)); - ImGui.TextUnformatted(text); - ImGui.PopStyleColor(); + + using (ImRaii.PushColor(ImGuiCol.Text, ImGui.GetStyle().Colors[(int)ImGuiCol.TextDisabled])) + ImGui.TextUnformatted(text); } if (!clicked) @@ -1632,12 +1650,10 @@ public sealed class ChatLogWindow : Window if (text.StartsWith('/')) { var command = text.Split(' ')[0]; - var cmd = Sheets.TextCommandSheet.FirstOrNull(cmd => - cmd.Command.ExtractText() == command || cmd.Alias.ExtractText() == command || - cmd.ShortCommand.ExtractText() == command || cmd.ShortAlias.ExtractText() == command); - - if (cmd != null) - Plugin.CommandHelpWindow.UpdateContent(cmd.Value); + if (AllCommands.TryGetValue(command, out var textCommand)) + Plugin.CommandHelpWindow.UpdateContent(textCommand.Description); + else if (Plugin.CommandManager.Commands.TryGetValue(command, out var info) && info.ShowInHelp) + Plugin.CommandHelpWindow.UpdateContent(info.HelpMessage); } if (data.EventFlag != ImGuiInputTextFlags.CallbackHistory) diff --git a/ChatTwo/Ui/CommandHelpWindow.cs b/ChatTwo/Ui/CommandHelpWindow.cs index 6d93884..16d5bdd 100644 --- a/ChatTwo/Ui/CommandHelpWindow.cs +++ b/ChatTwo/Ui/CommandHelpWindow.cs @@ -4,15 +4,15 @@ using Dalamud.Interface.Utility; using Dalamud.Interface.Windowing; using Dalamud.Utility; using Dalamud.Bindings.ImGui; -using Lumina.Excel.Sheets; +using Lumina.Text.ReadOnly; namespace ChatTwo.Ui; public class CommandHelpWindow : Window { private ChatLogWindow LogWindow { get; } - private TextCommand? Command { get; set; } + private ReadOnlySeString? CommandDescription { get; set; } - internal CommandHelpWindow(ChatLogWindow logWindow) : base($"command help##chat2-commandhelp") + internal CommandHelpWindow(ChatLogWindow logWindow) : base("command help##chat2-commandhelp") { LogWindow = logWindow; @@ -24,9 +24,9 @@ public class CommandHelpWindow : Window { } // Sets IsOpen to true if it should be drawn - public void UpdateContent(TextCommand command) + public void UpdateContent(ReadOnlySeString commandDesc) { - Command = command; + CommandDescription = commandDesc; var width = 350; var scaledWidth = width * ImGuiHelpers.GlobalScale; @@ -56,9 +56,9 @@ public class CommandHelpWindow : Window { public override void Draw() { - if (Command == null) + if (CommandDescription == null) return; - LogWindow.DrawChunks(ChunkUtil.ToChunks(Command.Value.Description.ToDalamudString(), ChunkSource.None, null).ToList()); + LogWindow.DrawChunks(ChunkUtil.ToChunks(CommandDescription.Value.ToDalamudString(), ChunkSource.None, null).ToList()); } } diff --git a/ChatTwo/Ui/SettingsTabs/About.cs b/ChatTwo/Ui/SettingsTabs/About.cs index d6a5b0e..c3432dc 100755 --- a/ChatTwo/Ui/SettingsTabs/About.cs +++ b/ChatTwo/Ui/SettingsTabs/About.cs @@ -33,7 +33,7 @@ internal sealed class About : ISettingsTab public void Draw(bool changed) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName)); diff --git a/ChatTwo/Ui/SettingsTabs/Changelog.cs b/ChatTwo/Ui/SettingsTabs/Changelog.cs index 6df74c7..8ee43a7 100644 --- a/ChatTwo/Ui/SettingsTabs/Changelog.cs +++ b/ChatTwo/Ui/SettingsTabs/Changelog.cs @@ -18,7 +18,7 @@ internal sealed class Changelog : ISettingsTab public void Draw(bool changed) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGui.TextUnformatted(Language.Options_Warning_NotImplemented); ImGuiUtil.OptionCheckbox(ref Mutable.PrintChangelog, Language.Options_PrintChangelog_Name, Language.Options_PrintChangelog_Description); diff --git a/ChatTwo/Ui/SettingsTabs/ChatLog.cs b/ChatTwo/Ui/SettingsTabs/ChatLog.cs index 069fde8..b8bb944 100644 --- a/ChatTwo/Ui/SettingsTabs/ChatLog.cs +++ b/ChatTwo/Ui/SettingsTabs/ChatLog.cs @@ -21,7 +21,7 @@ internal sealed class ChatLog : ISettingsTab public void Draw(bool changed) { - using (ImGuiUtil.TextWrapPos()) + using (ImRaii.TextWrapPos(0.0f)) { ImGuiUtil.OptionCheckbox(ref Mutable.KeepInputFocus, Language.Options_KeepInputFocus_Name, Language.Options_KeepInputFocus_Description); ImGui.Spacing(); diff --git a/ChatTwo/Ui/SettingsTabs/Database.cs b/ChatTwo/Ui/SettingsTabs/Database.cs index 219f865..a6e3610 100755 --- a/ChatTwo/Ui/SettingsTabs/Database.cs +++ b/ChatTwo/Ui/SettingsTabs/Database.cs @@ -138,7 +138,7 @@ internal sealed class Database : ISettingsTab return; using var treeNode = ImRaii.TreeNode(Language.Options_Database_Advanced); - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning); if (ImGuiUtil.CtrlShiftButton("Perform maintenance", "Ctrl+Shift: MessageManager.Store.PerformMaintenance()")) diff --git a/ChatTwo/Ui/SettingsTabs/Display.cs b/ChatTwo/Ui/SettingsTabs/Display.cs index f6980df..71da7e7 100755 --- a/ChatTwo/Ui/SettingsTabs/Display.cs +++ b/ChatTwo/Ui/SettingsTabs/Display.cs @@ -19,7 +19,7 @@ internal sealed class Display : ISettingsTab public void Draw(bool changed) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGuiUtil.OptionCheckbox(ref Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description); ImGui.Spacing(); diff --git a/ChatTwo/Ui/SettingsTabs/Emote.cs b/ChatTwo/Ui/SettingsTabs/Emote.cs index 4f43e63..64f2411 100644 --- a/ChatTwo/Ui/SettingsTabs/Emote.cs +++ b/ChatTwo/Ui/SettingsTabs/Emote.cs @@ -38,7 +38,7 @@ internal sealed class Emote : ISettingsTab public void Draw(bool changed) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGuiUtil.OptionCheckbox(ref Mutable.ShowEmotes, Language.Options_ShowEmotes_Name, Language.Options_ShowEmotes_Desc); ImGui.Spacing(); diff --git a/ChatTwo/Ui/SettingsTabs/Fonts.cs b/ChatTwo/Ui/SettingsTabs/Fonts.cs index b1343d1..be09180 100755 --- a/ChatTwo/Ui/SettingsTabs/Fonts.cs +++ b/ChatTwo/Ui/SettingsTabs/Fonts.cs @@ -3,6 +3,7 @@ using ChatTwo.Util; using Dalamud; using Dalamud.Interface.FontIdentifier; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility.Raii; namespace ChatTwo.Ui.SettingsTabs; @@ -19,7 +20,7 @@ public class Fonts : ISettingsTab public void Draw(bool _) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled); ImGui.Spacing(); diff --git a/ChatTwo/Ui/SettingsTabs/Preview.cs b/ChatTwo/Ui/SettingsTabs/Preview.cs index cfa0e1a..8cc0119 100644 --- a/ChatTwo/Ui/SettingsTabs/Preview.cs +++ b/ChatTwo/Ui/SettingsTabs/Preview.cs @@ -1,6 +1,7 @@ using ChatTwo.Resources; using ChatTwo.Util; using Dalamud.Bindings.ImGui; +using Dalamud.Interface.Utility.Raii; namespace ChatTwo.Ui.SettingsTabs; @@ -17,7 +18,7 @@ internal sealed class Preview : ISettingsTab public void Draw(bool changed) { - using var wrap = ImGuiUtil.TextWrapPos(); + using var wrap = ImRaii.TextWrapPos(0.0f); using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Preview_Name, Mutable.PreviewPosition.Name())) { diff --git a/ChatTwo/Ui/SettingsTabs/Webinterface.cs b/ChatTwo/Ui/SettingsTabs/Webinterface.cs index 7f8ee74..8623e61 100644 --- a/ChatTwo/Ui/SettingsTabs/Webinterface.cs +++ b/ChatTwo/Ui/SettingsTabs/Webinterface.cs @@ -138,7 +138,7 @@ internal sealed class Webinterface(Plugin plugin, Configuration mutable) : ISett clicked |= ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "urlOpen"); if (clicked) - WrapperUtil.TryOpenURI(uri); + WrapperUtil.TryOpenUri(uri); } else { diff --git a/ChatTwo/Util/AutoTranslate.cs b/ChatTwo/Util/AutoTranslate.cs index 7b8ce7b..32fc37e 100644 --- a/ChatTwo/Util/AutoTranslate.cs +++ b/ChatTwo/Util/AutoTranslate.cs @@ -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; @@ -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 = " ToChunks(ReadOnlySeString msg, ChunkSource source, ChatType? defaultColour) + // { + // var chunks = new List(); + // + // var italic = false; + // var foreground = new Stack(); + // var glow = new Stack(); + // 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 ToChunks(SeString msg, ChunkSource source, ChatType? defaultColour) { var chunks = new List(); @@ -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; } } diff --git a/ChatTwo/Util/ColourUtil.cs b/ChatTwo/Util/ColourUtil.cs index a3e7722..c47f4d6 100755 --- a/ChatTwo/Util/ColourUtil.cs +++ b/ChatTwo/Util/ColourUtil.cs @@ -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); diff --git a/ChatTwo/Util/DatePicker.cs b/ChatTwo/Util/DatePicker.cs index 16e5acf..47d9442 100644 --- a/ChatTwo/Util/DatePicker.cs +++ b/ChatTwo/Util/DatePicker.cs @@ -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; } diff --git a/ChatTwo/Util/ExtraPayload.cs b/ChatTwo/Util/ExtraPayload.cs index 3c8481f..8ee60b8 100644 --- a/ChatTwo/Util/ExtraPayload.cs +++ b/ChatTwo/Util/ExtraPayload.cs @@ -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; diff --git a/ChatTwo/Util/ImGuiUtil.cs b/ChatTwo/Util/ImGuiUtil.cs index 6467a4b..384e9e5 100755 --- a/ChatTwo/Util/ImGuiUtil.cs +++ b/ChatTwo/Util/ImGuiUtil.cs @@ -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? 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()) + foreach (var vk in Enum.GetValues()) { 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 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() { } } diff --git a/ChatTwo/Util/MathUtil.cs b/ChatTwo/Util/MathUtil.cs index aca34d2..7009885 100644 --- a/ChatTwo/Util/MathUtil.cs +++ b/ChatTwo/Util/MathUtil.cs @@ -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 diff --git a/ChatTwo/Util/MemoryUtil.cs b/ChatTwo/Util/MemoryUtil.cs new file mode 100644 index 0000000..5454e60 --- /dev/null +++ b/ChatTwo/Util/MemoryUtil.cs @@ -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()); + } +} \ No newline at end of file diff --git a/ChatTwo/Util/Payloads.cs b/ChatTwo/Util/Payloads.cs index d1ec2fb..eb9d3c9 100755 --- a/ChatTwo/Util/Payloads.cs +++ b/ChatTwo/Util/Payloads.cs @@ -63,18 +63,18 @@ internal class UriPayload(Uri uri) : Payload /// /// If the URI is invalid, or if the scheme is not supported. /// - 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) diff --git a/ChatTwo/Util/TabsUtil.cs b/ChatTwo/Util/TabsUtil.cs index dda54c1..5373242 100755 --- a/ChatTwo/Util/TabsUtil.cs +++ b/ChatTwo/Util/TabsUtil.cs @@ -10,6 +10,7 @@ internal static class TabsUtil var channels = new Dictionary(); foreach (var chatType in Enum.GetValues()) 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() diff --git a/ChatTwo/Util/Tokenizer.cs b/ChatTwo/Util/Tokenizer.cs index 82f90ab..53901e6 100644 --- a/ChatTwo/Util/Tokenizer.cs +++ b/ChatTwo/Util/Tokenizer.cs @@ -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. /// - private static Regex URLRegex = new( + private static readonly Regex UrlRegex = new( @"(?((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 ); diff --git a/ChatTwo/Util/WrapperUtil.cs b/ChatTwo/Util/WrapperUtil.cs index bfb64bc..251fb5e 100644 --- a/ChatTwo/Util/WrapperUtil.cs +++ b/ChatTwo/Util/WrapperUtil.cs @@ -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(this IEnumerable list) - => list.Select((x, i) => (x, i)); - - /// Return the first object fulfilling the predicate or null for structs. - /// The enumerable. - /// The predicate. - /// The first object fulfilling the predicate, or a null-optional. - [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - public static T? FirstOrNull(this IEnumerable values, Func predicate) where T : struct - { - foreach(var val in values) - if (predicate(val)) - return val; - - return null; - } } \ No newline at end of file diff --git a/ChatTwo/packages.lock.json b/ChatTwo/packages.lock.json index d8dd85c..019ea19 100644 --- a/ChatTwo/packages.lock.json +++ b/ChatTwo/packages.lock.json @@ -4,9 +4,9 @@ "net10.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[14.0.1, )", - "resolved": "14.0.1", - "contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA==" + "requested": "[14.0.2, )", + "resolved": "14.0.2", + "contentHash": "dQJeq+8eyHzra4Cg5eZ/3LAeS3/UpvvLriYJGSncMK9LqJ7Q4B6jwcOsxo3PfxVd15xj+IzVFxkPqIBmPQu8/w==" }, "DotNet.ReproducibleBuilds": { "type": "Direct",