From 20ee6d1278e1d48805fa4b8ee862a8e16d42fa29 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 1 Feb 2022 19:54:17 -0500 Subject: [PATCH] feat: add option to import chat colours from game --- ChatTwo/GameFunctions/Chat.cs | 42 ++++++++++++++++++++++++++ ChatTwo/GameFunctions/GameFunctions.cs | 2 -- ChatTwo/Ui/Settings.cs | 2 +- ChatTwo/Ui/SettingsTabs/ChatColours.cs | 14 +++++++-- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 51b0f94..a1b5936 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -55,6 +55,9 @@ internal sealed unsafe class Chat : IDisposable { [Signature("E8 ?? ?? ?? ?? 4C 8B C0 FF C3")] private readonly delegate* unmanaged _getLinkshellName = null!; + [Signature("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9")] + private readonly delegate* unmanaged _getColourInfo = null!; + // Hooks private delegate byte ChatLogRefreshDelegate(IntPtr log, ushort eventId, AtkValue* value); @@ -105,9 +108,13 @@ internal sealed unsafe class Chat : IDisposable { #pragma warning restore 0649 // Pointers + [Signature("48 8D 15 ?? ?? ?? ?? 0F B6 C8 48 8D 05", ScanType = ScanType.StaticAddress)] private readonly char* _currentCharacter = null!; + [Signature("48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D", ScanType = ScanType.StaticAddress)] + private IntPtr ColourLookup { get; init; } + // Events internal delegate void ChatActivatedEventDelegate(ChatActivatedArgs args); @@ -192,6 +199,41 @@ internal sealed unsafe class Chat : IDisposable { return cycleFunc(uiModule, idx); } + // This function looks up a channel's user-defined colour. + // + // If this function would ever return 0, it returns null instead. + internal uint? GetChannelColour(ChatType type) { + if (this._getColourInfo == null || this.ColourLookup == IntPtr.Zero) { + return null; + } + + // Colours are retrieved by looking up their code in a lookup table. Some codes share a colour, so they're lumped into a parent code here. + // Only codes >= 10 (say) have configurable colours. + // After getting the lookup value for the code, it is passed into a function with a handler which returns a pointer. + // This pointer + 32 is the RGB value. This functions returns RGBA with A always max. + + var parent = new ChatCode((ushort) type).Parent(); + + switch (parent) { + case ChatType.Debug: + case ChatType.Urgent: + case ChatType.Notice: + return type.DefaultColour(); + } + + var framework = (IntPtr) Framework.Instance(); + + var lookupResult = *(uint*) (this.ColourLookup + (int) parent * 4); + var info = this._getColourInfo(framework + 16, lookupResult); + var rgb = *(uint*) (info + 32) & 0xFFFFFF; + + if (rgb == 0) { + return null; + } + + return 0xFF | (rgb << 8); + } + private readonly Dictionary _keybinds = new(); internal IReadOnlyDictionary Keybinds => this._keybinds; diff --git a/ChatTwo/GameFunctions/GameFunctions.cs b/ChatTwo/GameFunctions/GameFunctions.cs index 29398b2..532f64a 100755 --- a/ChatTwo/GameFunctions/GameFunctions.cs +++ b/ChatTwo/GameFunctions/GameFunctions.cs @@ -50,8 +50,6 @@ internal unsafe class GameFunctions : IDisposable { #pragma warning restore 0649 - internal const int HqItemOffset = 1_000_000; - private Plugin Plugin { get; } internal Party Party { get; } internal Chat Chat { get; } diff --git a/ChatTwo/Ui/Settings.cs b/ChatTwo/Ui/Settings.cs index 218bf3e..50ca1f8 100755 --- a/ChatTwo/Ui/Settings.cs +++ b/ChatTwo/Ui/Settings.cs @@ -19,7 +19,7 @@ internal sealed class Settings : IUiComponent { this.Tabs = new List { new Display(this.Mutable), - new ChatColours(this.Mutable), + new ChatColours(this.Mutable, this.Ui.Plugin), new Tabs(this.Mutable), }; diff --git a/ChatTwo/Ui/SettingsTabs/ChatColours.cs b/ChatTwo/Ui/SettingsTabs/ChatColours.cs index 93f3f8a..cb89330 100755 --- a/ChatTwo/Ui/SettingsTabs/ChatColours.cs +++ b/ChatTwo/Ui/SettingsTabs/ChatColours.cs @@ -1,26 +1,36 @@ using ChatTwo.Code; using ChatTwo.Util; +using Dalamud.Interface; using ImGuiNET; namespace ChatTwo.Ui.SettingsTabs; internal sealed class ChatColours : ISettingsTab { private Configuration Mutable { get; } + private Plugin Plugin { get; } public string Name => "Chat colours"; - internal ChatColours(Configuration mutable) { + internal ChatColours(Configuration mutable, Plugin plugin) { this.Mutable = mutable; + this.Plugin = plugin; } public void Draw() { foreach (var type in Enum.GetValues()) { - if (ImGui.Button($"Default##{type}")) { + if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", "Reset to default")) { this.Mutable.ChatColours.Remove(type); } ImGui.SameLine(); + if (ImGuiUtil.IconButton(FontAwesomeIcon.LongArrowAltDown, $"{type}", "Import from game")) { + var gameColour = this.Plugin.Functions.Chat.GetChannelColour(type); + this.Mutable.ChatColours[type] = gameColour ?? type.DefaultColour() ?? 0; + } + + ImGui.SameLine(); + var vec = this.Mutable.ChatColours.TryGetValue(type, out var colour) ? ColourUtil.RgbaToVector3(colour) : ColourUtil.RgbaToVector3(type.DefaultColour() ?? 0);