diff --git a/ExpandedSearchInfo/ExpandedSearchInfo.csproj b/ExpandedSearchInfo/ExpandedSearchInfo.csproj
index 701406c..708a27c 100755
--- a/ExpandedSearchInfo/ExpandedSearchInfo.csproj
+++ b/ExpandedSearchInfo/ExpandedSearchInfo.csproj
@@ -1,11 +1,13 @@
- net48
+ net5-windows
1.3.4
latest
enable
true
+ false
+ true
@@ -13,6 +15,10 @@
$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll
False
+
+ $(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll
+ False
+
$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll
False
@@ -29,10 +35,12 @@
-
-
-
+
+
+
+
+
diff --git a/ExpandedSearchInfo/ExpandedSearchInfo.yaml b/ExpandedSearchInfo/ExpandedSearchInfo.yaml
index dc7ff6c..26f3985 100644
--- a/ExpandedSearchInfo/ExpandedSearchInfo.yaml
+++ b/ExpandedSearchInfo/ExpandedSearchInfo.yaml
@@ -1,5 +1,6 @@
author: ascclemens
name: Expanded Search Info
+punchline: Displays extra information pulled from search info when examining.
description: |-
Displays extra information pulled from search info when examining.
@@ -12,4 +13,6 @@ description: |-
Simply examine someone with a search info containing pointers to one of
these locations and the plugin will display information automatically.
+
+ Icon: expand by Gregor Cresnar from the Noun Project
repo_url: https://git.sr.ht/~jkcclemens/ExpandedSearchInfo
diff --git a/ExpandedSearchInfo/FodyWeavers.xml b/ExpandedSearchInfo/FodyWeavers.xml
deleted file mode 100755
index 2dfb1f4..0000000
--- a/ExpandedSearchInfo/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/ExpandedSearchInfo/GameFunctions.cs b/ExpandedSearchInfo/GameFunctions.cs
index ef9d9fe..d1c8cc7 100644
--- a/ExpandedSearchInfo/GameFunctions.cs
+++ b/ExpandedSearchInfo/GameFunctions.cs
@@ -1,8 +1,7 @@
using System;
-using System.Runtime.InteropServices;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Hooking;
-using Dalamud.Plugin;
+using Dalamud.Logging;
namespace ExpandedSearchInfo {
public class GameFunctions : IDisposable {
@@ -12,15 +11,15 @@ namespace ExpandedSearchInfo {
private readonly Hook? _searchInfoDownloadedHook;
- internal delegate void ReceiveSearchInfoEventDelegate(int actorId, SeString info);
+ internal delegate void ReceiveSearchInfoEventDelegate(uint objectId, SeString info);
internal event ReceiveSearchInfoEventDelegate? ReceiveSearchInfo;
internal GameFunctions(Plugin plugin) {
this.Plugin = plugin;
- var sidPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 56 48 83 EC 20 49 8B E8 8B DA");
- this._searchInfoDownloadedHook = new Hook(sidPtr, new SearchInfoDownloadedDelegate(this.SearchInfoDownloaded));
+ 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 = new Hook(sidPtr, this.SearchInfoDownloaded);
this._searchInfoDownloadedHook.Enable();
}
@@ -28,14 +27,14 @@ namespace ExpandedSearchInfo {
this._searchInfoDownloadedHook?.Dispose();
}
- private byte SearchInfoDownloaded(IntPtr data, IntPtr a2, IntPtr searchInfoPtr, IntPtr a4) {
+ 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 = Marshal.ReadInt32(data + 48);
+ var actorId = *(uint*) (data + 48);
- var searchInfo = this.Plugin.Interface.SeStringManager.ReadRawSeString(searchInfoPtr);
+ var searchInfo = this.Plugin.SeStringManager.ReadRawSeString(searchInfoPtr);
this.ReceiveSearchInfo?.Invoke(actorId, searchInfo);
} catch (Exception ex) {
diff --git a/ExpandedSearchInfo/Plugin.cs b/ExpandedSearchInfo/Plugin.cs
index c03910a..df9234a 100644
--- a/ExpandedSearchInfo/Plugin.cs
+++ b/ExpandedSearchInfo/Plugin.cs
@@ -1,4 +1,9 @@
-using Dalamud.Game.Command;
+using Dalamud.Game;
+using Dalamud.Game.ClientState.Objects;
+using Dalamud.Game.Command;
+using Dalamud.Game.Gui;
+using Dalamud.Game.Text.SeStringHandling;
+using Dalamud.IoC;
using Dalamud.Plugin;
namespace ExpandedSearchInfo {
@@ -6,15 +11,30 @@ namespace ExpandedSearchInfo {
public class Plugin : IDalamudPlugin {
public string Name => "Expanded Search Info";
- internal PluginConfiguration Config { get; private set; } = null!;
- internal DalamudPluginInterface Interface { get; private set; } = null!;
- internal GameFunctions Functions { get; private set; } = null!;
- internal SearchInfoRepository Repository { get; private set; } = null!;
- private PluginUi Ui { get; set; } = null!;
+ [PluginService]
+ internal DalamudPluginInterface Interface { get; init; } = null!;
- public void Initialize(DalamudPluginInterface pluginInterface) {
- this.Interface = pluginInterface;
+ [PluginService]
+ internal CommandManager CommandManager { get; init; } = null!;
+ [PluginService]
+ internal GameGui GameGui { get; init; } = null!;
+
+ [PluginService]
+ internal ObjectTable ObjectTable { get; init; } = null!;
+
+ [PluginService]
+ internal SeStringManager SeStringManager { get; init; } = null!;
+
+ [PluginService]
+ internal SigScanner SigScanner { get; init; } = null!;
+
+ internal PluginConfiguration Config { get; }
+ internal GameFunctions Functions { get; }
+ internal SearchInfoRepository Repository { get; }
+ private PluginUi Ui { get; }
+
+ public Plugin() {
this.Config = (PluginConfiguration?) this.Interface.GetPluginConfig() ?? new PluginConfiguration();
this.Config.Initialise(this);
@@ -22,13 +42,13 @@ namespace ExpandedSearchInfo {
this.Repository = new SearchInfoRepository(this);
this.Ui = new PluginUi(this);
- this.Interface.CommandManager.AddHandler("/esi", new CommandInfo(this.OnCommand) {
+ this.CommandManager.AddHandler("/esi", new CommandInfo(this.OnCommand) {
HelpMessage = "Toggles Expanded Search Info's configuration window",
});
}
public void Dispose() {
- this.Interface.CommandManager.RemoveHandler("/esi");
+ this.CommandManager.RemoveHandler("/esi");
this.Ui.Dispose();
this.Repository.Dispose();
this.Functions.Dispose();
diff --git a/ExpandedSearchInfo/PluginUi.cs b/ExpandedSearchInfo/PluginUi.cs
index e9c10c6..2f200fe 100644
--- a/ExpandedSearchInfo/PluginUi.cs
+++ b/ExpandedSearchInfo/PluginUi.cs
@@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Numerics;
using Dalamud.Interface;
+using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
namespace ExpandedSearchInfo {
@@ -18,17 +19,17 @@ namespace ExpandedSearchInfo {
internal PluginUi(Plugin plugin) {
this.Plugin = plugin;
- this.Plugin.Interface.UiBuilder.OnBuildUi += this.Draw;
- this.Plugin.Interface.UiBuilder.OnOpenConfigUi += this.OnOpenConfigUi;
+ this.Plugin.Interface.UiBuilder.Draw += this.Draw;
+ this.Plugin.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi;
}
- private void OnOpenConfigUi(object sender, EventArgs e) {
+ private void OnOpenConfigUi() {
this.ConfigVisible = true;
}
public void Dispose() {
- this.Plugin.Interface.UiBuilder.OnOpenConfigUi -= this.OnOpenConfigUi;
- this.Plugin.Interface.UiBuilder.OnBuildUi -= this.Draw;
+ this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.OnOpenConfigUi;
+ this.Plugin.Interface.UiBuilder.Draw -= this.Draw;
}
private static bool IconButton(FontAwesomeIcon icon, string? id = null) {
@@ -128,30 +129,31 @@ namespace ExpandedSearchInfo {
ImGui.End();
}
- private void DrawExpandedSearchInfo() {
+ private unsafe void DrawExpandedSearchInfo() {
// check if the examine window is open
- var addon = this.Plugin.Interface.Framework.Gui.GetAddonByName("CharacterInspect", 1);
- if (addon is not {Visible: true}) {
+ 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
- float width;
- float height;
- short x;
- short y;
-
- try {
- width = addon.Width;
- height = addon.Height;
- x = addon.X;
- y = addon.Y;
- } catch (Exception) {
+ 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.LastActorId;
+ var actorId = this.Plugin.Repository.LastObjectId;
if (actorId == 0 || !this.Plugin.Repository.SearchInfos.TryGetValue(actorId, out var expanded)) {
return;
}
diff --git a/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs b/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs
index f57e979..e1e2f0f 100644
--- a/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs
+++ b/ExpandedSearchInfo/Providers/BaseHtmlProvider.cs
@@ -24,7 +24,7 @@ namespace ExpandedSearchInfo.Providers {
public abstract bool Matches(Uri uri);
- public abstract IEnumerable? ExtractUris(int actorId, string info);
+ public abstract IEnumerable? ExtractUris(uint objectId, string info);
public abstract Task ExtractInfo(HttpResponseMessage response);
diff --git a/ExpandedSearchInfo/Providers/CarrdProvider.cs b/ExpandedSearchInfo/Providers/CarrdProvider.cs
index bc28ebe..49f0f4d 100644
--- a/ExpandedSearchInfo/Providers/CarrdProvider.cs
+++ b/ExpandedSearchInfo/Providers/CarrdProvider.cs
@@ -34,7 +34,7 @@ namespace ExpandedSearchInfo.Providers {
public override bool Matches(Uri uri) => Domains.Any(domain => uri.Host.EndsWith(domain));
- public override IEnumerable? ExtractUris(int actorId, string info) => null;
+ public override IEnumerable? ExtractUris(uint objectId, string info) => null;
public override async Task ExtractInfo(HttpResponseMessage response) {
var document = await this.DownloadDocument(response);
@@ -83,7 +83,7 @@ namespace ExpandedSearchInfo.Providers {
return new TextSection(
this,
$"{document.Title} (Carrd)",
- response.RequestMessage.RequestUri,
+ response.RequestMessage!.RequestUri!,
text
);
}
diff --git a/ExpandedSearchInfo/Providers/FListProvider.cs b/ExpandedSearchInfo/Providers/FListProvider.cs
index 59ab496..1733df0 100644
--- a/ExpandedSearchInfo/Providers/FListProvider.cs
+++ b/ExpandedSearchInfo/Providers/FListProvider.cs
@@ -27,19 +27,19 @@ namespace ExpandedSearchInfo.Providers {
public override void DrawConfig() {
}
- public override bool Matches(Uri uri) => (uri.Host == "www.f-list.net" || uri.Host == "f-list.net") && uri.AbsolutePath.StartsWith("/c/");
+ 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(int actorId, string info) {
+ public override IEnumerable? ExtractUris(uint objectId, string info) {
if (!info.ToLowerInvariant().Contains("c/")) {
return null;
}
- var actor = this.Plugin.Interface.ClientState.Actors.FirstOrDefault(actor => actor.ActorId == actorId);
- if (actor == null) {
+ var obj = this.Plugin.ObjectTable.FirstOrDefault(obj => obj.ObjectId == objectId);
+ if (obj == null) {
return null;
}
- var safeName = actor.Name.Replace("'", "");
+ var safeName = obj.Name.ToString().Replace("'", "");
return new[] {
new Uri($"https://www.f-list.net/c/{Uri.EscapeUriString(safeName)}"),
@@ -99,7 +99,7 @@ namespace ExpandedSearchInfo.Providers {
return new FListSection(
this,
$"{charName} (F-List)",
- response.RequestMessage.RequestUri,
+ response.RequestMessage!.RequestUri!,
info,
stats,
fave,
diff --git a/ExpandedSearchInfo/Providers/IProvider.cs b/ExpandedSearchInfo/Providers/IProvider.cs
index b0cd31c..dfa2dd1 100644
--- a/ExpandedSearchInfo/Providers/IProvider.cs
+++ b/ExpandedSearchInfo/Providers/IProvider.cs
@@ -34,10 +34,10 @@ namespace ExpandedSearchInfo.Providers {
/// 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
+ /// 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(int actorId, string info);
+ IEnumerable? ExtractUris(uint objectId, string info);
///
/// Extract the search info to be displayed given the HTTP response from a Uri.
diff --git a/ExpandedSearchInfo/Providers/PastebinProvider.cs b/ExpandedSearchInfo/Providers/PastebinProvider.cs
index 1a5fea9..ccb1a58 100644
--- a/ExpandedSearchInfo/Providers/PastebinProvider.cs
+++ b/ExpandedSearchInfo/Providers/PastebinProvider.cs
@@ -30,7 +30,7 @@ namespace ExpandedSearchInfo.Providers {
public bool Matches(Uri uri) => uri.Host == "pastebin.com" && uri.AbsolutePath.Length > 1;
- public IEnumerable? ExtractUris(int actorId, string info) {
+ public IEnumerable? ExtractUris(uint objectId, string info) {
var matches = Matcher.Matches(info);
return matches.Count == 0
? null
@@ -38,11 +38,11 @@ namespace ExpandedSearchInfo.Providers {
}
public async Task ExtractInfo(HttpResponseMessage response) {
- if (response.Content.Headers.ContentType.MediaType != "text/plain") {
+ if (response.Content.Headers.ContentType?.MediaType != "text/plain") {
return null;
}
- var id = response.RequestMessage.RequestUri.AbsolutePath.Split('/').LastOrDefault();
+ var id = response.RequestMessage!.RequestUri!.AbsolutePath.Split('/').LastOrDefault();
var info = await response.Content.ReadAsStringAsync();
diff --git a/ExpandedSearchInfo/Providers/PlainTextProvider.cs b/ExpandedSearchInfo/Providers/PlainTextProvider.cs
index 28258fc..c4d9b28 100644
--- a/ExpandedSearchInfo/Providers/PlainTextProvider.cs
+++ b/ExpandedSearchInfo/Providers/PlainTextProvider.cs
@@ -26,17 +26,17 @@ namespace ExpandedSearchInfo.Providers {
public bool Matches(Uri uri) => true;
- public IEnumerable? ExtractUris(int actorId, string info) => null;
+ public IEnumerable? ExtractUris(uint objectId, string info) => null;
public async Task ExtractInfo(HttpResponseMessage response) {
- if (response.Content.Headers.ContentType.MediaType != "text/plain") {
+ 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}", response.RequestMessage.RequestUri, info);
+ var uri = response.RequestMessage!.RequestUri!;
+ return new TextSection(this, $"Text##{uri}", uri, info);
}
}
}
diff --git a/ExpandedSearchInfo/Providers/RefsheetProvider.cs b/ExpandedSearchInfo/Providers/RefsheetProvider.cs
index a989862..d0616c9 100644
--- a/ExpandedSearchInfo/Providers/RefsheetProvider.cs
+++ b/ExpandedSearchInfo/Providers/RefsheetProvider.cs
@@ -29,9 +29,9 @@ namespace ExpandedSearchInfo.Providers {
public override void DrawConfig() {
}
- public override bool Matches(Uri uri) => uri.Host == "refsheet.net" || uri.Host == "ref.st";
+ public override bool Matches(Uri uri) => uri.Host is "refsheet.net" or "ref.st";
- public override IEnumerable? ExtractUris(int actorId, string info) => null;
+ public override IEnumerable? ExtractUris(uint objectId, string info) => null;
public override async Task ExtractInfo(HttpResponseMessage response) {
var document = await this.DownloadDocument(response);
@@ -52,6 +52,10 @@ namespace ExpandedSearchInfo.Providers {
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
@@ -113,7 +117,7 @@ namespace ExpandedSearchInfo.Providers {
return new RefsheetSection(
this,
$"{name} (Refsheet)",
- response.RequestMessage.RequestUri,
+ response.RequestMessage!.RequestUri!,
attributes,
notes,
cards
diff --git a/ExpandedSearchInfo/SearchInfoRepository.cs b/ExpandedSearchInfo/SearchInfoRepository.cs
index 7940148..ca2b1af 100644
--- a/ExpandedSearchInfo/SearchInfoRepository.cs
+++ b/ExpandedSearchInfo/SearchInfoRepository.cs
@@ -4,10 +4,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
-using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.Text.SeStringHandling;
-using Dalamud.Plugin;
+using Dalamud.Logging;
using ExpandedSearchInfo.Providers;
using ExpandedSearchInfo.Sections;
using Nager.PublicSuffix;
@@ -26,8 +25,8 @@ namespace ExpandedSearchInfo {
public class SearchInfoRepository : IDisposable {
private Plugin Plugin { get; }
private DomainParser Parser { get; }
- internal ConcurrentDictionary SearchInfos { get; } = new();
- internal int LastActorId { get; private set; }
+ internal ConcurrentDictionary SearchInfos { get; } = new();
+ internal uint LastObjectId { get; private set; }
private List Providers { get; } = new();
internal IEnumerable AllProviders => this.Providers;
@@ -62,48 +61,48 @@ namespace ExpandedSearchInfo {
this.Providers.Add(new PlainTextProvider(this.Plugin));
}
- private void ProcessSearchInfo(int actorId, SeString raw) {
- this.LastActorId = actorId;
+ 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(actorId, out _);
+ this.SearchInfos.TryRemove(objectId, out _);
return;
}
// check to see if info has changed
#if RELEASE
- if (this.SearchInfos.TryGetValue(actorId, out var existing)) {
+ if (this.SearchInfos.TryGetValue(objectId, out var existing)) {
if (existing.Info == info) {
return;
}
}
#endif
- new Thread(async () => {
+ Task.Run(async () => {
try {
- await this.DoExtraction(actorId, info);
+ await this.DoExtraction(objectId, info);
} catch (Exception ex) {
PluginLog.LogError($"Error in extraction thread:\n{ex}");
}
- }).Start();
+ });
}
- private async Task DoExtraction(int actorId, string info) {
+ 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(actorId, info))
+ .Select(provider => provider.ExtractUris(objectId, info))
.Where(uris => uris != null)
- .SelectMany(uris => uris);
+ .SelectMany(uris => uris!);
// add the extracted uris to the list
- downloadUris.AddRange(extractedUris!);
+ downloadUris.AddRange(extractedUris);
// go word-by-word and try to parse a uri
foreach (var word in info.Split(' ', '\n', '\r')) {
@@ -128,15 +127,15 @@ namespace ExpandedSearchInfo {
// if there were no uris found or extracted, remove existing search info and stop
if (downloadUris.Count == 0) {
- this.SearchInfos.TryRemove(actorId, out _);
+ this.SearchInfos.TryRemove(objectId, out _);
return;
}
// do the downloads
- await this.DownloadAndExtract(actorId, info, downloadUris);
+ await this.DownloadAndExtract(objectId, info, downloadUris);
}
- private async Task DownloadAndExtract(int actorId, string info, IEnumerable uris) {
+ private async Task DownloadAndExtract(uint objectId, string info, IEnumerable uris) {
var handler = new HttpClientHandler {
UseCookies = true,
AllowAutoRedirect = true,
@@ -183,12 +182,12 @@ namespace ExpandedSearchInfo {
// remove expanded search info if no sections resulted
if (sections.Count == 0) {
- this.SearchInfos.TryRemove(actorId, out _);
+ this.SearchInfos.TryRemove(objectId, out _);
return;
}
// otherwise set the expanded search info for this actor id
- this.SearchInfos[actorId] = new ExpandedSearchInfo(info, sections);
+ this.SearchInfos[objectId] = new ExpandedSearchInfo(info, sections);
}
}
}
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..0a8e7c2
Binary files /dev/null and b/icon.png differ
diff --git a/icon.svg b/icon.svg
new file mode 100755
index 0000000..95182db
--- /dev/null
+++ b/icon.svg
@@ -0,0 +1,76 @@
+
+