From 3eda6e5e571a782f500498dccfe425d64844e63a Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 28 Sep 2023 21:09:50 -0400 Subject: [PATCH] refactor: update for api 9 --- ExpandedSearchInfo/Configs/BaseConfig.cs | 12 +- ExpandedSearchInfo/Configs/CarrdConfig.cs | 6 +- ExpandedSearchInfo/Configs/FListConfig.cs | 14 +- ExpandedSearchInfo/Configs/PastebinConfig.cs | 6 +- ExpandedSearchInfo/Configs/PlainTextConfig.cs | 6 +- ExpandedSearchInfo/Configs/RefsheetConfig.cs | 6 +- ExpandedSearchInfo/GameFunctions.cs | 65 ++- ExpandedSearchInfo/Plugin.cs | 85 ++-- ExpandedSearchInfo/PluginConfiguration.cs | 38 +- ExpandedSearchInfo/PluginUi.cs | 381 +++++++++--------- .../Providers/BaseHtmlProvider.cs | 34 +- ExpandedSearchInfo/Providers/CarrdProvider.cs | 122 +++--- ExpandedSearchInfo/Providers/FListProvider.cs | 214 +++++----- ExpandedSearchInfo/Providers/IProvider.cs | 80 ++-- .../Providers/PastebinProvider.cs | 70 ++-- .../Providers/PlainTextProvider.cs | 64 +-- .../Providers/RefsheetProvider.cs | 288 ++++++------- ExpandedSearchInfo/SearchInfoRepository.cs | 347 ++++++++-------- ExpandedSearchInfo/Sections/FListSection.cs | 102 ++--- .../Sections/ISearchInfoSection.cs | 16 +- .../Sections/RefsheetSection.cs | 64 +-- ExpandedSearchInfo/Sections/TextSection.cs | 32 +- ExpandedSearchInfo/Util.cs | 36 +- 23 files changed, 1046 insertions(+), 1042 deletions(-) diff --git a/ExpandedSearchInfo/Configs/BaseConfig.cs b/ExpandedSearchInfo/Configs/BaseConfig.cs index c9b64c5..a4ebb2e 100755 --- a/ExpandedSearchInfo/Configs/BaseConfig.cs +++ b/ExpandedSearchInfo/Configs/BaseConfig.cs @@ -1,6 +1,6 @@ -namespace ExpandedSearchInfo.Configs { - public abstract class BaseConfig { - public bool Enabled { get; set; } = true; - public bool DefaultExpanded { get; set; } = true; - } -} +namespace ExpandedSearchInfo.Configs; + +public abstract class BaseConfig { + public bool Enabled { get; set; } = true; + public bool DefaultExpanded { get; set; } = true; +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Configs/CarrdConfig.cs b/ExpandedSearchInfo/Configs/CarrdConfig.cs index 78bf833..1d6327d 100755 --- a/ExpandedSearchInfo/Configs/CarrdConfig.cs +++ b/ExpandedSearchInfo/Configs/CarrdConfig.cs @@ -1,4 +1,4 @@ -namespace ExpandedSearchInfo.Configs { - public class CarrdConfig : BaseConfig { - } +namespace ExpandedSearchInfo.Configs; + +public class CarrdConfig : BaseConfig { } \ No newline at end of file diff --git a/ExpandedSearchInfo/Configs/FListConfig.cs b/ExpandedSearchInfo/Configs/FListConfig.cs index dc8a50e..d273de3 100755 --- a/ExpandedSearchInfo/Configs/FListConfig.cs +++ b/ExpandedSearchInfo/Configs/FListConfig.cs @@ -1,10 +1,10 @@ using Newtonsoft.Json; -namespace ExpandedSearchInfo.Configs { - public class FListConfig : BaseConfig { - [JsonConstructor] - public FListConfig() { - this.Enabled = false; - } +namespace ExpandedSearchInfo.Configs; + +public class FListConfig : BaseConfig { + [JsonConstructor] + public FListConfig() { + this.Enabled = false; } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Configs/PastebinConfig.cs b/ExpandedSearchInfo/Configs/PastebinConfig.cs index f49ccd5..a7713a4 100755 --- a/ExpandedSearchInfo/Configs/PastebinConfig.cs +++ b/ExpandedSearchInfo/Configs/PastebinConfig.cs @@ -1,4 +1,4 @@ -namespace ExpandedSearchInfo.Configs { - public class PastebinConfig : BaseConfig { - } +namespace ExpandedSearchInfo.Configs; + +public class PastebinConfig : BaseConfig { } \ No newline at end of file diff --git a/ExpandedSearchInfo/Configs/PlainTextConfig.cs b/ExpandedSearchInfo/Configs/PlainTextConfig.cs index 92df8fb..66371c2 100755 --- a/ExpandedSearchInfo/Configs/PlainTextConfig.cs +++ b/ExpandedSearchInfo/Configs/PlainTextConfig.cs @@ -1,4 +1,4 @@ -namespace ExpandedSearchInfo.Configs { - public class PlainTextConfig : BaseConfig { - } +namespace ExpandedSearchInfo.Configs; + +public class PlainTextConfig : BaseConfig { } \ No newline at end of file diff --git a/ExpandedSearchInfo/Configs/RefsheetConfig.cs b/ExpandedSearchInfo/Configs/RefsheetConfig.cs index 4a73136..95169c5 100755 --- a/ExpandedSearchInfo/Configs/RefsheetConfig.cs +++ b/ExpandedSearchInfo/Configs/RefsheetConfig.cs @@ -1,4 +1,4 @@ -namespace ExpandedSearchInfo.Configs { - public class RefsheetConfig : BaseConfig { - } +namespace ExpandedSearchInfo.Configs; + +public class RefsheetConfig : BaseConfig { } \ No newline at end of file diff --git a/ExpandedSearchInfo/GameFunctions.cs b/ExpandedSearchInfo/GameFunctions.cs index 8039887..347c7e3 100644 --- a/ExpandedSearchInfo/GameFunctions.cs +++ b/ExpandedSearchInfo/GameFunctions.cs @@ -1,47 +1,46 @@ using System; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; -using Dalamud.Logging; -namespace ExpandedSearchInfo { - public class GameFunctions : IDisposable { - private Plugin Plugin { get; } +namespace ExpandedSearchInfo; - private delegate byte SearchInfoDownloadedDelegate(IntPtr data, IntPtr a2, IntPtr searchInfoPtr, IntPtr a4); +public class GameFunctions : IDisposable { + private Plugin Plugin { get; } - private readonly Hook? _searchInfoDownloadedHook; + private delegate byte SearchInfoDownloadedDelegate(IntPtr data, IntPtr a2, IntPtr searchInfoPtr, IntPtr a4); - internal delegate void ReceiveSearchInfoEventDelegate(uint objectId, SeString info); + private readonly Hook? _searchInfoDownloadedHook; - internal event ReceiveSearchInfoEventDelegate? ReceiveSearchInfo; + internal delegate void ReceiveSearchInfoEventDelegate(uint objectId, SeString info); - internal GameFunctions(Plugin plugin) { - this.Plugin = plugin; + internal event ReceiveSearchInfoEventDelegate? ReceiveSearchInfo; - var sidPtr = this.Plugin.SigScanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 56 48 83 EC 20 49 8B E8 8B DA"); - this._searchInfoDownloadedHook = Hook.FromAddress(sidPtr, this.SearchInfoDownloaded); - this._searchInfoDownloadedHook.Enable(); + internal GameFunctions(Plugin plugin) { + this.Plugin = plugin; + + var sidPtr = this.Plugin.SigScanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 56 48 83 EC 20 49 8B E8 8B DA"); + this._searchInfoDownloadedHook = this.Plugin.GameInteropProvider.HookFromAddress(sidPtr, this.SearchInfoDownloaded); + this._searchInfoDownloadedHook.Enable(); + } + + public void Dispose() { + this._searchInfoDownloadedHook?.Dispose(); + } + + private unsafe byte SearchInfoDownloaded(IntPtr data, IntPtr a2, IntPtr searchInfoPtr, IntPtr a4) { + var result = this._searchInfoDownloadedHook!.Original(data, a2, searchInfoPtr, a4); + + try { + // Updated: 4.5 + var actorId = *(uint*) (data + 48); + + var searchInfo = Util.ReadRawSeString(searchInfoPtr); + + this.ReceiveSearchInfo?.Invoke(actorId, searchInfo); + } catch (Exception ex) { + Plugin.Log.Error(ex, "Error in SearchInfoDownloaded hook"); } - public void Dispose() { - this._searchInfoDownloadedHook?.Dispose(); - } - - private unsafe byte SearchInfoDownloaded(IntPtr data, IntPtr a2, IntPtr searchInfoPtr, IntPtr a4) { - var result = this._searchInfoDownloadedHook!.Original(data, a2, searchInfoPtr, a4); - - try { - // Updated: 4.5 - var actorId = *(uint*) (data + 48); - - var searchInfo = Util.ReadRawSeString(searchInfoPtr); - - this.ReceiveSearchInfo?.Invoke(actorId, searchInfo); - } catch (Exception ex) { - PluginLog.LogError(ex, "Error in SearchInfoDownloaded hook"); - } - - return result; - } + return result; } } diff --git a/ExpandedSearchInfo/Plugin.cs b/ExpandedSearchInfo/Plugin.cs index d31d4ab..e7ea70f 100644 --- a/ExpandedSearchInfo/Plugin.cs +++ b/ExpandedSearchInfo/Plugin.cs @@ -1,57 +1,62 @@ using Dalamud.Game; -using Dalamud.Game.ClientState.Objects; using Dalamud.Game.Command; -using Dalamud.Game.Gui; using Dalamud.IoC; using Dalamud.Plugin; +using Dalamud.Plugin.Services; -namespace ExpandedSearchInfo { - // ReSharper disable once ClassNeverInstantiated.Global - public class Plugin : IDalamudPlugin { - public string Name => "Expanded Search Info"; +namespace ExpandedSearchInfo; - [PluginService] - internal DalamudPluginInterface Interface { get; init; } = null!; +// ReSharper disable once ClassNeverInstantiated.Global +public class Plugin : IDalamudPlugin { + internal static string Name => "Expanded Search Info"; - [PluginService] - internal CommandManager CommandManager { get; init; } = null!; + [PluginService] + internal static IPluginLog Log { get; private set; } = null!; - [PluginService] - internal GameGui GameGui { get; init; } = null!; + [PluginService] + internal DalamudPluginInterface Interface { get; init; } = null!; - [PluginService] - internal ObjectTable ObjectTable { get; init; } = null!; + [PluginService] + internal ICommandManager CommandManager { get; init; } = null!; - [PluginService] - internal SigScanner SigScanner { get; init; } = null!; + [PluginService] + internal IGameGui GameGui { get; init; } = null!; - internal PluginConfiguration Config { get; } - internal GameFunctions Functions { get; } - internal SearchInfoRepository Repository { get; } - private PluginUi Ui { get; } + [PluginService] + internal IObjectTable ObjectTable { get; init; } = null!; - public Plugin() { - this.Config = (PluginConfiguration?) this.Interface.GetPluginConfig() ?? new PluginConfiguration(); - this.Config.Initialise(this); + [PluginService] + internal ISigScanner SigScanner { get; init; } = null!; - this.Functions = new GameFunctions(this); - this.Repository = new SearchInfoRepository(this); - this.Ui = new PluginUi(this); + [PluginService] + internal IGameInteropProvider GameInteropProvider { get; init; } = null!; - this.CommandManager.AddHandler("/esi", new CommandInfo(this.OnCommand) { - HelpMessage = "Toggles Expanded Search Info's configuration window", - }); - } + internal PluginConfiguration Config { get; } + internal GameFunctions Functions { get; } + internal SearchInfoRepository Repository { get; } + private PluginUi Ui { get; } - public void Dispose() { - this.CommandManager.RemoveHandler("/esi"); - this.Ui.Dispose(); - this.Repository.Dispose(); - this.Functions.Dispose(); - } + public Plugin() { + this.Config = (PluginConfiguration?) this.Interface.GetPluginConfig() ?? new PluginConfiguration(); + this.Config.Initialise(this); - private void OnCommand(string command, string arguments) { - this.Ui.ConfigVisible = !this.Ui.ConfigVisible; - } + this.Functions = new GameFunctions(this); + this.Repository = new SearchInfoRepository(this); + this.Ui = new PluginUi(this); + + this.CommandManager.AddHandler("/esi", new CommandInfo(this.OnCommand) { + HelpMessage = "Toggles Expanded Search Info's configuration window", + }); } -} + + public void Dispose() { + this.CommandManager.RemoveHandler("/esi"); + this.Ui.Dispose(); + this.Repository.Dispose(); + this.Functions.Dispose(); + } + + private void OnCommand(string command, string arguments) { + this.Ui.ConfigVisible = !this.Ui.ConfigVisible; + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/PluginConfiguration.cs b/ExpandedSearchInfo/PluginConfiguration.cs index c0d770e..ef75804 100755 --- a/ExpandedSearchInfo/PluginConfiguration.cs +++ b/ExpandedSearchInfo/PluginConfiguration.cs @@ -2,30 +2,30 @@ using Dalamud.Configuration; using ExpandedSearchInfo.Configs; -namespace ExpandedSearchInfo { - [Serializable] - public class PluginConfiguration : IPluginConfiguration { - private Plugin Plugin { get; set; } = null!; +namespace ExpandedSearchInfo; - public int Version { get; set; } = 1; +[Serializable] +public class PluginConfiguration : IPluginConfiguration { + private Plugin Plugin { get; set; } = null!; - public ProviderConfigs Configs { get; set; } = new(); + public int Version { get; set; } = 1; - internal void Initialise(Plugin plugin) { - this.Plugin = plugin; - } + public ProviderConfigs Configs { get; set; } = new(); - internal void Save() { - this.Plugin.Interface.SavePluginConfig(this); - } + internal void Initialise(Plugin plugin) { + this.Plugin = plugin; } - [Serializable] - public class ProviderConfigs { - public CarrdConfig Carrd { get; set; } = new(); - public FListConfig FList { get; set; } = new(); - public PastebinConfig Pastebin { get; set; } = new(); - public PlainTextConfig PlainText { get; set; } = new(); - public RefsheetConfig Refsheet { get; set; } = new(); + internal void Save() { + this.Plugin.Interface.SavePluginConfig(this); } } + +[Serializable] +public class ProviderConfigs { + public CarrdConfig Carrd { get; set; } = new(); + public FListConfig FList { get; set; } = new(); + public PastebinConfig Pastebin { get; set; } = new(); + public PlainTextConfig PlainText { get; set; } = new(); + public RefsheetConfig Refsheet { get; set; } = new(); +} \ No newline at end of file diff --git a/ExpandedSearchInfo/PluginUi.cs b/ExpandedSearchInfo/PluginUi.cs index 4c94358..1709c27 100644 --- a/ExpandedSearchInfo/PluginUi.cs +++ b/ExpandedSearchInfo/PluginUi.cs @@ -2,232 +2,233 @@ using System.Diagnostics; using System.Numerics; using Dalamud.Interface; +using Dalamud.Interface.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; using ImGuiNET; -namespace ExpandedSearchInfo { - public class PluginUi : IDisposable { - private Plugin Plugin { get; } +namespace ExpandedSearchInfo; - private bool _configVisible; +public class PluginUi : IDisposable { + private Plugin Plugin { get; } - internal bool ConfigVisible { - get => this._configVisible; - set => this._configVisible = value; + private bool _configVisible; + + internal bool ConfigVisible { + get => this._configVisible; + set => this._configVisible = value; + } + + internal PluginUi(Plugin plugin) { + this.Plugin = plugin; + + this.Plugin.Interface.UiBuilder.Draw += this.Draw; + this.Plugin.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi; + } + + private void OnOpenConfigUi() { + this.ConfigVisible = true; + } + + public void Dispose() { + this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.OnOpenConfigUi; + this.Plugin.Interface.UiBuilder.Draw -= this.Draw; + } + + private static bool IconButton(FontAwesomeIcon icon, string? id = null) { + ImGui.PushFont(UiBuilder.IconFont); + + var text = icon.ToIconString(); + if (id != null) { + text += $"##{id}"; } - internal PluginUi(Plugin plugin) { - this.Plugin = plugin; + var result = ImGui.Button(text); - this.Plugin.Interface.UiBuilder.Draw += this.Draw; - this.Plugin.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi; + ImGui.PopFont(); + + return result; + } + + private void Draw() { + this.DrawConfig(); + this.DrawExpandedSearchInfo(); + } + + private void DrawConfig() { + if (!this.ConfigVisible) { + return; } - private void OnOpenConfigUi() { - this.ConfigVisible = true; + ImGui.SetNextWindowSize(new Vector2(500, -1), ImGuiCond.FirstUseEver); + + if (!ImGui.Begin($"{Plugin.Name} settings", ref this._configVisible)) { + return; } - public void Dispose() { - this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.OnOpenConfigUi; - this.Plugin.Interface.UiBuilder.Draw -= this.Draw; + ImGui.PushTextWrapPos(); + + if (ImGui.Button("Clear cache")) { + this.Plugin.Repository.SearchInfos.Clear(); } - private static bool IconButton(FontAwesomeIcon icon, string? id = null) { - ImGui.PushFont(UiBuilder.IconFont); + ImGui.SameLine(); - var text = icon.ToIconString(); - if (id != null) { - text += $"##{id}"; - } + var cached = this.Plugin.Repository.SearchInfos.Count; + var playersString = cached switch { + 1 => "One player", + _ => $"{cached} players", + }; + ImGui.TextUnformatted($"{playersString} in the cache"); - var result = ImGui.Button(text); + ImGui.Spacing(); - ImGui.PopFont(); + ImGui.TextUnformatted("Expanded Search Info downloads information contained in search infos once and caches it for later retrieval. If you want to clear this cache, click the button above. You can also clear individual players from the cache with the button in their expanded info."); - return result; - } + ImGui.Separator(); - private void Draw() { - this.DrawConfig(); - this.DrawExpandedSearchInfo(); - } - - private void DrawConfig() { - if (!this.ConfigVisible) { + if (ImGui.CollapsingHeader("Providers", ImGuiTreeNodeFlags.DefaultOpen)) { + if (!ImGui.BeginTabBar("ESI tabs")) { return; } - ImGui.SetNextWindowSize(new Vector2(500, -1), ImGuiCond.FirstUseEver); + foreach (var provider in this.Plugin.Repository.AllProviders) { + if (!ImGui.BeginTabItem($"{provider.Name}##esi-provider")) { + continue; + } - if (!ImGui.Begin($"{this.Plugin.Name} settings", ref this._configVisible)) { - return; + ImGui.Columns(2); + + ImGui.SetColumnWidth(0, ImGui.GetWindowWidth() / 3); + + ImGui.TextUnformatted(provider.Description); + + ImGui.NextColumn(); + + var enabled = provider.Config.Enabled; + if (ImGui.Checkbox($"Enabled##{provider.Name}", ref enabled)) { + provider.Config.Enabled = enabled; + this.Plugin.Config.Save(); + } + + var defaultOpen = provider.Config.DefaultExpanded; + if (ImGui.Checkbox($"Open by default##{provider.Name}", ref defaultOpen)) { + provider.Config.DefaultExpanded = defaultOpen; + this.Plugin.Config.Save(); + } + + provider.DrawConfig(); + + ImGui.Columns(1); + + ImGui.EndTabItem(); } - ImGui.PushTextWrapPos(); + ImGui.EndTabBar(); + } - if (ImGui.Button("Clear cache")) { - this.Plugin.Repository.SearchInfos.Clear(); + ImGui.PopTextWrapPos(); + + ImGui.End(); + } + + private unsafe void DrawExpandedSearchInfo() { + // check if the examine window is open + var addonPtr = this.Plugin.GameGui.GetAddonByName("CharacterInspect", 1); + if (addonPtr == IntPtr.Zero) { + return; + } + + var addon = (AtkUnitBase*) addonPtr; + if (!addon->IsVisible) { + return; + } + + // get examine window info + var rootNode = addon->RootNode; + if (rootNode == null) { + return; + } + + var width = rootNode->Width * addon->Scale; + var height = rootNode->Height * addon->Scale; + var x = addon->X; + var y = addon->Y; + + // check the last actor id recorded (should be who the examine window is showing) + var actorId = this.Plugin.Repository.LastObjectId; + if (actorId == 0 || !this.Plugin.Repository.SearchInfos.TryGetValue(actorId, out var expanded)) { + return; + } + + // set window size + ImGui.SetNextWindowSizeConstraints( + new Vector2(0, 0), + new Vector2(ImGui.GetIO().DisplaySize.X / 4, height) + ); + ImGui.SetNextWindowSize(new Vector2(-1, -1)); + + if (!ImGui.Begin(Plugin.Name, ImGuiWindowFlags.NoTitleBar)) { + ImGui.End(); + return; + } + + ImGui.PushTextWrapPos(ImGui.GetIO().DisplaySize.X / 4 - 24); + + // show a section for each extracted section + for (var i = 0; i < expanded.Sections.Count; i++) { + var section = expanded.Sections[i]; + + var flags = section.Provider.Config.DefaultExpanded switch { + true => ImGuiTreeNodeFlags.DefaultOpen, + false => ImGuiTreeNodeFlags.None, + }; + if (!ImGui.CollapsingHeader($"{section.Name}##{i}", flags)) { + continue; + } + + ImGui.TreePush(); + + if (IconButton(FontAwesomeIcon.ExternalLinkAlt, $"open-{i}")) { + Process.Start(new ProcessStartInfo { + FileName = section.Uri.ToString(), + UseShellExecute = true, + }); + } + + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.TextUnformatted("Open in browser."); + ImGui.EndTooltip(); } ImGui.SameLine(); - var cached = this.Plugin.Repository.SearchInfos.Count; - var playersString = cached switch { - 1 => "One player", - _ => $"{cached} players", - }; - ImGui.TextUnformatted($"{playersString} in the cache"); - - ImGui.Spacing(); - - ImGui.TextUnformatted("Expanded Search Info downloads information contained in search infos once and caches it for later retrieval. If you want to clear this cache, click the button above. You can also clear individual players from the cache with the button in their expanded info."); - - ImGui.Separator(); - - if (ImGui.CollapsingHeader("Providers", ImGuiTreeNodeFlags.DefaultOpen)) { - if (!ImGui.BeginTabBar("ESI tabs")) { - return; - } - - foreach (var provider in this.Plugin.Repository.AllProviders) { - if (!ImGui.BeginTabItem($"{provider.Name}##esi-provider")) { - continue; - } - - ImGui.Columns(2); - - ImGui.SetColumnWidth(0, ImGui.GetWindowWidth() / 3); - - ImGui.TextUnformatted(provider.Description); - - ImGui.NextColumn(); - - var enabled = provider.Config.Enabled; - if (ImGui.Checkbox($"Enabled##{provider.Name}", ref enabled)) { - provider.Config.Enabled = enabled; - this.Plugin.Config.Save(); - } - - var defaultOpen = provider.Config.DefaultExpanded; - if (ImGui.Checkbox($"Open by default##{provider.Name}", ref defaultOpen)) { - provider.Config.DefaultExpanded = defaultOpen; - this.Plugin.Config.Save(); - } - - provider.DrawConfig(); - - ImGui.Columns(1); - - ImGui.EndTabItem(); - } - - ImGui.EndTabBar(); + if (IconButton(FontAwesomeIcon.Redo, $"refresh-{i}")) { + this.Plugin.Repository.SearchInfos.TryRemove(actorId, out _); } - ImGui.PopTextWrapPos(); + if (ImGui.IsItemHovered()) { + ImGui.BeginTooltip(); + ImGui.TextUnformatted("Clear the cache. Re-examine this character to redownload information."); + ImGui.EndTooltip(); + } - ImGui.End(); + section.Draw(); + + ImGui.TreePop(); } - private unsafe void DrawExpandedSearchInfo() { - // check if the examine window is open - var addonPtr = this.Plugin.GameGui.GetAddonByName("CharacterInspect", 1); - if (addonPtr == IntPtr.Zero) { - return; - } + ImGui.PopTextWrapPos(); - var addon = (AtkUnitBase*) addonPtr; - if (!addon->IsVisible) { - return; - } + // determine whether to show on the left or right of the examine window based on space available + var display = ImGui.GetIO().DisplaySize; + var actualWidth = ImGui.GetWindowWidth(); - // get examine window info - var rootNode = addon->RootNode; - if (rootNode == null) { - return; - } + var xPos = x + width + actualWidth > display.X + ? x - actualWidth + : x + width; + ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(xPos, y)); - var width = rootNode->Width * addon->Scale; - var height = rootNode->Height * addon->Scale; - var x = addon->X; - var y = addon->Y; - - // check the last actor id recorded (should be who the examine window is showing) - var actorId = this.Plugin.Repository.LastObjectId; - if (actorId == 0 || !this.Plugin.Repository.SearchInfos.TryGetValue(actorId, out var expanded)) { - return; - } - - // set window size - ImGui.SetNextWindowSizeConstraints( - new Vector2(0, 0), - new Vector2(ImGui.GetIO().DisplaySize.X / 4, height) - ); - ImGui.SetNextWindowSize(new Vector2(-1, -1)); - - if (!ImGui.Begin(this.Plugin.Name, ImGuiWindowFlags.NoTitleBar)) { - ImGui.End(); - return; - } - - ImGui.PushTextWrapPos(ImGui.GetIO().DisplaySize.X / 4 - 24); - - // show a section for each extracted section - for (var i = 0; i < expanded.Sections.Count; i++) { - var section = expanded.Sections[i]; - - var flags = section.Provider.Config.DefaultExpanded switch { - true => ImGuiTreeNodeFlags.DefaultOpen, - false => ImGuiTreeNodeFlags.None, - }; - if (!ImGui.CollapsingHeader($"{section.Name}##{i}", flags)) { - continue; - } - - ImGui.TreePush(); - - if (IconButton(FontAwesomeIcon.ExternalLinkAlt, $"open-{i}")) { - Process.Start(new ProcessStartInfo { - FileName = section.Uri.ToString(), - UseShellExecute = true, - }); - } - - if (ImGui.IsItemHovered()) { - ImGui.BeginTooltip(); - ImGui.TextUnformatted("Open in browser."); - ImGui.EndTooltip(); - } - - ImGui.SameLine(); - - if (IconButton(FontAwesomeIcon.Redo, $"refresh-{i}")) { - this.Plugin.Repository.SearchInfos.TryRemove(actorId, out _); - } - - if (ImGui.IsItemHovered()) { - ImGui.BeginTooltip(); - ImGui.TextUnformatted("Clear the cache. Re-examine this character to redownload information."); - ImGui.EndTooltip(); - } - - section.Draw(); - - ImGui.TreePop(); - } - - ImGui.PopTextWrapPos(); - - // determine whether to show on the left or right of the examine window based on space available - var display = ImGui.GetIO().DisplaySize; - var actualWidth = ImGui.GetWindowWidth(); - - var xPos = x + width + actualWidth > display.X - ? x - actualWidth - : x + width; - ImGui.SetWindowPos(ImGuiHelpers.MainViewport.Pos + new Vector2(xPos, y)); - - ImGui.End(); - } + ImGui.End(); } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs b/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs index e1e2f0f..d5d4b50 100644 --- a/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs +++ b/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs @@ -8,30 +8,30 @@ using AngleSharp.Html.Parser; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public abstract class BaseHtmlProvider : IProvider { - private IBrowsingContext Context { get; } = BrowsingContext.New(); +namespace ExpandedSearchInfo.Providers; - public abstract string Name { get; } +public abstract class BaseHtmlProvider : IProvider { + private IBrowsingContext Context { get; } = BrowsingContext.New(); - public abstract string Description { get; } + public abstract string Name { get; } - public abstract BaseConfig Config { get; } + public abstract string Description { get; } - public abstract bool ExtractsUris { get; } + public abstract BaseConfig Config { get; } - public abstract void DrawConfig(); + public abstract bool ExtractsUris { get; } - public abstract bool Matches(Uri uri); + public abstract void DrawConfig(); - public abstract IEnumerable? ExtractUris(uint objectId, string info); + public abstract bool Matches(Uri uri); - public abstract Task ExtractInfo(HttpResponseMessage response); + public abstract IEnumerable? ExtractUris(uint objectId, string info); - protected async Task DownloadDocument(HttpResponseMessage response) { - var html = await response.Content.ReadAsStringAsync(); - var parser = this.Context.GetService(); - return await parser.ParseDocumentAsync(html); - } + public abstract Task ExtractInfo(HttpResponseMessage response); + + protected async Task DownloadDocument(HttpResponseMessage response) { + var html = await response.Content.ReadAsStringAsync(); + var parser = this.Context.GetService(); + return await parser.ParseDocumentAsync(html); } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/CarrdProvider.cs b/ExpandedSearchInfo/Providers/CarrdProvider.cs index 49f0f4d..a4faf11 100644 --- a/ExpandedSearchInfo/Providers/CarrdProvider.cs +++ b/ExpandedSearchInfo/Providers/CarrdProvider.cs @@ -7,85 +7,85 @@ using AngleSharp.Dom; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public class CarrdProvider : BaseHtmlProvider { - private static readonly string[] Domains = { - ".carrd.co", - ".crd.co", - ".carrd.com", - }; +namespace ExpandedSearchInfo.Providers; - private Plugin Plugin { get; } +public class CarrdProvider : BaseHtmlProvider { + private static readonly string[] Domains = { + ".carrd.co", + ".crd.co", + ".carrd.com", + }; - public override string Name => "Carrd"; + private Plugin Plugin { get; } - public override string Description => "This provider provides information for carrd.co URLs and their aliases."; + public override string Name => "Carrd"; - public override BaseConfig Config => this.Plugin.Config.Configs.Carrd; + public override string Description => "This provider provides information for carrd.co URLs and their aliases."; - public override bool ExtractsUris => false; + public override BaseConfig Config => this.Plugin.Config.Configs.Carrd; - internal CarrdProvider(Plugin plugin) { - this.Plugin = plugin; - } + public override bool ExtractsUris => false; - public override void DrawConfig() { - } + internal CarrdProvider(Plugin plugin) { + this.Plugin = plugin; + } - public override bool Matches(Uri uri) => Domains.Any(domain => uri.Host.EndsWith(domain)); + public override void DrawConfig() { + } - public override IEnumerable? ExtractUris(uint objectId, string info) => null; + public override bool Matches(Uri uri) => Domains.Any(domain => uri.Host.EndsWith(domain)); - public override async Task ExtractInfo(HttpResponseMessage response) { - var document = await this.DownloadDocument(response); + public override IEnumerable? ExtractUris(uint objectId, string info) => null; - var text = string.Empty; + public override async Task ExtractInfo(HttpResponseMessage response) { + var document = await this.DownloadDocument(response); - IElement? lastList = null; - var listNum = 1; + var text = string.Empty; - foreach (var element in document.QuerySelectorAll("p, [id ^= 'text']")) { - // check if this element is in an li - var inLi = element.ParentElement?.TagName == "LI"; - // if the first element in a li, we need to prefix it - if (inLi && element.PreviousSibling == null) { - // check if this element is in the same list as the last list element we checked - if (element.ParentElement != lastList) { - // if not, update the last list and reset the counter - lastList = element.ParentElement; - listNum = 1; - } + IElement? lastList = null; + var listNum = 1; - // check if this list is an ol or ul - var isOl = element.ParentElement?.ParentElement?.TagName == "OL"; - if (isOl) { - // use the list number for ol - text += $"{listNum++}. "; - } else { - // use a dash for ul - text += "- "; - } + foreach (var element in document.QuerySelectorAll("p, [id ^= 'text']")) { + // check if this element is in an li + var inLi = element.ParentElement?.TagName == "LI"; + // if the first element in a li, we need to prefix it + if (inLi && element.PreviousSibling == null) { + // check if this element is in the same list as the last list element we checked + if (element.ParentElement != lastList) { + // if not, update the last list and reset the counter + lastList = element.ParentElement; + listNum = 1; } - // add the text from each child node - foreach (var node in element.ChildNodes) { - text += node.Text(); - // add an extra newline if the node is a br - if (node is IElement { TagName: "BR" }) { - text += '\n'; - } + // check if this list is an ol or ul + var isOl = element.ParentElement?.ParentElement?.TagName == "OL"; + if (isOl) { + // use the list number for ol + text += $"{listNum++}. "; + } else { + // use a dash for ul + text += "- "; } - - // add a newline after every element - text += '\n'; } - return new TextSection( - this, - $"{document.Title} (Carrd)", - response.RequestMessage!.RequestUri!, - text - ); + // add the text from each child node + foreach (var node in element.ChildNodes) { + text += node.Text(); + // add an extra newline if the node is a br + if (node is IElement { TagName: "BR" }) { + text += '\n'; + } + } + + // add a newline after every element + text += '\n'; } + + return new TextSection( + this, + $"{document.Title} (Carrd)", + response.RequestMessage!.RequestUri!, + text + ); } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/FListProvider.cs b/ExpandedSearchInfo/Providers/FListProvider.cs index 1733df0..11873ac 100644 --- a/ExpandedSearchInfo/Providers/FListProvider.cs +++ b/ExpandedSearchInfo/Providers/FListProvider.cs @@ -8,117 +8,117 @@ using AngleSharp.Dom; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public class FListProvider : BaseHtmlProvider { - private Plugin Plugin { get; } +namespace ExpandedSearchInfo.Providers; - public override string Name => "F-List (18+)"; +public class FListProvider : BaseHtmlProvider { + private Plugin Plugin { get; } - public override string Description => "This provider provides information for F-List URLs. It also searches for F-List profiles matching the character's name if /c/ is in their search info."; + public override string Name => "F-List (18+)"; - public override BaseConfig Config => this.Plugin.Config.Configs.FList; + public override string Description => "This provider provides information for F-List URLs. It also searches for F-List profiles matching the character's name if /c/ is in their search info."; - public override bool ExtractsUris => true; + public override BaseConfig Config => this.Plugin.Config.Configs.FList; - internal FListProvider(Plugin plugin) { - this.Plugin = plugin; - } + public override bool ExtractsUris => true; - public override void DrawConfig() { - } - - public override bool Matches(Uri uri) => uri.Host is "www.f-list.net" or "f-list.net" && uri.AbsolutePath.StartsWith("/c/"); - - public override IEnumerable? ExtractUris(uint objectId, string info) { - if (!info.ToLowerInvariant().Contains("c/")) { - return null; - } - - var obj = this.Plugin.ObjectTable.FirstOrDefault(obj => obj.ObjectId == objectId); - if (obj == null) { - return null; - } - - var safeName = obj.Name.ToString().Replace("'", ""); - - return new[] { - new Uri($"https://www.f-list.net/c/{Uri.EscapeUriString(safeName)}"), - }; - } - - public override async Task ExtractInfo(HttpResponseMessage response) { - var document = await this.DownloadDocument(response); - - var error = document.QuerySelector("#DisplayedMessage"); - if (error != null) { - var errorText = error.Text(); - - if (errorText.Contains("No such character exists")) { - return null; - } - - if (errorText.Contains("has been banned")) { - return null; - } - } - - var stats = new List>(); - var statBox = document.QuerySelector(".statbox"); - if (statBox != null) { - foreach (var stat in statBox.Children) { - if (!stat.Matches(".taglabel")) { - continue; - } - - var name = stat.Text().Trim().Trim(' ', '\r', '\n', '\t', ':'); - var value = stat.NextSibling.Text().Trim(' ', '\r', '\n', '\t', ':'); - stats.Add(new Tuple(name, value)); - } - } - - var info = string.Empty; - var formatted = document.QuerySelector("#tabs-1 > .FormattedBlock"); - if (formatted != null) { - foreach (var child in formatted.ChildNodes) { - info += child.Text(); - if (child is IElement childElem && childElem.TagName != "BR") { - info += "\n"; - } - } - } - - // remove bbcode and turn special characters into normal ascii - info = info.StripBbCode().Normalize(NormalizationForm.FormKD); - - var fave = KinkSection(document, "Character_FetishlistFave"); - var yes = KinkSection(document, "Character_FetishlistYes"); - var maybe = KinkSection(document, "Character_FetishlistMaybe"); - var no = KinkSection(document, "Character_FetishlistNo"); - - var charName = document.Title.Split('-')[2].Trim(); - return new FListSection( - this, - $"{charName} (F-List)", - response.RequestMessage!.RequestUri!, - info, - stats, - fave, - yes, - maybe, - no - ); - } - - private static List> KinkSection(IParentNode document, string id) { - var kinks = new List>(); - var kinkElems = document.QuerySelectorAll($"#{id} > a"); - foreach (var kink in kinkElems) { - var name = kink.Text().Trim(); - var value = kink.Attributes.GetNamedItem("rel")?.Value ?? ""; - kinks.Add(new Tuple(name, value)); - } - - return kinks; - } + internal FListProvider(Plugin plugin) { + this.Plugin = plugin; } -} + + public override void DrawConfig() { + } + + public override bool Matches(Uri uri) => uri.Host is "www.f-list.net" or "f-list.net" && uri.AbsolutePath.StartsWith("/c/"); + + public override IEnumerable? ExtractUris(uint objectId, string info) { + if (!info.ToLowerInvariant().Contains("c/")) { + return null; + } + + var obj = this.Plugin.ObjectTable.FirstOrDefault(obj => obj.ObjectId == objectId); + if (obj == null) { + return null; + } + + var safeName = obj.Name.ToString().Replace("'", ""); + + return new[] { + new Uri($"https://www.f-list.net/c/{Uri.EscapeUriString(safeName)}"), + }; + } + + public override async Task ExtractInfo(HttpResponseMessage response) { + var document = await this.DownloadDocument(response); + + var error = document.QuerySelector("#DisplayedMessage"); + if (error != null) { + var errorText = error.Text(); + + if (errorText.Contains("No such character exists")) { + return null; + } + + if (errorText.Contains("has been banned")) { + return null; + } + } + + var stats = new List>(); + var statBox = document.QuerySelector(".statbox"); + if (statBox != null) { + foreach (var stat in statBox.Children) { + if (!stat.Matches(".taglabel")) { + continue; + } + + var name = stat.Text().Trim().Trim(' ', '\r', '\n', '\t', ':'); + var value = stat.NextSibling.Text().Trim(' ', '\r', '\n', '\t', ':'); + stats.Add(new Tuple(name, value)); + } + } + + var info = string.Empty; + var formatted = document.QuerySelector("#tabs-1 > .FormattedBlock"); + if (formatted != null) { + foreach (var child in formatted.ChildNodes) { + info += child.Text(); + if (child is IElement childElem && childElem.TagName != "BR") { + info += "\n"; + } + } + } + + // remove bbcode and turn special characters into normal ascii + info = info.StripBbCode().Normalize(NormalizationForm.FormKD); + + var fave = KinkSection(document, "Character_FetishlistFave"); + var yes = KinkSection(document, "Character_FetishlistYes"); + var maybe = KinkSection(document, "Character_FetishlistMaybe"); + var no = KinkSection(document, "Character_FetishlistNo"); + + var charName = document.Title.Split('-')[2].Trim(); + return new FListSection( + this, + $"{charName} (F-List)", + response.RequestMessage!.RequestUri!, + info, + stats, + fave, + yes, + maybe, + no + ); + } + + private static List> KinkSection(IParentNode document, string id) { + var kinks = new List>(); + var kinkElems = document.QuerySelectorAll($"#{id} > a"); + foreach (var kink in kinkElems) { + var name = kink.Text().Trim(); + var value = kink.Attributes.GetNamedItem("rel")?.Value ?? ""; + kinks.Add(new Tuple(name, value)); + } + + return kinks; + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/IProvider.cs b/ExpandedSearchInfo/Providers/IProvider.cs index eea6fe9..edf949b 100644 --- a/ExpandedSearchInfo/Providers/IProvider.cs +++ b/ExpandedSearchInfo/Providers/IProvider.cs @@ -5,52 +5,52 @@ using System.Threading.Tasks; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public interface IProvider { - string Name { get; } +namespace ExpandedSearchInfo.Providers; - string Description { get; } +public interface IProvider { + string Name { get; } - BaseConfig Config { get; } + string Description { get; } - /// - /// If this provider is capable of parsing the search info for custom Uris, this should be true. - /// - /// Note that normal Uris are parsed by the plugin itself, so this can remain false for providers - /// that only handle normal Uris. - /// - bool ExtractsUris { get; } + BaseConfig Config { get; } - void DrawConfig(); + /// + /// If this provider is capable of parsing the search info for custom Uris, this should be true. + /// + /// Note that normal Uris are parsed by the plugin itself, so this can remain false for providers + /// that only handle normal Uris. + /// + bool ExtractsUris { get; } - /// - /// Determine if this provider should run on the given Uri. - /// - /// Uri to test - /// true if this provider's Extract method should be run for the HTTP response from this Uri - bool Matches(Uri uri); + void DrawConfig(); - /// - /// For providers that require Uris, this can return null. - /// For providers that don't require Uris, this must return a Uri extracted from the given search info. - /// - /// The actor ID associated with the search info - /// A character's full search info - /// null for providers that require Uris, a Uri for providers that don't - IEnumerable? ExtractUris(uint objectId, string info); + /// + /// Determine if this provider should run on the given Uri. + /// + /// Uri to test + /// true if this provider's Extract method should be run for the HTTP response from this Uri + bool Matches(Uri uri); - /// - /// Extract the search info to be displayed given the HTTP response from a Uri. - /// - /// HTTP response from a Uri - /// null if search info could not be extracted or the search info as a string if it could - Task ExtractInfo(HttpResponseMessage response); + /// + /// For providers that require Uris, this can return null. + /// For providers that don't require Uris, this must return a Uri extracted from the given search info. + /// + /// The actor ID associated with the search info + /// A character's full search info + /// null for providers that require Uris, a Uri for providers that don't + IEnumerable? ExtractUris(uint objectId, string info); - /// - /// Modify any requests made for this provider before they are sent. - /// - /// HTTP request about to be sent - void ModifyRequest(HttpRequestMessage request) { - } + /// + /// Extract the search info to be displayed given the HTTP response from a Uri. + /// + /// HTTP response from a Uri + /// null if search info could not be extracted or the search info as a string if it could + Task ExtractInfo(HttpResponseMessage response); + + /// + /// Modify any requests made for this provider before they are sent. + /// + /// HTTP request about to be sent + void ModifyRequest(HttpRequestMessage request) { } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/PastebinProvider.cs b/ExpandedSearchInfo/Providers/PastebinProvider.cs index ccb1a58..7f1afd0 100644 --- a/ExpandedSearchInfo/Providers/PastebinProvider.cs +++ b/ExpandedSearchInfo/Providers/PastebinProvider.cs @@ -7,46 +7,46 @@ using System.Threading.Tasks; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public class PastebinProvider : IProvider { - private static readonly Regex Matcher = new(@"pb:(\S+)", RegexOptions.Compiled | RegexOptions.IgnoreCase); +namespace ExpandedSearchInfo.Providers; - private Plugin Plugin { get; } +public class PastebinProvider : IProvider { + private static readonly Regex Matcher = new(@"pb:(\S+)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - public string Name => "pastebin.com"; + private Plugin Plugin { get; } - public string Description => "This provider provides information from pastebin.com URLs. It also works on tags such as \"pb:pasteid\"."; + public string Name => "pastebin.com"; - public BaseConfig Config => this.Plugin.Config.Configs.Pastebin; + public string Description => "This provider provides information from pastebin.com URLs. It also works on tags such as \"pb:pasteid\"."; - public bool ExtractsUris => true; + public BaseConfig Config => this.Plugin.Config.Configs.Pastebin; - public PastebinProvider(Plugin plugin) { - this.Plugin = plugin; - } + public bool ExtractsUris => true; - public void DrawConfig() { - } - - public bool Matches(Uri uri) => uri.Host == "pastebin.com" && uri.AbsolutePath.Length > 1; - - public IEnumerable? ExtractUris(uint objectId, string info) { - var matches = Matcher.Matches(info); - return matches.Count == 0 - ? null - : from Match match in matches select match.Groups[1].Value into id select new Uri($"https://pastebin.com/raw/{id}"); - } - - public async Task ExtractInfo(HttpResponseMessage response) { - if (response.Content.Headers.ContentType?.MediaType != "text/plain") { - return null; - } - - var id = response.RequestMessage!.RequestUri!.AbsolutePath.Split('/').LastOrDefault(); - - var info = await response.Content.ReadAsStringAsync(); - - return new TextSection(this, $"Pastebin ({id})", response.RequestMessage.RequestUri, info); - } + public PastebinProvider(Plugin plugin) { + this.Plugin = plugin; } -} + + public void DrawConfig() { + } + + public bool Matches(Uri uri) => uri.Host == "pastebin.com" && uri.AbsolutePath.Length > 1; + + public IEnumerable? ExtractUris(uint objectId, string info) { + var matches = Matcher.Matches(info); + return matches.Count == 0 + ? null + : from Match match in matches select match.Groups[1].Value into id select new Uri($"https://pastebin.com/raw/{id}"); + } + + public async Task ExtractInfo(HttpResponseMessage response) { + if (response.Content.Headers.ContentType?.MediaType != "text/plain") { + return null; + } + + var id = response.RequestMessage!.RequestUri!.AbsolutePath.Split('/').LastOrDefault(); + + var info = await response.Content.ReadAsStringAsync(); + + return new TextSection(this, $"Pastebin ({id})", response.RequestMessage.RequestUri, info); + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/PlainTextProvider.cs b/ExpandedSearchInfo/Providers/PlainTextProvider.cs index 559f4aa..95fe4af 100644 --- a/ExpandedSearchInfo/Providers/PlainTextProvider.cs +++ b/ExpandedSearchInfo/Providers/PlainTextProvider.cs @@ -6,42 +6,42 @@ using System.Threading.Tasks; using ExpandedSearchInfo.Configs; using ExpandedSearchInfo.Sections; -namespace ExpandedSearchInfo.Providers { - public class PlainTextProvider : IProvider { - private Plugin Plugin { get; } +namespace ExpandedSearchInfo.Providers; - public string Name => "Plain text"; +public class PlainTextProvider : IProvider { + private Plugin Plugin { get; } - public string Description => "This provider provides information for any URL that provides plain text."; + public string Name => "Plain text"; - public BaseConfig Config => this.Plugin.Config.Configs.PlainText; + public string Description => "This provider provides information for any URL that provides plain text."; - public bool ExtractsUris => false; + public BaseConfig Config => this.Plugin.Config.Configs.PlainText; - internal PlainTextProvider(Plugin plugin) { - this.Plugin = plugin; - } + public bool ExtractsUris => false; - public void DrawConfig() { - } - - public bool Matches(Uri uri) => true; - - public IEnumerable? ExtractUris(uint objectId, string info) => null; - - public async Task ExtractInfo(HttpResponseMessage response) { - if (response.Content.Headers.ContentType?.MediaType != "text/plain") { - return null; - } - - var info = await response.Content.ReadAsStringAsync(); - - var uri = response.RequestMessage!.RequestUri!; - return new TextSection(this, $"Text##{uri}", uri, info); - } - - public void ModifyRequest(HttpRequestMessage request) { - request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); - } + internal PlainTextProvider(Plugin plugin) { + this.Plugin = plugin; } -} + + public void DrawConfig() { + } + + public bool Matches(Uri uri) => true; + + public IEnumerable? ExtractUris(uint objectId, string info) => null; + + public async Task ExtractInfo(HttpResponseMessage response) { + if (response.Content.Headers.ContentType?.MediaType != "text/plain") { + return null; + } + + var info = await response.Content.ReadAsStringAsync(); + + var uri = response.RequestMessage!.RequestUri!; + return new TextSection(this, $"Text##{uri}", uri, info); + } + + public void ModifyRequest(HttpRequestMessage request) { + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Providers/RefsheetProvider.cs b/ExpandedSearchInfo/Providers/RefsheetProvider.cs index d0616c9..4d06481 100644 --- a/ExpandedSearchInfo/Providers/RefsheetProvider.cs +++ b/ExpandedSearchInfo/Providers/RefsheetProvider.cs @@ -8,155 +8,155 @@ using ExpandedSearchInfo.Sections; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace ExpandedSearchInfo.Providers { - public class RefsheetProvider : BaseHtmlProvider { - private const string JsonLineStart = "var props = "; +namespace ExpandedSearchInfo.Providers; - private Plugin Plugin { get; } +public class RefsheetProvider : BaseHtmlProvider { + private const string JsonLineStart = "var props = "; - public override string Name => "Refsheet"; + private Plugin Plugin { get; } - public override string Description => "This provider provides information for refsheet.net and ref.st URLs."; + public override string Name => "Refsheet"; - public override BaseConfig Config => this.Plugin.Config.Configs.Refsheet; + public override string Description => "This provider provides information for refsheet.net and ref.st URLs."; - public override bool ExtractsUris => false; + public override BaseConfig Config => this.Plugin.Config.Configs.Refsheet; - internal RefsheetProvider(Plugin plugin) { - this.Plugin = plugin; - } + public override bool ExtractsUris => false; - public override void DrawConfig() { - } - - public override bool Matches(Uri uri) => uri.Host is "refsheet.net" or "ref.st"; - - public override IEnumerable? ExtractUris(uint objectId, string info) => null; - - public override async Task ExtractInfo(HttpResponseMessage response) { - var document = await this.DownloadDocument(response); - - // refsheet provides all the content but... uses js to display it? - // find the script containing the json and use it - var script = document.QuerySelectorAll("script[type = 'text/javascript']").LastOrDefault(); - if (script == null) { - return null; - } - - var jsonLine = script.InnerHtml.Split('\n') - .Select(line => line.Trim()) - .FirstOrDefault(line => line.StartsWith(JsonLineStart)); - if (jsonLine == null) { - return null; - } - - var json = jsonLine.Substring(JsonLineStart.Length, jsonLine.Length - JsonLineStart.Length - 1); - var parsed = JsonConvert.DeserializeObject(json); - if (parsed == null) { - return null; - } - - var character = parsed.EagerLoad.Character; - - // get character name - var name = character.Name; - - // get all attributes - var attributes = new List>(); - - // handle built-in attributes first - if (!string.IsNullOrWhiteSpace(character.Gender)) { - attributes.Add(new Tuple("Gender", character.Gender)); - } - - if (!string.IsNullOrWhiteSpace(character.Species)) { - attributes.Add(new Tuple("Species", character.Species)); - } - - if (!string.IsNullOrWhiteSpace(character.Height)) { - attributes.Add(new Tuple("Height", character.Height)); - } - - if (!string.IsNullOrWhiteSpace(character.Weight)) { - attributes.Add(new Tuple("Weight", character.Weight)); - } - - if (!string.IsNullOrWhiteSpace(character.BodyType)) { - attributes.Add(new Tuple("Body type", character.BodyType)); - } - - if (!string.IsNullOrWhiteSpace(character.Personality)) { - attributes.Add(new Tuple("Personality", character.Personality)); - } - - // then look for custom attributes - foreach (var attr in character.CustomAttributes) { - attributes.Add(new Tuple(attr.Name, attr.Value)); - } - - // get important notes - var notes = character.SpecialNotes; - - // get cards - var cards = new List>(); - - // get about card - if (!string.IsNullOrWhiteSpace(character.Profile)) { - cards.Add(new Tuple($"About {character.Name}", character.Profile)); - } - - // get likes/dislikes cards - if (!string.IsNullOrWhiteSpace(character.Likes)) { - cards.Add(new Tuple("Likes", character.Likes)); - } - - if (!string.IsNullOrWhiteSpace(character.Dislikes)) { - cards.Add(new Tuple("Dislikes", character.Dislikes)); - } - - return new RefsheetSection( - this, - $"{name} (Refsheet)", - response.RequestMessage!.RequestUri!, - attributes, - notes, - cards - ); - } - - #pragma warning disable 8618 - [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] - private class RefsheetData { - public RefsheetEagerLoad EagerLoad { get; set; } - } - - [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] - private class RefsheetEagerLoad { - public RefsheetCharacter Character { get; set; } - } - - [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - private class RefsheetCharacter { - public string Name { get; set; } - public string Profile { get; set; } - public string Gender { get; set; } - public string Species { get; set; } - public string Height { get; set; } - public string Weight { get; set; } - public string BodyType { get; set; } - public string Personality { get; set; } - public string SpecialNotes { get; set; } - public string Likes { get; set; } - public string Dislikes { get; set; } - public List CustomAttributes { get; set; } - } - - [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] - private class RefsheetCustomAttribute { - public string Name { get; set; } - public string Value { get; set; } - public string Id { get; set; } - } - #pragma warning restore 8618 + internal RefsheetProvider(Plugin plugin) { + this.Plugin = plugin; } -} + + public override void DrawConfig() { + } + + public override bool Matches(Uri uri) => uri.Host is "refsheet.net" or "ref.st"; + + public override IEnumerable? ExtractUris(uint objectId, string info) => null; + + public override async Task ExtractInfo(HttpResponseMessage response) { + var document = await this.DownloadDocument(response); + + // refsheet provides all the content but... uses js to display it? + // find the script containing the json and use it + var script = document.QuerySelectorAll("script[type = 'text/javascript']").LastOrDefault(); + if (script == null) { + return null; + } + + var jsonLine = script.InnerHtml.Split('\n') + .Select(line => line.Trim()) + .FirstOrDefault(line => line.StartsWith(JsonLineStart)); + if (jsonLine == null) { + return null; + } + + var json = jsonLine.Substring(JsonLineStart.Length, jsonLine.Length - JsonLineStart.Length - 1); + var parsed = JsonConvert.DeserializeObject(json); + if (parsed == null) { + return null; + } + + var character = parsed.EagerLoad.Character; + + // get character name + var name = character.Name; + + // get all attributes + var attributes = new List>(); + + // handle built-in attributes first + if (!string.IsNullOrWhiteSpace(character.Gender)) { + attributes.Add(new Tuple("Gender", character.Gender)); + } + + if (!string.IsNullOrWhiteSpace(character.Species)) { + attributes.Add(new Tuple("Species", character.Species)); + } + + if (!string.IsNullOrWhiteSpace(character.Height)) { + attributes.Add(new Tuple("Height", character.Height)); + } + + if (!string.IsNullOrWhiteSpace(character.Weight)) { + attributes.Add(new Tuple("Weight", character.Weight)); + } + + if (!string.IsNullOrWhiteSpace(character.BodyType)) { + attributes.Add(new Tuple("Body type", character.BodyType)); + } + + if (!string.IsNullOrWhiteSpace(character.Personality)) { + attributes.Add(new Tuple("Personality", character.Personality)); + } + + // then look for custom attributes + foreach (var attr in character.CustomAttributes) { + attributes.Add(new Tuple(attr.Name, attr.Value)); + } + + // get important notes + var notes = character.SpecialNotes; + + // get cards + var cards = new List>(); + + // get about card + if (!string.IsNullOrWhiteSpace(character.Profile)) { + cards.Add(new Tuple($"About {character.Name}", character.Profile)); + } + + // get likes/dislikes cards + if (!string.IsNullOrWhiteSpace(character.Likes)) { + cards.Add(new Tuple("Likes", character.Likes)); + } + + if (!string.IsNullOrWhiteSpace(character.Dislikes)) { + cards.Add(new Tuple("Dislikes", character.Dislikes)); + } + + return new RefsheetSection( + this, + $"{name} (Refsheet)", + response.RequestMessage!.RequestUri!, + attributes, + notes, + cards + ); + } + + #pragma warning disable 8618 + [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] + private class RefsheetData { + public RefsheetEagerLoad EagerLoad { get; set; } + } + + [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))] + private class RefsheetEagerLoad { + public RefsheetCharacter Character { get; set; } + } + + [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + private class RefsheetCharacter { + public string Name { get; set; } + public string Profile { get; set; } + public string Gender { get; set; } + public string Species { get; set; } + public string Height { get; set; } + public string Weight { get; set; } + public string BodyType { get; set; } + public string Personality { get; set; } + public string SpecialNotes { get; set; } + public string Likes { get; set; } + public string Dislikes { get; set; } + public List CustomAttributes { get; set; } + } + + [JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))] + private class RefsheetCustomAttribute { + public string Name { get; set; } + public string Value { get; set; } + public string Id { get; set; } + } + #pragma warning restore 8618 +} \ No newline at end of file diff --git a/ExpandedSearchInfo/SearchInfoRepository.cs b/ExpandedSearchInfo/SearchInfoRepository.cs index 30579b2..fe04a3b 100644 --- a/ExpandedSearchInfo/SearchInfoRepository.cs +++ b/ExpandedSearchInfo/SearchInfoRepository.cs @@ -7,199 +7,198 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Logging; using ExpandedSearchInfo.Providers; using ExpandedSearchInfo.Sections; using Nager.PublicSuffix; -namespace ExpandedSearchInfo { - public class ExpandedSearchInfo { - public string Info { get; } - public List Sections { get; } +namespace ExpandedSearchInfo; - public ExpandedSearchInfo(string info, List sections) { - this.Info = info; - this.Sections = sections; +public class ExpandedSearchInfo { + public string Info { get; } + public List Sections { get; } + + public ExpandedSearchInfo(string info, List sections) { + this.Info = info; + this.Sections = sections; + } +} + +public class SearchInfoRepository : IDisposable { + private Plugin Plugin { get; } + private DomainParser Parser { get; } + internal ConcurrentDictionary SearchInfos { get; } = new(); + internal uint LastObjectId { get; private set; } + + private List Providers { get; } = new(); + internal IEnumerable AllProviders => this.Providers; + + internal SearchInfoRepository(Plugin plugin) { + this.Plugin = plugin; + + // create the public suffix list parser + var provider = new WebTldRuleProvider(); + if (!provider.CacheProvider.IsCacheValid()) { + provider.BuildAsync().GetAwaiter().GetResult(); } + + this.Parser = new DomainParser(provider); + + // add providers + this.AddProviders(); + + // listen for search info + this.Plugin.Functions.ReceiveSearchInfo += this.ProcessSearchInfo; } - public class SearchInfoRepository : IDisposable { - private Plugin Plugin { get; } - private DomainParser Parser { get; } - internal ConcurrentDictionary SearchInfos { get; } = new(); - internal uint LastObjectId { get; private set; } + public void Dispose() { + this.Plugin.Functions.ReceiveSearchInfo -= this.ProcessSearchInfo; + } - private List Providers { get; } = new(); - internal IEnumerable AllProviders => this.Providers; + private void AddProviders() { + this.Providers.Add(new PastebinProvider(this.Plugin)); + this.Providers.Add(new CarrdProvider(this.Plugin)); + this.Providers.Add(new FListProvider(this.Plugin)); + this.Providers.Add(new RefsheetProvider(this.Plugin)); + this.Providers.Add(new PlainTextProvider(this.Plugin)); + } - internal SearchInfoRepository(Plugin plugin) { - this.Plugin = plugin; + private void ProcessSearchInfo(uint objectId, SeString raw) { + this.LastObjectId = objectId; - // create the public suffix list parser - var provider = new WebTldRuleProvider(); - if (!provider.CacheProvider.IsCacheValid()) { - provider.BuildAsync().GetAwaiter().GetResult(); - } + var info = raw.TextValue; - this.Parser = new DomainParser(provider); - - // add providers - this.AddProviders(); - - // listen for search info - this.Plugin.Functions.ReceiveSearchInfo += this.ProcessSearchInfo; + // if empty search info, short circuit + if (string.IsNullOrWhiteSpace(info)) { + // remove any existing search info + this.SearchInfos.TryRemove(objectId, out _); + return; } - public void Dispose() { - this.Plugin.Functions.ReceiveSearchInfo -= this.ProcessSearchInfo; - } - - private void AddProviders() { - this.Providers.Add(new PastebinProvider(this.Plugin)); - this.Providers.Add(new CarrdProvider(this.Plugin)); - this.Providers.Add(new FListProvider(this.Plugin)); - this.Providers.Add(new RefsheetProvider(this.Plugin)); - this.Providers.Add(new PlainTextProvider(this.Plugin)); - } - - private void ProcessSearchInfo(uint objectId, SeString raw) { - this.LastObjectId = objectId; - - var info = raw.TextValue; - - // if empty search info, short circuit - if (string.IsNullOrWhiteSpace(info)) { - // remove any existing search info - this.SearchInfos.TryRemove(objectId, out _); - return; - } - - // check to see if info has changed - #if RELEASE + // check to see if info has changed + #if RELEASE if (this.SearchInfos.TryGetValue(objectId, out var existing)) { if (existing.Info == info) { return; } } - #endif + #endif - Task.Run(async () => { - try { - await this.DoExtraction(objectId, info); - } catch (Exception ex) { - PluginLog.LogError(ex, "Error in extraction thread"); - } - }); - } - - private async Task DoExtraction(uint objectId, string info) { - var downloadUris = new List(); - - // extract uris from the search info with providers - var extractedUris = this.Providers - .Where(provider => provider.Config.Enabled && provider.ExtractsUris) - .Select(provider => provider.ExtractUris(objectId, info)) - .Where(uris => uris != null) - .SelectMany(uris => uris!); - - // add the extracted uris to the list - downloadUris.AddRange(extractedUris); - - // go word-by-word and try to parse a uri - foreach (var word in info.Split(' ', '\n', '\r')) { - Uri found; - try { - found = new UriBuilder(word.Trim()).Uri; - } catch (UriFormatException) { - continue; - } - - // make sure the hostname is a valid domain - try { - if (!this.Parser.IsValidDomain(found.Host)) { - continue; - } - } catch (ParseException) { - continue; - } - - downloadUris.Add(found); + Task.Run(async () => { + try { + await this.DoExtraction(objectId, info); + } catch (Exception ex) { + Plugin.Log.Error(ex, "Error in extraction thread"); } - - // if there were no uris found or extracted, remove existing search info and stop - if (downloadUris.Count == 0) { - this.SearchInfos.TryRemove(objectId, out _); - return; - } - - // do the downloads - await this.DownloadAndExtract(objectId, info, downloadUris); - } - - private async Task DownloadAndExtract(uint objectId, string info, IEnumerable uris) { - var handler = new HttpClientHandler { - UseCookies = true, - AllowAutoRedirect = true, - MaxAutomaticRedirections = 5, - }; - handler.CookieContainer.Add(new Cookie("warning", "1", "/", "www.f-list.net")); - - var version = this.GetType().Assembly.GetName().Version?.ToString(3) ?? "unknown"; - var client = new HttpClient(handler) { - DefaultRequestHeaders = { - UserAgent = { new ProductInfoHeaderValue("ExpandedSearchInfo", version) }, - }, - }; - - var sections = new List(); - - // run through each extracted uri - foreach (var uri in uris) { - if (uri.Scheme is not ("http" or "https")) { - continue; - } - - // find the providers that run on this uri - var matching = this.Providers - .Where(provider => provider.Config.Enabled && provider.Matches(uri)) - .ToList(); - - // skip the uri if no providers - if (matching.Count == 0) { - continue; - } - - // get the http response from the uri and make sure it's ok - var req = new HttpRequestMessage(HttpMethod.Get, uri); - foreach (var provider in matching) { - provider.ModifyRequest(req); - } - - var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead); - if (resp.StatusCode != HttpStatusCode.OK) { - continue; - } - - // go through each provider in order and take the first one that provides info - foreach (var provider in matching) { - var extracted = await provider.ExtractInfo(resp); - if (extracted == null) { - continue; - } - - sections.Add(extracted); - break; - } - } - - // remove expanded search info if no sections resulted - if (sections.Count == 0) { - this.SearchInfos.TryRemove(objectId, out _); - return; - } - - // otherwise set the expanded search info for this actor id - this.SearchInfos[objectId] = new ExpandedSearchInfo(info, sections); - } + }); } -} + + private async Task DoExtraction(uint objectId, string info) { + var downloadUris = new List(); + + // extract uris from the search info with providers + var extractedUris = this.Providers + .Where(provider => provider.Config.Enabled && provider.ExtractsUris) + .Select(provider => provider.ExtractUris(objectId, info)) + .Where(uris => uris != null) + .SelectMany(uris => uris!); + + // add the extracted uris to the list + downloadUris.AddRange(extractedUris); + + // go word-by-word and try to parse a uri + foreach (var word in info.Split(' ', '\n', '\r')) { + Uri found; + try { + found = new UriBuilder(word.Trim()).Uri; + } catch (UriFormatException) { + continue; + } + + // make sure the hostname is a valid domain + try { + if (!this.Parser.IsValidDomain(found.Host)) { + continue; + } + } catch (ParseException) { + continue; + } + + downloadUris.Add(found); + } + + // if there were no uris found or extracted, remove existing search info and stop + if (downloadUris.Count == 0) { + this.SearchInfos.TryRemove(objectId, out _); + return; + } + + // do the downloads + await this.DownloadAndExtract(objectId, info, downloadUris); + } + + private async Task DownloadAndExtract(uint objectId, string info, IEnumerable uris) { + var handler = new HttpClientHandler { + UseCookies = true, + AllowAutoRedirect = true, + MaxAutomaticRedirections = 5, + }; + handler.CookieContainer.Add(new Cookie("warning", "1", "/", "www.f-list.net")); + + var version = this.GetType().Assembly.GetName().Version?.ToString(3) ?? "unknown"; + var client = new HttpClient(handler) { + DefaultRequestHeaders = { + UserAgent = { new ProductInfoHeaderValue("ExpandedSearchInfo", version) }, + }, + }; + + var sections = new List(); + + // run through each extracted uri + foreach (var uri in uris) { + if (uri.Scheme is not ("http" or "https")) { + continue; + } + + // find the providers that run on this uri + var matching = this.Providers + .Where(provider => provider.Config.Enabled && provider.Matches(uri)) + .ToList(); + + // skip the uri if no providers + if (matching.Count == 0) { + continue; + } + + // get the http response from the uri and make sure it's ok + var req = new HttpRequestMessage(HttpMethod.Get, uri); + foreach (var provider in matching) { + provider.ModifyRequest(req); + } + + var resp = await client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead); + if (resp.StatusCode != HttpStatusCode.OK) { + continue; + } + + // go through each provider in order and take the first one that provides info + foreach (var provider in matching) { + var extracted = await provider.ExtractInfo(resp); + if (extracted == null) { + continue; + } + + sections.Add(extracted); + break; + } + } + + // remove expanded search info if no sections resulted + if (sections.Count == 0) { + this.SearchInfos.TryRemove(objectId, out _); + return; + } + + // otherwise set the expanded search info for this actor id + this.SearchInfos[objectId] = new ExpandedSearchInfo(info, sections); + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Sections/FListSection.cs b/ExpandedSearchInfo/Sections/FListSection.cs index f868216..30b5743 100644 --- a/ExpandedSearchInfo/Sections/FListSection.cs +++ b/ExpandedSearchInfo/Sections/FListSection.cs @@ -4,67 +4,67 @@ using System.Linq; using ExpandedSearchInfo.Providers; using ImGuiNET; -namespace ExpandedSearchInfo.Sections { - public class FListSection : ISearchInfoSection { - public IProvider Provider { get; } - public string Name { get; } - public Uri Uri { get; } +namespace ExpandedSearchInfo.Sections; - private string Info { get; } - private List> Stats { get; } - private List> Fave { get; } - private List> Yes { get; } - private List> Maybe { get; } - private List> No { get; } +public class FListSection : ISearchInfoSection { + public IProvider Provider { get; } + public string Name { get; } + public Uri Uri { get; } - internal FListSection(IProvider provider, string name, Uri uri, string info, List> stats, List> fave, List> yes, List> maybe, List> no) { - this.Provider = provider; - this.Name = name; - this.Uri = uri; + private string Info { get; } + private List> Stats { get; } + private List> Fave { get; } + private List> Yes { get; } + private List> Maybe { get; } + private List> No { get; } - this.Info = info; - this.Stats = stats; - this.Fave = fave; - this.Yes = yes; - this.Maybe = maybe; - this.No = no; + internal FListSection(IProvider provider, string name, Uri uri, string info, List> stats, List> fave, List> yes, List> maybe, List> no) { + this.Provider = provider; + this.Name = name; + this.Uri = uri; + + this.Info = info; + this.Stats = stats; + this.Fave = fave; + this.Yes = yes; + this.Maybe = maybe; + this.No = no; + } + + public void Draw() { + if (ImGui.CollapsingHeader($"Stats##{this.Name}")) { + var stats = string.Join("\n", this.Stats.Select(entry => $"{entry.Item1}: {entry.Item2}")); + ImGui.TextUnformatted(stats); } - public void Draw() { - if (ImGui.CollapsingHeader($"Stats##{this.Name}")) { - var stats = string.Join("\n", this.Stats.Select(entry => $"{entry.Item1}: {entry.Item2}")); - ImGui.TextUnformatted(stats); - } - - if (ImGui.CollapsingHeader($"Info##{this.Name}", ImGuiTreeNodeFlags.DefaultOpen)) { - Util.DrawLines(this.Info); - } - - this.DrawKinkSection("Fave", this.Fave); - this.DrawKinkSection("Yes", this.Yes); - this.DrawKinkSection("Maybe", this.Maybe); - this.DrawKinkSection("No", this.No); + if (ImGui.CollapsingHeader($"Info##{this.Name}", ImGuiTreeNodeFlags.DefaultOpen)) { + Util.DrawLines(this.Info); } - private void DrawKinkSection(string sectionName, IEnumerable> kinks) { - if (!ImGui.CollapsingHeader($"{sectionName}##{this.Name}")) { - return; + this.DrawKinkSection("Fave", this.Fave); + this.DrawKinkSection("Yes", this.Yes); + this.DrawKinkSection("Maybe", this.Maybe); + this.DrawKinkSection("No", this.No); + } + + private void DrawKinkSection(string sectionName, IEnumerable> kinks) { + if (!ImGui.CollapsingHeader($"{sectionName}##{this.Name}")) { + return; + } + + foreach (var (name, description) in kinks) { + ImGui.TextUnformatted(name); + if (!ImGui.IsItemHovered()) { + continue; } - foreach (var (name, description) in kinks) { - ImGui.TextUnformatted(name); - if (!ImGui.IsItemHovered()) { - continue; - } + ImGui.BeginTooltip(); - ImGui.BeginTooltip(); + ImGui.PushTextWrapPos(ImGui.GetIO().DisplaySize.X / 8); + ImGui.TextUnformatted(description); + ImGui.PopTextWrapPos(); - ImGui.PushTextWrapPos(ImGui.GetIO().DisplaySize.X / 8); - ImGui.TextUnformatted(description); - ImGui.PopTextWrapPos(); - - ImGui.EndTooltip(); - } + ImGui.EndTooltip(); } } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Sections/ISearchInfoSection.cs b/ExpandedSearchInfo/Sections/ISearchInfoSection.cs index 08d1cf8..669fb8b 100644 --- a/ExpandedSearchInfo/Sections/ISearchInfoSection.cs +++ b/ExpandedSearchInfo/Sections/ISearchInfoSection.cs @@ -1,12 +1,12 @@ using System; using ExpandedSearchInfo.Providers; -namespace ExpandedSearchInfo.Sections { - public interface ISearchInfoSection { - IProvider Provider { get; } - string Name { get; } - Uri Uri { get; } +namespace ExpandedSearchInfo.Sections; - void Draw(); - } -} +public interface ISearchInfoSection { + IProvider Provider { get; } + string Name { get; } + Uri Uri { get; } + + void Draw(); +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Sections/RefsheetSection.cs b/ExpandedSearchInfo/Sections/RefsheetSection.cs index 60602db..cfc30e2 100644 --- a/ExpandedSearchInfo/Sections/RefsheetSection.cs +++ b/ExpandedSearchInfo/Sections/RefsheetSection.cs @@ -3,43 +3,43 @@ using System.Collections.Generic; using ExpandedSearchInfo.Providers; using ImGuiNET; -namespace ExpandedSearchInfo.Sections { - public class RefsheetSection : ISearchInfoSection { - public IProvider Provider { get; } - public string Name { get; } - public Uri Uri { get; } +namespace ExpandedSearchInfo.Sections; - private List> Attributes { get; } - private string Notes { get; } - private List> Cards { get; } +public class RefsheetSection : ISearchInfoSection { + public IProvider Provider { get; } + public string Name { get; } + public Uri Uri { get; } - internal RefsheetSection(IProvider provider, string name, Uri uri, List> attributes, string notes, List> cards) { - this.Provider = provider; - this.Name = name; - this.Uri = uri; - this.Attributes = attributes; - this.Notes = notes; - this.Cards = cards; + private List> Attributes { get; } + private string Notes { get; } + private List> Cards { get; } + + internal RefsheetSection(IProvider provider, string name, Uri uri, List> attributes, string notes, List> cards) { + this.Provider = provider; + this.Name = name; + this.Uri = uri; + this.Attributes = attributes; + this.Notes = notes; + this.Cards = cards; + } + + public void Draw() { + if (ImGui.CollapsingHeader($"Attributes##{this.Name}")) { + foreach (var (key, value) in this.Attributes) { + ImGui.TextUnformatted($"{key}: {value}"); + } } - public void Draw() { - if (ImGui.CollapsingHeader($"Attributes##{this.Name}")) { - foreach (var (key, value) in this.Attributes) { - ImGui.TextUnformatted($"{key}: {value}"); - } + if (ImGui.CollapsingHeader($"Notes##{this.Name}")) { + Util.DrawLines(this.Notes); + } + + foreach (var (cardName, cardContent) in this.Cards) { + if (!ImGui.CollapsingHeader($"{cardName}##{this.Name}")) { + continue; } - if (ImGui.CollapsingHeader($"Notes##{this.Name}")) { - Util.DrawLines(this.Notes); - } - - foreach (var (cardName, cardContent) in this.Cards) { - if (!ImGui.CollapsingHeader($"{cardName}##{this.Name}")) { - continue; - } - - Util.DrawLines(cardContent); - } + Util.DrawLines(cardContent); } } -} +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Sections/TextSection.cs b/ExpandedSearchInfo/Sections/TextSection.cs index 31ad051..5507904 100644 --- a/ExpandedSearchInfo/Sections/TextSection.cs +++ b/ExpandedSearchInfo/Sections/TextSection.cs @@ -1,23 +1,23 @@ using System; using ExpandedSearchInfo.Providers; -namespace ExpandedSearchInfo.Sections { - public class TextSection : ISearchInfoSection { - public IProvider Provider { get; } - public string Name { get; } - public Uri Uri { get; } +namespace ExpandedSearchInfo.Sections; - private string Info { get; } +public class TextSection : ISearchInfoSection { + public IProvider Provider { get; } + public string Name { get; } + public Uri Uri { get; } - internal TextSection(IProvider provider, string name, Uri uri, string info) { - this.Provider = provider; - this.Name = name; - this.Uri = uri; - this.Info = info; - } + private string Info { get; } - public void Draw() { - Util.DrawLines(this.Info); - } + internal TextSection(IProvider provider, string name, Uri uri, string info) { + this.Provider = provider; + this.Name = name; + this.Uri = uri; + this.Info = info; } -} + + public void Draw() { + Util.DrawLines(this.Info); + } +} \ No newline at end of file diff --git a/ExpandedSearchInfo/Util.cs b/ExpandedSearchInfo/Util.cs index cf0085d..c3ed657 100644 --- a/ExpandedSearchInfo/Util.cs +++ b/ExpandedSearchInfo/Util.cs @@ -4,29 +4,29 @@ using System.Text.RegularExpressions; using Dalamud.Game.Text.SeStringHandling; using ImGuiNET; -namespace ExpandedSearchInfo { - internal static class Util { - private static readonly Regex BbCodeTag = new(@"\[/?\w+(?:=.+?)?\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); +namespace ExpandedSearchInfo; - internal static unsafe SeString ReadRawSeString(IntPtr data) { - var bytes = new List(); +internal static class Util { + private static readonly Regex BbCodeTag = new(@"\[/?\w+(?:=.+?)?\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); - var ptr = (byte*) data; - while (*ptr != 0) { - bytes.Add(*ptr); - ptr += 1; - } + internal static unsafe SeString ReadRawSeString(IntPtr data) { + var bytes = new List(); - return SeString.Parse(bytes.ToArray()); + var ptr = (byte*) data; + while (*ptr != 0) { + bytes.Add(*ptr); + ptr += 1; } - internal static string StripBbCode(this string input) => BbCodeTag.Replace(input, ""); + return SeString.Parse(bytes.ToArray()); + } - internal static void DrawLines(string input) { - // FIXME: this is a workaround for imgui breaking on extremely long strings - foreach (var line in input.Split(new[] {"\n", "\r", "\r\n"}, StringSplitOptions.None)) { - ImGui.TextUnformatted(line); - } + internal static string StripBbCode(this string input) => BbCodeTag.Replace(input, ""); + + internal static void DrawLines(string input) { + // FIXME: this is a workaround for imgui breaking on extremely long strings + foreach (var line in input.Split(new[] {"\n", "\r", "\r\n"}, StringSplitOptions.None)) { + ImGui.TextUnformatted(line); } } -} +} \ No newline at end of file