diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index 202b394c6..479297c20 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -16,6 +16,7 @@ using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Windows;
+using Dalamud.Interface.Internal.Windows.Data;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Internal.Windows.SelfTest;
using Dalamud.Interface.Internal.Windows.Settings;
diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs
index 3b3c4e003..d1e7a6b78 100644
--- a/Dalamud/Interface/Internal/UiDebug.cs
+++ b/Dalamud/Interface/Internal/UiDebug.cs
@@ -53,8 +53,6 @@ internal unsafe class UiDebug
{
}
- private delegate AtkStage* GetAtkStageSingleton();
-
///
/// Renders this window.
///
@@ -165,7 +163,7 @@ internal unsafe class UiDebug
private void PrintSimpleNode(AtkResNode* node, string treePrefix)
{
var popped = false;
- var isVisible = (node->Flags & 0x10) == 0x10;
+ var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible);
if (isVisible)
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255));
@@ -296,7 +294,7 @@ internal unsafe class UiDebug
var compNode = (AtkComponentNode*)node;
var popped = false;
- var isVisible = (node->Flags & 0x10) == 0x10;
+ var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible);
var componentInfo = compNode->Component->UldManager;
@@ -396,7 +394,7 @@ internal unsafe class UiDebug
ImGui.SameLine();
if (ImGui.SmallButton($"T:Visible##{(ulong)node:X}"))
{
- node->Flags ^= 0x10;
+ node->NodeFlags ^= NodeFlags.Visible;
}
ImGui.SameLine();
@@ -573,7 +571,7 @@ internal unsafe class UiDebug
if (node == null) return false;
while (node != null)
{
- if ((node->Flags & (short)NodeFlags.Visible) != (short)NodeFlags.Visible) return false;
+ if (!node->NodeFlags.HasFlag(NodeFlags.Visible)) return false;
node = node->ParentNode;
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/DataKindEnum.cs b/Dalamud/Interface/Internal/Windows/Data/DataKindEnum.cs
new file mode 100644
index 000000000..99c6cb6e9
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/DataKindEnum.cs
@@ -0,0 +1,158 @@
+// ReSharper disable InconsistentNaming // Naming is suppressed so we can replace '_' with ' '
+namespace Dalamud.Interface.Internal.Windows;
+
+///
+/// Enum representing a DataKind for the Data Window.
+///
+internal enum DataKind
+{
+ ///
+ /// Server Opcode Display.
+ ///
+ Server_OpCode,
+
+ ///
+ /// Address.
+ ///
+ Address,
+
+ ///
+ /// Object Table.
+ ///
+ Object_Table,
+
+ ///
+ /// Fate Table.
+ ///
+ Fate_Table,
+
+ ///
+ /// SE Font Test.
+ ///
+ SE_Font_Test,
+
+ ///
+ /// FontAwesome Test.
+ ///
+ FontAwesome_Test,
+
+ ///
+ /// Party List.
+ ///
+ Party_List,
+
+ ///
+ /// Buddy List.
+ ///
+ Buddy_List,
+
+ ///
+ /// Plugin IPC Test.
+ ///
+ Plugin_IPC,
+
+ ///
+ /// Player Condition.
+ ///
+ Condition,
+
+ ///
+ /// Gauge.
+ ///
+ Gauge,
+
+ ///
+ /// Command.
+ ///
+ Command,
+
+ ///
+ /// Addon.
+ ///
+ Addon,
+
+ ///
+ /// Addon Inspector.
+ ///
+ Addon_Inspector,
+
+ ///
+ /// AtkArrayData Browser.
+ ///
+ AtkArrayData_Browser,
+
+ ///
+ /// StartInfo.
+ ///
+ StartInfo,
+
+ ///
+ /// Target.
+ ///
+ Target,
+
+ ///
+ /// Toast.
+ ///
+ Toast,
+
+ ///
+ /// Fly Text.
+ ///
+ FlyText,
+
+ ///
+ /// ImGui.
+ ///
+ ImGui,
+
+ ///
+ /// Tex.
+ ///
+ Tex,
+
+ ///
+ /// KeyState.
+ ///
+ KeyState,
+
+ ///
+ /// GamePad.
+ ///
+ Gamepad,
+
+ ///
+ /// Configuration.
+ ///
+ Configuration,
+
+ ///
+ /// Task Scheduler.
+ ///
+ TaskSched,
+
+ ///
+ /// Hook.
+ ///
+ Hook,
+
+ ///
+ /// Aetherytes.
+ ///
+ Aetherytes,
+
+ ///
+ /// DTR Bar.
+ ///
+ Dtr_Bar,
+
+ ///
+ /// UIColor.
+ ///
+ UIColor,
+
+ ///
+ /// Data Share.
+ ///
+ DataShare,
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
new file mode 100644
index 000000000..f392d3912
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/DataWindow.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+
+using Dalamud.Game.Gui;
+using Dalamud.Interface.Components;
+using Dalamud.Interface.Windowing;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Class responsible for drawing the data/debug window.
+///
+internal class DataWindow : Window
+{
+ private readonly IDataWindowWidget[] modules =
+ {
+ new ServerOpcodeWidget(),
+ new AddressesWidget(),
+ new ObjectTableWidget(),
+ new FateTableWidget(),
+ new SeFontTestWidget(),
+ new FontAwesomeTestWidget(),
+ new PartyListWidget(),
+ new BuddyListWidget(),
+ new PluginIpcWidget(),
+ new ConditionWidget(),
+ new GaugeWidget(),
+ new CommandWidget(),
+ new AddonWidget(),
+ new AddonInspectorWidget(),
+ new AtkArrayDataBrowserWidget(),
+ new StartInfoWidget(),
+ new TargetWidget(),
+ new ToastWidget(),
+ new FlyTextWidget(),
+ new ImGuiWidget(),
+ new TexWidget(),
+ new KeyStateWidget(),
+ new GamepadWidget(),
+ new ConfigurationWidget(),
+ new TaskSchedulerWidget(),
+ new HookWidget(),
+ new AetherytesWidget(),
+ new DtrBarWidget(),
+ new UIColorWidget(),
+ new DataShareWidget(),
+ };
+
+ private readonly Dictionary dataKindNames = new();
+
+ private bool isExcept;
+ private DataKind currentKind;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DataWindow()
+ : base("Dalamud Data")
+ {
+ this.Size = new Vector2(500, 500);
+ this.SizeCondition = ImGuiCond.FirstUseEver;
+
+ this.RespectCloseHotkey = false;
+
+ foreach (var dataKind in Enum.GetValues())
+ {
+ this.dataKindNames[dataKind] = dataKind.ToString().Replace("_", " ");
+ }
+
+ this.Load();
+ }
+
+ ///
+ public override void OnOpen()
+ {
+ }
+
+ ///
+ public override void OnClose()
+ {
+ }
+
+ ///
+ /// Set the DataKind dropdown menu.
+ ///
+ /// Data kind name, can be lower and/or without spaces.
+ public void SetDataKind(string dataKind)
+ {
+ if (string.IsNullOrEmpty(dataKind))
+ return;
+
+ dataKind = dataKind switch
+ {
+ "ai" => "Addon Inspector",
+ "at" => "Object Table", // Actor Table
+ "ot" => "Object Table",
+ "uic" => "UIColor",
+ _ => dataKind,
+ };
+
+ dataKind = dataKind.Replace(" ", string.Empty).ToLower();
+
+ var matched = Enum
+ .GetValues()
+ .FirstOrDefault(kind => Enum.GetName(kind)?.Replace("_", string.Empty).ToLower() == dataKind);
+
+ if (matched != default)
+ {
+ this.currentKind = matched;
+ }
+ else
+ {
+ Service.Get().PrintError($"/xldata: Invalid data type {dataKind}");
+ }
+ }
+
+ ///
+ /// Draw the window via ImGui.
+ ///
+ public override void Draw()
+ {
+ if (ImGuiComponents.IconButton("forceReload", FontAwesomeIcon.Sync)) this.Load();
+ if (ImGui.IsItemHovered()) ImGui.SetTooltip("Force Reload");
+ ImGui.SameLine();
+ var copy = ImGuiComponents.IconButton("copyAll", FontAwesomeIcon.ClipboardList);
+ if (ImGui.IsItemHovered()) ImGui.SetTooltip("Copy All");
+ ImGui.SameLine();
+
+ ImGui.SetNextItemWidth(275.0f * ImGuiHelpers.GlobalScale);
+ if (ImGui.BeginCombo("Data Kind", this.dataKindNames[this.currentKind]))
+ {
+ foreach (var module in this.modules.OrderBy(module => this.dataKindNames[module.DataKind]))
+ {
+ if (ImGui.Selectable(this.dataKindNames[module.DataKind], this.currentKind == module.DataKind))
+ {
+ this.currentKind = module.DataKind;
+ }
+ }
+
+ ImGui.EndCombo();
+ }
+
+ ImGuiHelpers.ScaledDummy(10.0f);
+
+ ImGui.BeginChild("scrolling", Vector2.Zero, false, ImGuiWindowFlags.HorizontalScrollbar);
+
+ if (copy)
+ ImGui.LogToClipboard();
+
+ try
+ {
+ var selectedWidget = this.modules.FirstOrDefault(dataWindowWidget => dataWindowWidget.DataKind == this.currentKind);
+
+ if (selectedWidget is { Ready: true })
+ {
+ selectedWidget.Draw();
+ }
+ else
+ {
+ ImGui.TextUnformatted("Data not ready.");
+ }
+
+ this.isExcept = false;
+ }
+ catch (Exception ex)
+ {
+ if (!this.isExcept)
+ {
+ Log.Error(ex, "Could not draw data");
+ }
+
+ this.isExcept = true;
+
+ ImGui.TextUnformatted(ex.ToString());
+ }
+
+ ImGui.EndChild();
+ }
+
+ private void Load()
+ {
+ foreach (var widget in this.modules)
+ {
+ widget.Load();
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/IDataWindowWidget.cs b/Dalamud/Interface/Internal/Windows/Data/IDataWindowWidget.cs
new file mode 100644
index 000000000..ebbdfff83
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/IDataWindowWidget.cs
@@ -0,0 +1,27 @@
+namespace Dalamud.Interface.Internal.Windows;
+
+///
+/// Class representing a date window entry.
+///
+internal interface IDataWindowWidget
+{
+ ///
+ /// Gets the Data Kind for this data window module.
+ ///
+ DataKind DataKind { get; init; }
+
+ ///
+ /// Gets or sets a value indicating whether this data window module is ready.
+ ///
+ bool Ready { get; protected set; }
+
+ ///
+ /// Loads the necessary data for this data window module.
+ ///
+ void Load();
+
+ ///
+ /// Draws this data window module.
+ ///
+ void Draw();
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs
new file mode 100644
index 000000000..977037cc5
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonInspectorWidget.cs
@@ -0,0 +1,32 @@
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying addon inspector.
+///
+internal class AddonInspectorWidget : IDataWindowWidget
+{
+ private UiDebug? addonInspector;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Addon_Inspector;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.addonInspector = new UiDebug();
+
+ if (this.addonInspector is not null)
+ {
+ this.Ready = true;
+ }
+ }
+
+ ///
+ public void Draw()
+ {
+ this.addonInspector?.Draw();
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs
new file mode 100644
index 000000000..b26b7e311
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs
@@ -0,0 +1,66 @@
+using Dalamud.Game.Gui;
+using Dalamud.Memory;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying Addon Data.
+///
+internal unsafe class AddonWidget : IDataWindowWidget
+{
+ private string inputAddonName = string.Empty;
+ private int inputAddonIndex;
+ private nint findAgentInterfacePtr;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Addon;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var gameGui = Service.Get();
+
+ ImGui.InputText("Addon Name", ref this.inputAddonName, 256);
+ ImGui.InputInt("Addon Index", ref this.inputAddonIndex);
+
+ if (this.inputAddonName.IsNullOrEmpty())
+ return;
+
+ var address = gameGui.GetAddonByName(this.inputAddonName, this.inputAddonIndex);
+
+ if (address == nint.Zero)
+ {
+ ImGui.Text("Null");
+ return;
+ }
+
+ var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
+ var name = MemoryHelper.ReadStringNullTerminated((nint)addon->Name);
+ ImGui.TextUnformatted($"{name} - 0x{address.ToInt64():X}\n v:{addon->IsVisible} x:{addon->X} y:{addon->Y} s:{addon->Scale}, w:{addon->RootNode->Width}, h:{addon->RootNode->Height}");
+
+ if (ImGui.Button("Find Agent"))
+ {
+ this.findAgentInterfacePtr = gameGui.FindAgentInterface(address);
+ }
+
+ if (this.findAgentInterfacePtr != nint.Zero)
+ {
+ ImGui.TextUnformatted($"Agent: 0x{this.findAgentInterfacePtr.ToInt64():X}");
+ ImGui.SameLine();
+
+ if (ImGui.Button("C"))
+ ImGui.SetClipboardText(this.findAgentInterfacePtr.ToInt64().ToString("X"));
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs
new file mode 100644
index 000000000..606fedadd
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs
@@ -0,0 +1,64 @@
+using System.Collections.Generic;
+
+using Dalamud.Game;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget to display resolved .text sigs.
+///
+internal class AddressesWidget : IDataWindowWidget
+{
+ private string inputSig = string.Empty;
+ private nint sigResult = nint.Zero;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Address;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.InputText(".text sig", ref this.inputSig, 400);
+ if (ImGui.Button("Resolve"))
+ {
+ try
+ {
+ var sigScanner = Service.Get();
+ this.sigResult = sigScanner.ScanText(this.inputSig);
+ }
+ catch (KeyNotFoundException)
+ {
+ this.sigResult = new nint(-1);
+ }
+ }
+
+ ImGui.Text($"Result: {this.sigResult.ToInt64():X}");
+ ImGui.SameLine();
+ if (ImGui.Button($"C##{this.sigResult.ToInt64():X}"))
+ ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("X"));
+
+ foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues)
+ {
+ ImGui.TextUnformatted($"{debugScannedValue.Key}");
+ foreach (var valueTuple in debugScannedValue.Value)
+ {
+ ImGui.TextUnformatted(
+ $" {valueTuple.ClassName} - 0x{valueTuple.Address.ToInt64():X}");
+ ImGui.SameLine();
+
+ if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}"))
+ ImGui.SetClipboardText(valueTuple.Address.ToInt64().ToString("X"));
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs
new file mode 100644
index 000000000..cc4771847
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AetherytesWidget.cs
@@ -0,0 +1,87 @@
+using Dalamud.Game.ClientState.Aetherytes;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying aetheryte table.
+///
+internal class AetherytesWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Aetherytes;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ if (!ImGui.BeginTable("##aetheryteTable", 11, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders))
+ return;
+
+ ImGui.TableSetupScrollFreeze(0, 1);
+ ImGui.TableSetupColumn("Idx", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("ID", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Zone", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Ward", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Plot", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Sub", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Gil", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Fav", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Shared", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableSetupColumn("Apartment", ImGuiTableColumnFlags.WidthFixed);
+ ImGui.TableHeadersRow();
+
+ var tpList = Service.Get();
+
+ for (var i = 0; i < tpList.Length; i++)
+ {
+ var info = tpList[i];
+ if (info == null)
+ continue;
+
+ ImGui.TableNextColumn(); // Idx
+ ImGui.TextUnformatted($"{i}");
+
+ ImGui.TableNextColumn(); // Name
+ ImGui.TextUnformatted($"{info.AetheryteData.GameData?.PlaceName.Value?.Name}");
+
+ ImGui.TableNextColumn(); // ID
+ ImGui.TextUnformatted($"{info.AetheryteId}");
+
+ ImGui.TableNextColumn(); // Zone
+ ImGui.TextUnformatted($"{info.TerritoryId}");
+
+ ImGui.TableNextColumn(); // Ward
+ ImGui.TextUnformatted($"{info.Ward}");
+
+ ImGui.TableNextColumn(); // Plot
+ ImGui.TextUnformatted($"{info.Plot}");
+
+ ImGui.TableNextColumn(); // Sub
+ ImGui.TextUnformatted($"{info.SubIndex}");
+
+ ImGui.TableNextColumn(); // Gil
+ ImGui.TextUnformatted($"{info.GilCost}");
+
+ ImGui.TableNextColumn(); // Favourite
+ ImGui.TextUnformatted($"{info.IsFavourite}");
+
+ ImGui.TableNextColumn(); // Shared
+ ImGui.TextUnformatted($"{info.IsSharedHouse}");
+
+ ImGui.TableNextColumn(); // Apartment
+ ImGui.TextUnformatted($"{info.IsAppartment}");
+ }
+
+ ImGui.EndTable();
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs
new file mode 100644
index 000000000..df98f99a6
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AtkArrayDataBrowserWidget.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Numerics;
+
+using Dalamud.Memory;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying AtkArrayData.
+///
+internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.AtkArrayData_Browser;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var fontWidth = ImGui.CalcTextSize("A").X;
+ var fontHeight = ImGui.GetTextLineHeightWithSpacing();
+ var uiModule = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->GetUiModule();
+
+ if (uiModule == null)
+ {
+ ImGui.Text("UIModule unavailable.");
+ return;
+ }
+
+ var atkArrayDataHolder = &uiModule->GetRaptureAtkModule()->AtkModule.AtkArrayDataHolder;
+
+ if (ImGui.BeginTabBar("AtkArrayDataBrowserTabBar"))
+ {
+ if (ImGui.BeginTabItem($"NumberArrayData [{atkArrayDataHolder->NumberArrayCount}]"))
+ {
+ if (ImGui.BeginTable("NumberArrayDataTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY))
+ {
+ ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
+ ImGui.TableSetupColumn("Size", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
+ ImGui.TableSetupColumn("Pointer", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableHeadersRow();
+ for (var numberArrayIndex = 0; numberArrayIndex < atkArrayDataHolder->NumberArrayCount; numberArrayIndex++)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.Text($"{numberArrayIndex} [{numberArrayIndex * 8:X}]");
+ ImGui.TableNextColumn();
+ var numberArrayData = atkArrayDataHolder->NumberArrays[numberArrayIndex];
+ if (numberArrayData != null)
+ {
+ ImGui.Text($"{numberArrayData->AtkArrayData.Size}");
+ ImGui.TableNextColumn();
+ if (ImGui.TreeNodeEx($"{(long)numberArrayData:X}###{numberArrayIndex}", ImGuiTreeNodeFlags.SpanFullWidth))
+ {
+ ImGui.NewLine();
+ var tableHeight = Math.Min(40, numberArrayData->AtkArrayData.Size + 4);
+ if (ImGui.BeginTable($"NumberArrayDataTable", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, new Vector2(0.0F, fontHeight * tableHeight)))
+ {
+ ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 6);
+ ImGui.TableSetupColumn("Hex", ImGuiTableColumnFlags.WidthFixed, fontWidth * 9);
+ ImGui.TableSetupColumn("Integer", ImGuiTableColumnFlags.WidthFixed, fontWidth * 12);
+ ImGui.TableSetupColumn("Float", ImGuiTableColumnFlags.WidthFixed, fontWidth * 20);
+ ImGui.TableHeadersRow();
+ for (var numberIndex = 0; numberIndex < numberArrayData->AtkArrayData.Size; numberIndex++)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.Text($"{numberIndex}");
+ ImGui.TableNextColumn();
+ ImGui.Text($"{numberArrayData->IntArray[numberIndex]:X}");
+ ImGui.TableNextColumn();
+ ImGui.Text($"{numberArrayData->IntArray[numberIndex]}");
+ ImGui.TableNextColumn();
+ ImGui.Text($"{*(float*)&numberArrayData->IntArray[numberIndex]}");
+ }
+
+ ImGui.EndTable();
+ }
+
+ ImGui.TreePop();
+ }
+ }
+ else
+ {
+ ImGui.TextDisabled("--");
+ ImGui.TableNextColumn();
+ ImGui.TextDisabled("--");
+ }
+ }
+
+ ImGui.EndTable();
+ }
+
+ ImGui.EndTabItem();
+ }
+
+ if (ImGui.BeginTabItem($"StringArrayData [{atkArrayDataHolder->StringArrayCount}]"))
+ {
+ if (ImGui.BeginTable("StringArrayDataTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY))
+ {
+ ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
+ ImGui.TableSetupColumn("Size", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
+ ImGui.TableSetupColumn("Pointer", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableHeadersRow();
+ for (var stringArrayIndex = 0; stringArrayIndex < atkArrayDataHolder->StringArrayCount; stringArrayIndex++)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.Text($"{stringArrayIndex} [{stringArrayIndex * 8:X}]");
+ ImGui.TableNextColumn();
+ var stringArrayData = atkArrayDataHolder->StringArrays[stringArrayIndex];
+ if (stringArrayData != null)
+ {
+ ImGui.Text($"{stringArrayData->AtkArrayData.Size}");
+ ImGui.TableNextColumn();
+ if (ImGui.TreeNodeEx($"{(long)stringArrayData:X}###{stringArrayIndex}", ImGuiTreeNodeFlags.SpanFullWidth))
+ {
+ ImGui.NewLine();
+ var tableHeight = Math.Min(40, stringArrayData->AtkArrayData.Size + 4);
+ if (ImGui.BeginTable($"StringArrayDataTable", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, new Vector2(0.0F, fontHeight * tableHeight)))
+ {
+ ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 6);
+ ImGui.TableSetupColumn("String", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableHeadersRow();
+ for (var stringIndex = 0; stringIndex < stringArrayData->AtkArrayData.Size; stringIndex++)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.Text($"{stringIndex}");
+ ImGui.TableNextColumn();
+ if (stringArrayData->StringArray[stringIndex] != null)
+ {
+ ImGui.Text($"{MemoryHelper.ReadSeStringNullTerminated(new IntPtr(stringArrayData->StringArray[stringIndex]))}");
+ }
+ else
+ {
+ ImGui.TextDisabled("--");
+ }
+ }
+
+ ImGui.EndTable();
+ }
+
+ ImGui.TreePop();
+ }
+ }
+ else
+ {
+ ImGui.TextDisabled("--");
+ ImGui.TableNextColumn();
+ ImGui.TextDisabled("--");
+ }
+ }
+
+ ImGui.EndTable();
+ }
+
+ ImGui.EndTabItem();
+ }
+
+ ImGui.EndTabBar();
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs
new file mode 100644
index 000000000..2aeb9d10d
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/BuddyListWidget.cs
@@ -0,0 +1,110 @@
+using Dalamud.Game.ClientState.Buddy;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying data about the Buddy List.
+///
+internal class BuddyListWidget : IDataWindowWidget
+{
+ private bool resolveGameData;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Buddy_List;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var buddyList = Service.Get();
+
+ ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
+
+ ImGui.Text($"BuddyList: {buddyList.BuddyListAddress.ToInt64():X}");
+ {
+ var member = buddyList.CompanionBuddy;
+ if (member == null)
+ {
+ ImGui.Text("[Companion] null");
+ }
+ else
+ {
+ ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
+ if (this.resolveGameData)
+ {
+ var gameObject = member.GameObject;
+ if (gameObject == null)
+ {
+ ImGui.Text("GameObject was null");
+ }
+ else
+ {
+ Util.PrintGameObject(gameObject, "-", this.resolveGameData);
+ }
+ }
+ }
+ }
+
+ {
+ var member = buddyList.PetBuddy;
+ if (member == null)
+ {
+ ImGui.Text("[Pet] null");
+ }
+ else
+ {
+ ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
+ if (this.resolveGameData)
+ {
+ var gameObject = member.GameObject;
+ if (gameObject == null)
+ {
+ ImGui.Text("GameObject was null");
+ }
+ else
+ {
+ Util.PrintGameObject(gameObject, "-", this.resolveGameData);
+ }
+ }
+ }
+ }
+
+ {
+ var count = buddyList.Length;
+ if (count == 0)
+ {
+ ImGui.Text("[BattleBuddy] None present");
+ }
+ else
+ {
+ for (var i = 0; i < count; i++)
+ {
+ var member = buddyList[i];
+ ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.ObjectId} - {member?.DataID}");
+ if (this.resolveGameData)
+ {
+ var gameObject = member?.GameObject;
+ if (gameObject == null)
+ {
+ ImGui.Text("GameObject was null");
+ }
+ else
+ {
+ Util.PrintGameObject(gameObject, "-", this.resolveGameData);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs
new file mode 100644
index 000000000..e415431ba
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs
@@ -0,0 +1,33 @@
+using Dalamud.Game.Command;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying command info.
+///
+internal class CommandWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Command;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var commandManager = Service.Get();
+
+ foreach (var command in commandManager.Commands)
+ {
+ ImGui.Text($"{command.Key}\n -> {command.Value.HelpMessage}\n -> In help: {command.Value.ShowInHelp}\n\n");
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs
new file mode 100644
index 000000000..a5224589f
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs
@@ -0,0 +1,52 @@
+using Dalamud.Game.ClientState.Conditions;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying current character condition flags.
+///
+internal class ConditionWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Condition;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var condition = Service.Get();
+
+#if DEBUG
+ ImGui.Text($"ptr: 0x{condition.Address.ToInt64():X}");
+#endif
+
+ ImGui.Text("Current Conditions:");
+ ImGui.Separator();
+
+ var didAny = false;
+
+ for (var i = 0; i < Condition.MaxConditionEntries; i++)
+ {
+ var typedCondition = (ConditionFlag)i;
+ var cond = condition[typedCondition];
+
+ if (!cond) continue;
+
+ didAny = true;
+
+ ImGui.Text($"ID: {i} Enum: {typedCondition}");
+ }
+
+ if (!didAny)
+ ImGui.Text("None. Talk to a shop NPC or visit a market board to find out more!");
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs
new file mode 100644
index 000000000..3922f22b7
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConfigurationWidget.cs
@@ -0,0 +1,29 @@
+using Dalamud.Configuration.Internal;
+using Dalamud.Utility;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying configuration info.
+///
+internal class ConfigurationWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Configuration;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var config = Service.Get();
+ Util.ShowObject(config);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs
new file mode 100644
index 000000000..6ec741fe8
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DataShareWidget.cs
@@ -0,0 +1,53 @@
+using Dalamud.Plugin.Ipc.Internal;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying plugin data share modules.
+///
+internal class DataShareWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.DataShare;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ if (!ImGui.BeginTable("###DataShareTable", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg))
+ return;
+
+ try
+ {
+ ImGui.TableSetupColumn("Shared Tag");
+ ImGui.TableSetupColumn("Creator Assembly");
+ ImGui.TableSetupColumn("#", ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
+ ImGui.TableSetupColumn("Consumers");
+ ImGui.TableHeadersRow();
+ foreach (var share in Service.Get().GetAllShares())
+ {
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(share.Tag);
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(share.CreatorAssembly);
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(share.Users.Length.ToString());
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(string.Join(", ", share.Users));
+ }
+ }
+ finally
+ {
+ ImGui.EndTable();
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs
new file mode 100644
index 000000000..6d3a67e1a
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs
@@ -0,0 +1,80 @@
+using Dalamud.Configuration.Internal;
+using Dalamud.Game.Gui.Dtr;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying dtr test.
+///
+internal class DtrBarWidget : IDataWindowWidget
+{
+ private DtrBarEntry? dtrTest1;
+ private DtrBarEntry? dtrTest2;
+ private DtrBarEntry? dtrTest3;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Dtr_Bar;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ this.DrawDtrTestEntry(ref this.dtrTest1, "DTR Test #1");
+ ImGui.Separator();
+ this.DrawDtrTestEntry(ref this.dtrTest2, "DTR Test #2");
+ ImGui.Separator();
+ this.DrawDtrTestEntry(ref this.dtrTest3, "DTR Test #3");
+ ImGui.Separator();
+
+ var configuration = Service.Get();
+ if (configuration.DtrOrder != null)
+ {
+ ImGui.Separator();
+
+ foreach (var order in configuration.DtrOrder)
+ {
+ ImGui.Text(order);
+ }
+ }
+ }
+
+ private void DrawDtrTestEntry(ref DtrBarEntry? entry, string title)
+ {
+ var dtrBar = Service.Get();
+
+ if (entry != null)
+ {
+ ImGui.Text(title);
+
+ var text = entry.Text?.TextValue ?? string.Empty;
+ if (ImGui.InputText($"Text###{entry.Title}t", ref text, 255))
+ entry.Text = text;
+
+ var shown = entry.Shown;
+ if (ImGui.Checkbox($"Shown###{entry.Title}s", ref shown))
+ entry.Shown = shown;
+
+ if (ImGui.Button($"Remove###{entry.Title}r"))
+ {
+ entry.Remove();
+ entry = null;
+ }
+ }
+ else
+ {
+ if (ImGui.Button($"Add###{title}"))
+ {
+ entry = dtrBar.Get(title, title);
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs
new file mode 100644
index 000000000..78a93c1cc
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FateTableWidget.cs
@@ -0,0 +1,68 @@
+using Dalamud.Game.ClientState.Fates;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying the Fate Table.
+///
+internal class FateTableWidget : IDataWindowWidget
+{
+ private bool resolveGameData;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Fate_Table;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
+
+ var fateTable = Service.Get();
+
+ var stateString = string.Empty;
+ if (fateTable.Length == 0)
+ {
+ ImGui.TextUnformatted("No fates or data not ready.");
+ }
+ else
+ {
+ stateString += $"FateTableLen: {fateTable.Length}\n";
+
+ ImGui.TextUnformatted(stateString);
+
+ for (var i = 0; i < fateTable.Length; i++)
+ {
+ var fate = fateTable[i];
+ if (fate == null)
+ continue;
+
+ var fateString = $"{fate.Address.ToInt64():X}:[{i}]" +
+ $" - Lv.{fate.Level} {fate.Name} ({fate.Progress}%)" +
+ $" - X{fate.Position.X} Y{fate.Position.Y} Z{fate.Position.Z}" +
+ $" - Territory {(this.resolveGameData ? (fate.TerritoryType.GameData?.Name ?? fate.TerritoryType.Id.ToString()) : fate.TerritoryType.Id.ToString())}\n";
+
+ fateString += $" StartTimeEpoch: {fate.StartTimeEpoch}" +
+ $" - Duration: {fate.Duration}" +
+ $" - State: {fate.State}" +
+ $" - GameData name: {(this.resolveGameData ? (fate.GameData.Name ?? fate.FateId.ToString()) : fate.FateId.ToString())}";
+
+ ImGui.TextUnformatted(fateString);
+ ImGui.SameLine();
+ if (ImGui.Button("C"))
+ {
+ ImGui.SetClipboardText(fate.Address.ToString("X"));
+ }
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs
new file mode 100644
index 000000000..99c1a3e02
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FlyTextWidget.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Numerics;
+
+using Dalamud.Game.Gui.FlyText;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying fly text info.
+///
+internal class FlyTextWidget : IDataWindowWidget
+{
+ private int flyActor;
+ private FlyTextKind flyKind;
+ private int flyVal1;
+ private int flyVal2;
+ private string flyText1 = string.Empty;
+ private string flyText2 = string.Empty;
+ private int flyIcon;
+ private int flyDmgIcon;
+ private Vector4 flyColor = new(1, 0, 0, 1);
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.FlyText;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ if (ImGui.BeginCombo("Kind", this.flyKind.ToString()))
+ {
+ var names = Enum.GetNames(typeof(FlyTextKind));
+ for (var i = 0; i < names.Length; i++)
+ {
+ if (ImGui.Selectable($"{names[i]} ({i})"))
+ this.flyKind = (FlyTextKind)i;
+ }
+
+ ImGui.EndCombo();
+ }
+
+ ImGui.InputText("Text1", ref this.flyText1, 200);
+ ImGui.InputText("Text2", ref this.flyText2, 200);
+
+ ImGui.InputInt("Val1", ref this.flyVal1);
+ ImGui.InputInt("Val2", ref this.flyVal2);
+
+ ImGui.InputInt("Icon ID", ref this.flyIcon);
+ ImGui.InputInt("Damage Icon ID", ref this.flyDmgIcon);
+ ImGui.ColorEdit4("Color", ref this.flyColor);
+ ImGui.InputInt("Actor Index", ref this.flyActor);
+ var sendColor = ImGui.ColorConvertFloat4ToU32(this.flyColor);
+
+ if (ImGui.Button("Send"))
+ {
+ Service.Get().AddFlyText(
+ this.flyKind,
+ unchecked((uint)this.flyActor),
+ unchecked((uint)this.flyVal1),
+ unchecked((uint)this.flyVal2),
+ this.flyText1,
+ this.flyText2,
+ sendColor,
+ unchecked((uint)this.flyIcon),
+ unchecked((uint)this.flyDmgIcon));
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs
new file mode 100644
index 000000000..1ed5e9e83
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/FontAwesomeTestWidget.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget to display FontAwesome Symbols.
+///
+internal class FontAwesomeTestWidget : IDataWindowWidget
+{
+ private List? icons;
+ private List? iconNames;
+ private string[]? iconCategories;
+ private int selectedIconCategory;
+ private string iconSearchInput = string.Empty;
+ private bool iconSearchChanged = true;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.FontAwesome_Test;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
+
+ this.iconCategories ??= FontAwesomeHelpers.GetCategories();
+
+ if (this.iconSearchChanged)
+ {
+ this.icons = FontAwesomeHelpers.SearchIcons(this.iconSearchInput, this.iconCategories[this.selectedIconCategory]);
+ this.iconNames = this.icons.Select(icon => Enum.GetName(icon)!).ToList();
+ this.iconSearchChanged = false;
+ }
+
+ ImGui.SetNextItemWidth(160f);
+ var categoryIndex = this.selectedIconCategory;
+ if (ImGui.Combo("####FontAwesomeCategorySearch", ref categoryIndex, this.iconCategories, this.iconCategories.Length))
+ {
+ this.selectedIconCategory = categoryIndex;
+ this.iconSearchChanged = true;
+ }
+
+ ImGui.SameLine(170f);
+ ImGui.SetNextItemWidth(180f);
+ if (ImGui.InputTextWithHint($"###FontAwesomeInputSearch", "search icons", ref this.iconSearchInput, 50))
+ {
+ this.iconSearchChanged = true;
+ }
+
+ ImGuiHelpers.ScaledDummy(10f);
+ for (var i = 0; i < this.icons?.Count; i++)
+ {
+ ImGui.Text($"0x{(int)this.icons[i].ToIconChar():X}");
+ ImGuiHelpers.ScaledRelativeSameLine(50f);
+ ImGui.Text($"{this.iconNames?[i]}");
+ ImGuiHelpers.ScaledRelativeSameLine(280f);
+ ImGui.PushFont(UiBuilder.IconFont);
+ ImGui.Text(this.icons[i].ToIconString());
+ ImGui.PopFont();
+ ImGuiHelpers.ScaledDummy(2f);
+ }
+
+ ImGui.PopStyleVar();
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs
new file mode 100644
index 000000000..5c92e3ad1
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs
@@ -0,0 +1,86 @@
+using System;
+
+using Dalamud.Game.ClientState.GamePad;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying gamepad info.
+///
+internal class GamepadWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Gamepad;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var gamepadState = Service.Get();
+
+ static void DrawHelper(string text, uint mask, Func resolve)
+ {
+ ImGui.Text($"{text} {mask:X4}");
+ ImGui.Text($"DPadLeft {resolve(GamepadButtons.DpadLeft)} " +
+ $"DPadUp {resolve(GamepadButtons.DpadUp)} " +
+ $"DPadRight {resolve(GamepadButtons.DpadRight)} " +
+ $"DPadDown {resolve(GamepadButtons.DpadDown)} ");
+ ImGui.Text($"West {resolve(GamepadButtons.West)} " +
+ $"North {resolve(GamepadButtons.North)} " +
+ $"East {resolve(GamepadButtons.East)} " +
+ $"South {resolve(GamepadButtons.South)} ");
+ ImGui.Text($"L1 {resolve(GamepadButtons.L1)} " +
+ $"L2 {resolve(GamepadButtons.L2)} " +
+ $"R1 {resolve(GamepadButtons.R1)} " +
+ $"R2 {resolve(GamepadButtons.R2)} ");
+ ImGui.Text($"Select {resolve(GamepadButtons.Select)} " +
+ $"Start {resolve(GamepadButtons.Start)} " +
+ $"L3 {resolve(GamepadButtons.L3)} " +
+ $"R3 {resolve(GamepadButtons.R3)} ");
+ }
+
+ ImGui.Text($"GamepadInput 0x{gamepadState.GamepadInputAddress.ToInt64():X}");
+
+#if DEBUG
+ if (ImGui.IsItemHovered())
+ ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
+
+ if (ImGui.IsItemClicked())
+ ImGui.SetClipboardText($"0x{gamepadState.GamepadInputAddress.ToInt64():X}");
+#endif
+
+ DrawHelper(
+ "Buttons Raw",
+ gamepadState.ButtonsRaw,
+ gamepadState.Raw);
+ DrawHelper(
+ "Buttons Pressed",
+ gamepadState.ButtonsPressed,
+ gamepadState.Pressed);
+ DrawHelper(
+ "Buttons Repeat",
+ gamepadState.ButtonsRepeat,
+ gamepadState.Repeat);
+ DrawHelper(
+ "Buttons Released",
+ gamepadState.ButtonsReleased,
+ gamepadState.Released);
+ ImGui.Text($"LeftStickLeft {gamepadState.LeftStickLeft:0.00} " +
+ $"LeftStickUp {gamepadState.LeftStickUp:0.00} " +
+ $"LeftStickRight {gamepadState.LeftStickRight:0.00} " +
+ $"LeftStickDown {gamepadState.LeftStickDown:0.00} ");
+ ImGui.Text($"RightStickLeft {gamepadState.RightStickLeft:0.00} " +
+ $"RightStickUp {gamepadState.RightStickUp:0.00} " +
+ $"RightStickRight {gamepadState.RightStickRight:0.00} " +
+ $"RightStickDown {gamepadState.RightStickDown:0.00} ");
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs
new file mode 100644
index 000000000..02862b33d
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GaugeWidget.cs
@@ -0,0 +1,72 @@
+using Dalamud.Game.ClientState;
+using Dalamud.Game.ClientState.JobGauge;
+using Dalamud.Game.ClientState.JobGauge.Types;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying job gauge data.
+///
+internal class GaugeWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Gauge;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var clientState = Service.Get();
+ var jobGauges = Service.Get();
+
+ var player = clientState.LocalPlayer;
+ if (player == null)
+ {
+ ImGui.Text("Player is not present");
+ return;
+ }
+
+ var jobID = player.ClassJob.Id;
+ JobGaugeBase? gauge = jobID switch
+ {
+ 19 => jobGauges.Get(),
+ 20 => jobGauges.Get(),
+ 21 => jobGauges.Get(),
+ 22 => jobGauges.Get(),
+ 23 => jobGauges.Get(),
+ 24 => jobGauges.Get(),
+ 25 => jobGauges.Get(),
+ 27 => jobGauges.Get(),
+ 28 => jobGauges.Get(),
+ 30 => jobGauges.Get(),
+ 31 => jobGauges.Get(),
+ 32 => jobGauges.Get(),
+ 33 => jobGauges.Get(),
+ 34 => jobGauges.Get(),
+ 35 => jobGauges.Get(),
+ 37 => jobGauges.Get(),
+ 38 => jobGauges.Get(),
+ 39 => jobGauges.Get(),
+ 40 => jobGauges.Get(),
+ _ => null,
+ };
+
+ if (gauge == null)
+ {
+ ImGui.Text("No supported gauge exists for this job.");
+ return;
+ }
+
+ Util.ShowObject(gauge);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs
new file mode 100644
index 000000000..aa565b1e6
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/HookWidget.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Dalamud.Hooking;
+using ImGuiNET;
+using PInvoke;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying hook information.
+///
+internal class HookWidget : IDataWindowWidget
+{
+ private Hook? messageBoxMinHook;
+ private bool hookUseMinHook;
+
+ private delegate int MessageBoxWDelegate(
+ IntPtr hWnd,
+ [MarshalAs(UnmanagedType.LPWStr)] string text,
+ [MarshalAs(UnmanagedType.LPWStr)] string caption,
+ NativeFunctions.MessageBoxType type);
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Hook;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ try
+ {
+ ImGui.Checkbox("Use MinHook", ref this.hookUseMinHook);
+
+ if (ImGui.Button("Create"))
+ this.messageBoxMinHook = Hook.FromSymbol("User32", "MessageBoxW", this.MessageBoxWDetour, this.hookUseMinHook);
+
+ if (ImGui.Button("Enable"))
+ this.messageBoxMinHook?.Enable();
+
+ if (ImGui.Button("Disable"))
+ this.messageBoxMinHook?.Disable();
+
+ if (ImGui.Button("Call Original"))
+ this.messageBoxMinHook?.Original(IntPtr.Zero, "Hello from .Original", "Hook Test", NativeFunctions.MessageBoxType.Ok);
+
+ if (ImGui.Button("Dispose"))
+ {
+ this.messageBoxMinHook?.Dispose();
+ this.messageBoxMinHook = null;
+ }
+
+ if (ImGui.Button("Test"))
+ _ = NativeFunctions.MessageBoxW(IntPtr.Zero, "Hi", "Hello", NativeFunctions.MessageBoxType.Ok);
+
+ if (this.messageBoxMinHook != null)
+ ImGui.Text("Enabled: " + this.messageBoxMinHook?.IsEnabled);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "MinHook error caught");
+ }
+ }
+
+ private int MessageBoxWDetour(IntPtr hwnd, string text, string caption, NativeFunctions.MessageBoxType type)
+ {
+ Log.Information("[DATAHOOK] {Hwnd} {Text} {Caption} {Type}", hwnd, text, caption, type);
+
+ var result = this.messageBoxMinHook!.Original(hwnd, "Cause Access Violation?", caption, NativeFunctions.MessageBoxType.YesNo);
+
+ if (result == (int)User32.MessageBoxResult.IDYES)
+ {
+ Marshal.ReadByte(IntPtr.Zero);
+ }
+
+ return result;
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs
new file mode 100644
index 000000000..8afce718f
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs
@@ -0,0 +1,74 @@
+using System;
+
+using Dalamud.Interface.Internal.Notifications;
+using Dalamud.Interface.Windowing;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying ImGui test.
+///
+internal class ImGuiWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.ImGui;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var interfaceManager = Service.Get();
+ var notifications = Service.Get();
+
+ ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size);
+ ImGui.Text("OverrideGameCursor: " + interfaceManager.OverrideGameCursor);
+
+ ImGui.Button("THIS IS A BUTTON###hoverTestButton");
+ interfaceManager.OverrideGameCursor = !ImGui.IsItemHovered();
+
+ ImGui.Separator();
+
+ ImGui.TextUnformatted($"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
+
+ ImGui.Separator();
+
+ if (ImGui.Button("Add random notification"))
+ {
+ var rand = new Random();
+
+ var title = rand.Next(0, 5) switch
+ {
+ 0 => "This is a toast",
+ 1 => "Truly, a toast",
+ 2 => "I am testing this toast",
+ 3 => "I hope this looks right",
+ 4 => "Good stuff",
+ 5 => "Nice",
+ _ => null,
+ };
+
+ var type = rand.Next(0, 4) switch
+ {
+ 0 => NotificationType.Error,
+ 1 => NotificationType.Warning,
+ 2 => NotificationType.Info,
+ 3 => NotificationType.Success,
+ 4 => NotificationType.None,
+ _ => NotificationType.None,
+ };
+
+ const string text = "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla.";
+
+ notifications.AddNotification(text, title, type);
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs
new file mode 100644
index 000000000..accc48b4b
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/KeyStateWidget.cs
@@ -0,0 +1,50 @@
+using Dalamud.Game.ClientState.Keys;
+using Dalamud.Interface.Colors;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying keyboard state.
+///
+internal class KeyStateWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.KeyState;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var keyState = Service.Get();
+
+ ImGui.Columns(4);
+
+ var i = 0;
+ foreach (var vkCode in keyState.GetValidVirtualKeys())
+ {
+ var code = (int)vkCode;
+ var value = keyState[code];
+
+ ImGui.PushStyleColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed);
+
+ ImGui.Text($"{vkCode} ({code})");
+
+ ImGui.PopStyleColor();
+
+ i++;
+ if (i % 24 == 0)
+ ImGui.NextColumn();
+ }
+
+ ImGui.Columns(1);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs
new file mode 100644
index 000000000..dd41315f2
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ObjectTableWidget.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Numerics;
+
+using Dalamud.Game.ClientState;
+using Dalamud.Game.ClientState.Objects;
+using Dalamud.Game.Gui;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget to display the Object Table.
+///
+internal class ObjectTableWidget : IDataWindowWidget
+{
+ private bool resolveGameData;
+ private bool drawCharacters;
+ private float maxCharaDrawDistance = 20.0f;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Object_Table;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
+
+ var chatGui = Service.Get();
+ var clientState = Service.Get();
+ var gameGui = Service.Get();
+ var objectTable = Service.Get();
+
+ var stateString = string.Empty;
+
+ if (clientState.LocalPlayer == null)
+ {
+ ImGui.TextUnformatted("LocalPlayer null.");
+ }
+ else if (clientState.IsPvPExcludingDen)
+ {
+ ImGui.TextUnformatted("Cannot access object table while in PvP.");
+ }
+ else
+ {
+ stateString += $"ObjectTableLen: {objectTable.Length}\n";
+ stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n";
+ stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.GameData?.Name : clientState.LocalPlayer.CurrentWorld.Id.ToString())}\n";
+ stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.GameData?.Name : clientState.LocalPlayer.HomeWorld.Id.ToString())}\n";
+ stateString += $"LocalCID: {clientState.LocalContentId:X}\n";
+ stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n";
+ stateString += $"TerritoryType: {clientState.TerritoryType}\n\n";
+
+ ImGui.TextUnformatted(stateString);
+
+ ImGui.Checkbox("Draw characters on screen", ref this.drawCharacters);
+ ImGui.SliderFloat("Draw Distance", ref this.maxCharaDrawDistance, 2f, 40f);
+
+ for (var i = 0; i < objectTable.Length; i++)
+ {
+ var obj = objectTable[i];
+
+ if (obj == null)
+ continue;
+
+ Util.PrintGameObject(obj, i.ToString(), this.resolveGameData);
+
+ if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords))
+ {
+ // So, while WorldToScreen will return false if the point is off of game client screen, to
+ // to avoid performance issues, we have to manually determine if creating a window would
+ // produce a new viewport, and skip rendering it if so
+ var objectText = $"{obj.Address.ToInt64():X}:{obj.ObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}";
+
+ var screenPos = ImGui.GetMainViewport().Pos;
+ var screenSize = ImGui.GetMainViewport().Size;
+
+ var windowSize = ImGui.CalcTextSize(objectText);
+
+ // Add some extra safety padding
+ windowSize.X += ImGui.GetStyle().WindowPadding.X + 10;
+ windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10;
+
+ if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X ||
+ screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y)
+ continue;
+
+ if (obj.YalmDistanceX > this.maxCharaDrawDistance)
+ continue;
+
+ ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y));
+
+ ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f));
+ if (ImGui.Begin(
+ $"Actor{i}##ActorWindow{i}",
+ ImGuiWindowFlags.NoDecoration |
+ ImGuiWindowFlags.AlwaysAutoResize |
+ ImGuiWindowFlags.NoSavedSettings |
+ ImGuiWindowFlags.NoMove |
+ ImGuiWindowFlags.NoMouseInputs |
+ ImGuiWindowFlags.NoDocking |
+ ImGuiWindowFlags.NoFocusOnAppearing |
+ ImGuiWindowFlags.NoNav))
+ ImGui.Text(objectText);
+ ImGui.End();
+ }
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs
new file mode 100644
index 000000000..c5ac1fb8f
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PartyListWidget.cs
@@ -0,0 +1,63 @@
+using Dalamud.Game.ClientState.Party;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying information about the current party.
+///
+internal class PartyListWidget : IDataWindowWidget
+{
+ private bool resolveGameData;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Party_List;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var partyList = Service.Get();
+
+ ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
+
+ ImGui.Text($"GroupManager: {partyList.GroupManagerAddress.ToInt64():X}");
+ ImGui.Text($"GroupList: {partyList.GroupListAddress.ToInt64():X}");
+ ImGui.Text($"AllianceList: {partyList.AllianceListAddress.ToInt64():X}");
+
+ ImGui.Text($"{partyList.Length} Members");
+
+ for (var i = 0; i < partyList.Length; i++)
+ {
+ var member = partyList[i];
+ if (member == null)
+ {
+ ImGui.Text($"[{i}] was null");
+ continue;
+ }
+
+ ImGui.Text($"[{i}] {member.Address.ToInt64():X} - {member.Name} - {member.GameObject?.ObjectId}");
+ if (this.resolveGameData)
+ {
+ var actor = member.GameObject;
+ if (actor == null)
+ {
+ ImGui.Text("Actor was null");
+ }
+ else
+ {
+ Util.PrintGameObject(actor, "-", this.resolveGameData);
+ }
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs
new file mode 100644
index 000000000..9aae9bba3
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/PluginIpcWidget.cs
@@ -0,0 +1,84 @@
+using System;
+
+using Dalamud.Plugin.Ipc;
+using Dalamud.Plugin.Ipc.Internal;
+using Dalamud.Utility;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for testing plugin IPC systems.
+///
+internal class PluginIpcWidget : IDataWindowWidget
+{
+ // IPC
+ private ICallGateProvider? ipcPub;
+ private ICallGateSubscriber? ipcSub;
+ private string callGateResponse = string.Empty;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Plugin_IPC;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ if (this.ipcPub == null)
+ {
+ this.ipcPub = new CallGatePubSub("dataDemo1");
+
+ this.ipcPub.RegisterAction(msg =>
+ {
+ Log.Information("Data action was called: {Msg}", msg);
+ });
+
+ this.ipcPub.RegisterFunc(msg =>
+ {
+ Log.Information("Data func was called: {Msg}", msg);
+ return Guid.NewGuid().ToString();
+ });
+ }
+
+ if (this.ipcSub == null)
+ {
+ this.ipcSub = new CallGatePubSub("dataDemo1");
+ this.ipcSub.Subscribe(_ =>
+ {
+ Log.Information("PONG1");
+ });
+ this.ipcSub.Subscribe(_ =>
+ {
+ Log.Information("PONG2");
+ });
+ this.ipcSub.Subscribe(_ => throw new Exception("PONG3"));
+ }
+
+ if (ImGui.Button("PING"))
+ {
+ this.ipcPub.SendMessage("PING");
+ }
+
+ if (ImGui.Button("Action"))
+ {
+ this.ipcSub.InvokeAction("button1");
+ }
+
+ if (ImGui.Button("Func"))
+ {
+ this.callGateResponse = this.ipcSub.InvokeFunc("button2");
+ }
+
+ if (!this.callGateResponse.IsNullOrEmpty())
+ ImGui.Text($"Response: {this.callGateResponse}");
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs
new file mode 100644
index 000000000..a642c439d
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/SeFontTestWidget.cs
@@ -0,0 +1,33 @@
+using Dalamud.Game.Text;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying test data for SE Font Symbols.
+///
+internal class SeFontTestWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.SE_Font_Test;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var specialChars = string.Empty;
+
+ for (var i = 0xE020; i <= 0xE0DB; i++)
+ specialChars += $"0x{i:X} - {(SeIconChar)i} - {(char)i}\n";
+
+ ImGui.TextUnformatted(specialChars);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs
new file mode 100644
index 000000000..f414e0957
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ServerOpcodeWidget.cs
@@ -0,0 +1,37 @@
+using Dalamud.Data;
+using ImGuiNET;
+using Newtonsoft.Json;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget to display the currently set server opcodes.
+///
+internal class ServerOpcodeWidget : IDataWindowWidget
+{
+ private string? serverOpString;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Server_OpCode;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ var dataManager = Service.Get();
+
+ if (dataManager.IsDataReady)
+ {
+ this.serverOpString = JsonConvert.SerializeObject(dataManager.ServerOpCodes, Formatting.Indented);
+ this.Ready = true;
+ }
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.TextUnformatted(this.serverOpString ?? "serverOpString not initialized");
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs
new file mode 100644
index 000000000..656efe388
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/StartInfoWidget.cs
@@ -0,0 +1,30 @@
+using ImGuiNET;
+using Newtonsoft.Json;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying start info.
+///
+internal class StartInfoWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.StartInfo;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var startInfo = Service.Get();
+
+ ImGui.Text(JsonConvert.SerializeObject(startInfo, Formatting.Indented));
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs
new file mode 100644
index 000000000..07d6e8f72
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TargetWidget.cs
@@ -0,0 +1,88 @@
+using Dalamud.Game.ClientState;
+using Dalamud.Game.ClientState.Objects;
+using Dalamud.Utility;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying target info.
+///
+internal class TargetWidget : IDataWindowWidget
+{
+ private bool resolveGameData;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Target;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
+
+ var clientState = Service.Get();
+ var targetMgr = Service.Get();
+
+ if (targetMgr.Target != null)
+ {
+ Util.PrintGameObject(targetMgr.Target, "CurrentTarget", this.resolveGameData);
+
+ ImGui.Text("Target");
+ Util.ShowGameObjectStruct(targetMgr.Target);
+
+ var tot = targetMgr.Target.TargetObject;
+ if (tot != null)
+ {
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.Separator();
+ ImGui.Text("ToT");
+ Util.ShowGameObjectStruct(tot);
+ }
+
+ ImGuiHelpers.ScaledDummy(10);
+ }
+
+ if (targetMgr.FocusTarget != null)
+ Util.PrintGameObject(targetMgr.FocusTarget, "FocusTarget", this.resolveGameData);
+
+ if (targetMgr.MouseOverTarget != null)
+ Util.PrintGameObject(targetMgr.MouseOverTarget, "MouseOverTarget", this.resolveGameData);
+
+ if (targetMgr.PreviousTarget != null)
+ Util.PrintGameObject(targetMgr.PreviousTarget, "PreviousTarget", this.resolveGameData);
+
+ if (targetMgr.SoftTarget != null)
+ Util.PrintGameObject(targetMgr.SoftTarget, "SoftTarget", this.resolveGameData);
+
+ if (ImGui.Button("Clear CT"))
+ targetMgr.ClearTarget();
+
+ if (ImGui.Button("Clear FT"))
+ targetMgr.ClearFocusTarget();
+
+ var localPlayer = clientState.LocalPlayer;
+
+ if (localPlayer != null)
+ {
+ if (ImGui.Button("Set CT"))
+ targetMgr.SetTarget(localPlayer);
+
+ if (ImGui.Button("Set FT"))
+ targetMgr.SetFocusTarget(localPlayer);
+ }
+ else
+ {
+ ImGui.Text("LocalPlayer is null.");
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs
new file mode 100644
index 000000000..7d91cd154
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TaskSchedulerWidget.cs
@@ -0,0 +1,256 @@
+// ReSharper disable MethodSupportsCancellation // Using alternative method of cancelling tasks by throwing exceptions.
+using System;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Dalamud.Game;
+using Dalamud.Interface.Colors;
+using Dalamud.Logging.Internal;
+using ImGuiNET;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying task scheduler test.
+///
+internal class TaskSchedulerWidget : IDataWindowWidget
+{
+ private CancellationTokenSource taskSchedulerCancelSource = new();
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.TaskSched;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ if (ImGui.Button("Clear list"))
+ {
+ TaskTracker.Clear();
+ }
+
+ ImGui.SameLine();
+ ImGuiHelpers.ScaledDummy(10);
+ ImGui.SameLine();
+
+ if (ImGui.Button("Cancel using CancellationTokenSource"))
+ {
+ this.taskSchedulerCancelSource.Cancel();
+ this.taskSchedulerCancelSource = new();
+ }
+
+ ImGui.Text("Run in any thread: ");
+ ImGui.SameLine();
+
+ if (ImGui.Button("Short Task.Run"))
+ {
+ Task.Run(() => { Thread.Sleep(500); });
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("Task in task(Delay)"))
+ {
+ var token = this.taskSchedulerCancelSource.Token;
+ Task.Run(async () => await this.TestTaskInTaskDelay(token), token);
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("Task in task(Sleep)"))
+ {
+ Task.Run(async () => await this.TestTaskInTaskSleep());
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("Faulting task"))
+ {
+ Task.Run(() =>
+ {
+ Thread.Sleep(200);
+
+ string a = null;
+ a.Contains("dalamud"); // Intentional null exception.
+ });
+ }
+
+ ImGui.Text("Run in Framework.Update: ");
+ ImGui.SameLine();
+
+ if (ImGui.Button("ASAP"))
+ {
+ Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("In 1s"))
+ {
+ Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("In 60f"))
+ {
+ Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedulerCancelSource.Token, delayTicks: 60));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("Error in 1s"))
+ {
+ Task.Run(async () => await Service.Get().RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedulerCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("As long as it's in Framework Thread"))
+ {
+ Task.Run(async () => await Service.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
+ Service.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
+ }
+
+ if (ImGui.Button("Drown in tasks"))
+ {
+ var token = this.taskSchedulerCancelSource.Token;
+ Task.Run(
+ () =>
+ {
+ for (var i = 0; i < 100; i++)
+ {
+ token.ThrowIfCancellationRequested();
+ Task.Run(
+ () =>
+ {
+ for (var j = 0; j < 100; j++)
+ {
+ token.ThrowIfCancellationRequested();
+ Task.Run(
+ () =>
+ {
+ for (var k = 0; k < 100; k++)
+ {
+ token.ThrowIfCancellationRequested();
+ Task.Run(
+ () =>
+ {
+ for (var l = 0; l < 100; l++)
+ {
+ token.ThrowIfCancellationRequested();
+ Task.Run(
+ async () =>
+ {
+ for (var m = 0; m < 100; m++)
+ {
+ token.ThrowIfCancellationRequested();
+ await Task.Delay(1, token);
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+
+ ImGui.SameLine();
+
+ ImGuiHelpers.ScaledDummy(20);
+
+ // Needed to init the task tracker, if we're not on a debug build
+ Service.Get().Enable();
+
+ for (var i = 0; i < TaskTracker.Tasks.Count; i++)
+ {
+ var task = TaskTracker.Tasks[i];
+ var subTime = DateTime.Now;
+ if (task.Task == null)
+ subTime = task.FinishTime;
+
+ switch (task.Status)
+ {
+ case TaskStatus.Created:
+ case TaskStatus.WaitingForActivation:
+ case TaskStatus.WaitingToRun:
+ ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudGrey);
+ break;
+ case TaskStatus.Running:
+ case TaskStatus.WaitingForChildrenToComplete:
+ ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedBlue);
+ break;
+ case TaskStatus.RanToCompletion:
+ ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedGreen);
+ break;
+ case TaskStatus.Canceled:
+ case TaskStatus.Faulted:
+ ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudRed);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ if (ImGui.CollapsingHeader($"#{task.Id} - {task.Status} {(subTime - task.StartTime).TotalMilliseconds}ms###task{i}"))
+ {
+ task.IsBeingViewed = true;
+
+ if (ImGui.Button("CANCEL (May not work)"))
+ {
+ try
+ {
+ var cancelFunc =
+ typeof(Task).GetMethod("InternalCancel", BindingFlags.NonPublic | BindingFlags.Instance);
+ cancelFunc?.Invoke(task, null);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Could not cancel task");
+ }
+ }
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ ImGui.TextUnformatted(task.StackTrace?.ToString());
+
+ if (task.Exception != null)
+ {
+ ImGuiHelpers.ScaledDummy(15);
+ ImGui.TextColored(ImGuiColors.DalamudRed, "EXCEPTION:");
+ ImGui.TextUnformatted(task.Exception.ToString());
+ }
+ }
+ else
+ {
+ task.IsBeingViewed = false;
+ }
+
+ ImGui.PopStyleColor(1);
+ }
+ }
+
+ private async Task TestTaskInTaskDelay(CancellationToken token)
+ {
+ await Task.Delay(5000, token);
+ }
+
+#pragma warning disable 1998
+ private async Task TestTaskInTaskSleep()
+#pragma warning restore 1998
+ {
+ Thread.Sleep(5000);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs
new file mode 100644
index 000000000..4a0cfe21a
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/TexWidget.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Numerics;
+
+using Dalamud.Data;
+using Dalamud.Utility;
+using ImGuiNET;
+using ImGuiScene;
+using Serilog;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying texture test.
+///
+internal class TexWidget : IDataWindowWidget
+{
+ private string inputTexPath = string.Empty;
+ private TextureWrap? debugTex;
+ private Vector2 inputTexUv0 = Vector2.Zero;
+ private Vector2 inputTexUv1 = Vector2.One;
+ private Vector4 inputTintCol = Vector4.One;
+ private Vector2 inputTexScale = Vector2.Zero;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Tex;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var dataManager = Service.Get();
+
+ ImGui.InputText("Tex Path", ref this.inputTexPath, 255);
+ ImGui.InputFloat2("UV0", ref this.inputTexUv0);
+ ImGui.InputFloat2("UV1", ref this.inputTexUv1);
+ ImGui.InputFloat4("Tint", ref this.inputTintCol);
+ ImGui.InputFloat2("Scale", ref this.inputTexScale);
+
+ if (ImGui.Button("Load Tex"))
+ {
+ try
+ {
+ this.debugTex = dataManager.GetImGuiTexture(this.inputTexPath);
+ this.inputTexScale = new Vector2(this.debugTex?.Width ?? 0, this.debugTex?.Height ?? 0);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Could not load tex");
+ }
+ }
+
+ ImGuiHelpers.ScaledDummy(10);
+
+ if (this.debugTex != null)
+ {
+ ImGui.Image(this.debugTex.ImGuiHandle, this.inputTexScale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol);
+ ImGuiHelpers.ScaledDummy(5);
+ Util.ShowObject(this.debugTex);
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs
new file mode 100644
index 000000000..c75230e73
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ToastWidget.cs
@@ -0,0 +1,74 @@
+using System.Numerics;
+
+using Dalamud.Game.Gui.Toast;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying toast test.
+///
+internal class ToastWidget : IDataWindowWidget
+{
+ private string inputTextToast = string.Empty;
+ private int toastPosition;
+ private int toastSpeed;
+ private int questToastPosition;
+ private bool questToastSound;
+ private int questToastIconId;
+ private bool questToastCheckmark;
+
+ ///
+ public DataKind DataKind { get; init; } = DataKind.Toast;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var toastGui = Service.Get();
+
+ ImGui.InputText("Toast text", ref this.inputTextToast, 200);
+
+ ImGui.Combo("Toast Position", ref this.toastPosition, new[] { "Bottom", "Top", }, 2);
+ ImGui.Combo("Toast Speed", ref this.toastSpeed, new[] { "Slow", "Fast", }, 2);
+ ImGui.Combo("Quest Toast Position", ref this.questToastPosition, new[] { "Centre", "Right", "Left" }, 3);
+ ImGui.Checkbox("Quest Checkmark", ref this.questToastCheckmark);
+ ImGui.Checkbox("Quest Play Sound", ref this.questToastSound);
+ ImGui.InputInt("Quest Icon ID", ref this.questToastIconId);
+
+ ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
+
+ if (ImGui.Button("Show toast"))
+ {
+ toastGui.ShowNormal(this.inputTextToast, new ToastOptions
+ {
+ Position = (ToastPosition)this.toastPosition,
+ Speed = (ToastSpeed)this.toastSpeed,
+ });
+ }
+
+ if (ImGui.Button("Show Quest toast"))
+ {
+ toastGui.ShowQuest(this.inputTextToast, new QuestToastOptions
+ {
+ Position = (QuestToastPosition)this.questToastPosition,
+ DisplayCheckmark = this.questToastCheckmark,
+ IconId = (uint)this.questToastIconId,
+ PlaySound = this.questToastSound,
+ });
+ }
+
+ if (ImGui.Button("Show Error toast"))
+ {
+ toastGui.ShowError(this.inputTextToast);
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs
new file mode 100644
index 000000000..1d0ccdce6
--- /dev/null
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/UIColorWidget.cs
@@ -0,0 +1,60 @@
+using System.Numerics;
+
+using Dalamud.Data;
+using ImGuiNET;
+using Lumina.Excel.GeneratedSheets;
+
+namespace Dalamud.Interface.Internal.Windows.Data;
+
+///
+/// Widget for displaying all UI Colors from Lumina.
+///
+internal class UIColorWidget : IDataWindowWidget
+{
+ ///
+ public DataKind DataKind { get; init; } = DataKind.UIColor;
+
+ ///
+ public bool Ready { get; set; }
+
+ ///
+ public void Load()
+ {
+ this.Ready = true;
+ }
+
+ ///
+ public void Draw()
+ {
+ var colorSheet = Service.Get().GetExcelSheet();
+ if (colorSheet is null) return;
+
+ foreach (var color in colorSheet)
+ {
+ this.DrawUiColor(color);
+ }
+ }
+
+ private void DrawUiColor(UIColor color)
+ {
+ ImGui.Text($"[{color.RowId:D3}] ");
+ ImGui.SameLine();
+ ImGui.TextColored(this.ConvertToVector4(color.Unknown2), $"Unknown2 ");
+ ImGui.SameLine();
+ ImGui.TextColored(this.ConvertToVector4(color.UIForeground), "UIForeground ");
+ ImGui.SameLine();
+ ImGui.TextColored(this.ConvertToVector4(color.Unknown3), "Unknown3 ");
+ ImGui.SameLine();
+ ImGui.TextColored(this.ConvertToVector4(color.UIGlow), "UIGlow");
+ }
+
+ private Vector4 ConvertToVector4(uint color)
+ {
+ var r = (byte)(color >> 24);
+ var g = (byte)(color >> 16);
+ var b = (byte)(color >> 8);
+ var a = (byte)color;
+
+ return new Vector4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs
deleted file mode 100644
index c46916343..000000000
--- a/Dalamud/Interface/Internal/Windows/DataWindow.cs
+++ /dev/null
@@ -1,1886 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-using Dalamud.Configuration.Internal;
-using Dalamud.Data;
-using Dalamud.Game;
-using Dalamud.Game.ClientState;
-using Dalamud.Game.ClientState.Aetherytes;
-using Dalamud.Game.ClientState.Buddy;
-using Dalamud.Game.ClientState.Conditions;
-using Dalamud.Game.ClientState.Fates;
-using Dalamud.Game.ClientState.GamePad;
-using Dalamud.Game.ClientState.JobGauge;
-using Dalamud.Game.ClientState.JobGauge.Types;
-using Dalamud.Game.ClientState.Keys;
-using Dalamud.Game.ClientState.Objects;
-using Dalamud.Game.ClientState.Objects.SubKinds;
-using Dalamud.Game.ClientState.Objects.Types;
-using Dalamud.Game.ClientState.Party;
-using Dalamud.Game.Command;
-using Dalamud.Game.Gui;
-using Dalamud.Game.Gui.Dtr;
-using Dalamud.Game.Gui.FlyText;
-using Dalamud.Game.Gui.Toast;
-using Dalamud.Game.Text;
-using Dalamud.Hooking;
-using Dalamud.Interface.Colors;
-using Dalamud.Interface.Internal.Notifications;
-using Dalamud.Interface.Windowing;
-using Dalamud.Logging.Internal;
-using Dalamud.Memory;
-using Dalamud.Plugin.Ipc;
-using Dalamud.Plugin.Ipc.Internal;
-using Dalamud.Utility;
-using ImGuiNET;
-using ImGuiScene;
-using Lumina.Excel.GeneratedSheets;
-using Newtonsoft.Json;
-using PInvoke;
-using Serilog;
-
-using Condition = Dalamud.Game.ClientState.Conditions.Condition;
-
-namespace Dalamud.Interface.Internal.Windows;
-
-///
-/// Class responsible for drawing the data/debug window.
-///
-internal class DataWindow : Window
-{
- private readonly string[] dataKindNames = Enum.GetNames(typeof(DataKind)).Select(k => k.Replace("_", " ")).ToArray();
-
- private bool wasReady;
- private bool isExcept;
- private string serverOpString;
- private DataKind currentKind;
-
- private bool drawCharas = false;
- private float maxCharaDrawDistance = 20;
-
- private string inputSig = string.Empty;
- private IntPtr sigResult = IntPtr.Zero;
-
- private string inputAddonName = string.Empty;
- private int inputAddonIndex;
-
- private IntPtr findAgentInterfacePtr;
-
- private bool resolveGameData = false;
- private bool resolveObjects = false;
-
- private UiDebug addonInspector = null;
-
- private Hook? messageBoxMinHook;
- private bool hookUseMinHook = false;
-
- // FontAwesome
- private List? icons;
- private List iconNames;
- private string[]? iconCategories;
- private int selectedIconCategory;
- private string iconSearchInput = string.Empty;
- private bool iconSearchChanged = true;
-
- // IPC
- private ICallGateProvider ipcPub;
- private ICallGateSubscriber ipcSub;
- private string callGateResponse = string.Empty;
-
- // Toast fields
- private string inputTextToast = string.Empty;
- private int toastPosition = 0;
- private int toastSpeed = 0;
- private int questToastPosition = 0;
- private bool questToastSound = false;
- private int questToastIconId = 0;
- private bool questToastCheckmark = false;
-
- // Fly text fields
- private int flyActor;
- private FlyTextKind flyKind;
- private int flyVal1;
- private int flyVal2;
- private string flyText1 = string.Empty;
- private string flyText2 = string.Empty;
- private int flyIcon;
- private int flyDmgIcon;
- private Vector4 flyColor = new(1, 0, 0, 1);
-
- // ImGui fields
- private string inputTexPath = string.Empty;
- private TextureWrap debugTex = null;
- private Vector2 inputTexUv0 = Vector2.Zero;
- private Vector2 inputTexUv1 = Vector2.One;
- private Vector4 inputTintCol = Vector4.One;
- private Vector2 inputTexScale = Vector2.Zero;
-
- // DTR
- private DtrBarEntry? dtrTest1;
- private DtrBarEntry? dtrTest2;
- private DtrBarEntry? dtrTest3;
-
- // Task Scheduler
- private CancellationTokenSource taskSchedCancelSource = new();
-
- private uint copyButtonIndex = 0;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DataWindow()
- : base("Dalamud Data")
- {
- this.Size = new Vector2(500, 500);
- this.SizeCondition = ImGuiCond.FirstUseEver;
-
- this.RespectCloseHotkey = false;
-
- this.Load();
- }
-
- private delegate int MessageBoxWDelegate(
- IntPtr hWnd,
- [MarshalAs(UnmanagedType.LPWStr)] string text,
- [MarshalAs(UnmanagedType.LPWStr)] string caption,
- NativeFunctions.MessageBoxType type);
-
- private enum DataKind
- {
- Server_OpCode,
- Address,
- Object_Table,
- Fate_Table,
- SE_Font_Test,
- FontAwesome_Test,
- Party_List,
- Buddy_List,
- Plugin_IPC,
- Condition,
- Gauge,
- Command,
- Addon,
- Addon_Inspector,
- AtkArrayData_Browser,
- StartInfo,
- Target,
- Toast,
- FlyText,
- ImGui,
- Tex,
- KeyState,
- Gamepad,
- Configuration,
- TaskSched,
- Hook,
- Aetherytes,
- Dtr_Bar,
- UIColor,
- DataShare,
- }
-
- ///
- public override void OnOpen()
- {
- }
-
- ///
- public override void OnClose()
- {
- }
-
- ///
- /// Set the DataKind dropdown menu.
- ///
- /// Data kind name, can be lower and/or without spaces.
- public void SetDataKind(string dataKind)
- {
- if (string.IsNullOrEmpty(dataKind))
- return;
-
- dataKind = dataKind switch
- {
- "ai" => "Addon Inspector",
- "at" => "Object Table", // Actor Table
- "ot" => "Object Table",
- "uic" => "UIColor",
- _ => dataKind,
- };
-
- dataKind = dataKind.Replace(" ", string.Empty).ToLower();
-
- var matched = Enum.GetValues()
- .Where(kind => Enum.GetName(kind).Replace("_", string.Empty).ToLower() == dataKind)
- .FirstOrDefault();
-
- if (matched != default)
- {
- this.currentKind = matched;
- }
- else
- {
- Service.Get().PrintError($"/xldata: Invalid data type {dataKind}");
- }
- }
-
- ///
- /// Draw the window via ImGui.
- ///
- public override void Draw()
- {
- this.copyButtonIndex = 0;
-
- // Main window
- if (ImGui.Button("Force Reload"))
- this.Load();
- ImGui.SameLine();
- var copy = ImGui.Button("Copy all");
- ImGui.SameLine();
-
- var currentKindIndex = (int)this.currentKind;
- if (ImGui.Combo("Data kind", ref currentKindIndex, this.dataKindNames, this.dataKindNames.Length))
- {
- this.currentKind = (DataKind)currentKindIndex;
- }
-
- ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
-
- ImGui.BeginChild("scrolling", new Vector2(0, 0), false, ImGuiWindowFlags.HorizontalScrollbar);
-
- if (copy)
- ImGui.LogToClipboard();
-
- ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(0, 0));
-
- try
- {
- if (this.wasReady)
- {
- switch (this.currentKind)
- {
- case DataKind.Server_OpCode:
- this.DrawServerOpCode();
- break;
-
- case DataKind.Address:
- this.DrawAddress();
- break;
-
- case DataKind.Object_Table:
- this.DrawObjectTable();
- break;
-
- case DataKind.Fate_Table:
- this.DrawFateTable();
- break;
-
- case DataKind.SE_Font_Test:
- this.DrawSEFontTest();
- break;
-
- case DataKind.FontAwesome_Test:
- this.DrawFontAwesomeTest();
- break;
-
- case DataKind.Party_List:
- this.DrawPartyList();
- break;
-
- case DataKind.Buddy_List:
- this.DrawBuddyList();
- break;
-
- case DataKind.Plugin_IPC:
- this.DrawPluginIPC();
- break;
-
- case DataKind.Condition:
- this.DrawCondition();
- break;
-
- case DataKind.Gauge:
- this.DrawGauge();
- break;
-
- case DataKind.Command:
- this.DrawCommand();
- break;
-
- case DataKind.Addon:
- this.DrawAddon();
- break;
-
- case DataKind.Addon_Inspector:
- this.DrawAddonInspector();
- break;
-
- case DataKind.AtkArrayData_Browser:
- this.DrawAtkArrayDataBrowser();
- break;
-
- case DataKind.StartInfo:
- this.DrawStartInfo();
- break;
-
- case DataKind.Target:
- this.DrawTarget();
- break;
-
- case DataKind.Toast:
- this.DrawToast();
- break;
-
- case DataKind.FlyText:
- this.DrawFlyText();
- break;
-
- case DataKind.ImGui:
- this.DrawImGui();
- break;
-
- case DataKind.Tex:
- this.DrawTex();
- break;
-
- case DataKind.KeyState:
- this.DrawKeyState();
- break;
-
- case DataKind.Gamepad:
- this.DrawGamepad();
- break;
-
- case DataKind.Configuration:
- this.DrawConfiguration();
- break;
-
- case DataKind.TaskSched:
- this.DrawTaskSched();
- break;
-
- case DataKind.Hook:
- this.DrawHook();
- break;
-
- case DataKind.Aetherytes:
- this.DrawAetherytes();
- break;
-
- case DataKind.Dtr_Bar:
- this.DrawDtr();
- break;
-
- case DataKind.UIColor:
- this.DrawUIColor();
- break;
- case DataKind.DataShare:
- this.DrawDataShareTab();
- break;
- }
- }
- else
- {
- ImGui.TextUnformatted("Data not ready.");
- }
-
- this.isExcept = false;
- }
- catch (Exception ex)
- {
- if (!this.isExcept)
- {
- Log.Error(ex, "Could not draw data");
- }
-
- this.isExcept = true;
-
- ImGui.TextUnformatted(ex.ToString());
- }
-
- ImGui.PopStyleVar();
-
- ImGui.EndChild();
- }
-
- private int MessageBoxWDetour(IntPtr hwnd, string text, string caption, NativeFunctions.MessageBoxType type)
- {
- Log.Information("[DATAHOOK] {0} {1} {2} {3}", hwnd, text, caption, type);
-
- var result = this.messageBoxMinHook.Original(hwnd, "Cause Access Violation?", caption, NativeFunctions.MessageBoxType.YesNo);
-
- if (result == (int)User32.MessageBoxResult.IDYES)
- {
- Marshal.ReadByte(IntPtr.Zero);
- }
-
- return result;
- }
-
- private void DrawServerOpCode()
- {
- ImGui.TextUnformatted(this.serverOpString);
- }
-
- private void DrawAddress()
- {
- ImGui.InputText(".text sig", ref this.inputSig, 400);
- if (ImGui.Button("Resolve"))
- {
- try
- {
- var sigScanner = Service.Get();
- this.sigResult = sigScanner.ScanText(this.inputSig);
- }
- catch (KeyNotFoundException)
- {
- this.sigResult = new IntPtr(-1);
- }
- }
-
- ImGui.Text($"Result: {this.sigResult.ToInt64():X}");
- ImGui.SameLine();
- if (ImGui.Button($"C{this.copyButtonIndex++}"))
- ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("x"));
-
- foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues)
- {
- ImGui.TextUnformatted($"{debugScannedValue.Key}");
- foreach (var valueTuple in debugScannedValue.Value)
- {
- ImGui.TextUnformatted(
- $" {valueTuple.ClassName} - 0x{valueTuple.Address.ToInt64():x}");
- ImGui.SameLine();
-
- if (ImGui.Button($"C##copyAddress{this.copyButtonIndex++}"))
- ImGui.SetClipboardText(valueTuple.Address.ToInt64().ToString("x"));
- }
- }
- }
-
- private void DrawObjectTable()
- {
- var chatGui = Service.Get();
- var clientState = Service.Get();
- var framework = Service.Get();
- var gameGui = Service.Get();
- var objectTable = Service.Get();
-
- var stateString = string.Empty;
-
- if (clientState.LocalPlayer == null)
- {
- ImGui.TextUnformatted("LocalPlayer null.");
- }
- else if (clientState.IsPvPExcludingDen)
- {
- ImGui.TextUnformatted("Cannot access object table while in PvP.");
- }
- else
- {
- stateString += $"ObjectTableLen: {objectTable.Length}\n";
- stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n";
- stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.GameData.Name : clientState.LocalPlayer.CurrentWorld.Id.ToString())}\n";
- stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.GameData.Name : clientState.LocalPlayer.HomeWorld.Id.ToString())}\n";
- stateString += $"LocalCID: {clientState.LocalContentId:X}\n";
- stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n";
- stateString += $"TerritoryType: {clientState.TerritoryType}\n\n";
-
- ImGui.TextUnformatted(stateString);
-
- ImGui.Checkbox("Draw characters on screen", ref this.drawCharas);
- ImGui.SliderFloat("Draw Distance", ref this.maxCharaDrawDistance, 2f, 40f);
-
- for (var i = 0; i < objectTable.Length; i++)
- {
- var obj = objectTable[i];
-
- if (obj == null)
- continue;
-
- this.PrintGameObject(obj, i.ToString());
-
- if (this.drawCharas && gameGui.WorldToScreen(obj.Position, out var screenCoords))
- {
- // So, while WorldToScreen will return false if the point is off of game client screen, to
- // to avoid performance issues, we have to manually determine if creating a window would
- // produce a new viewport, and skip rendering it if so
- var objectText = $"{obj.Address.ToInt64():X}:{obj.ObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}";
-
- var screenPos = ImGui.GetMainViewport().Pos;
- var screenSize = ImGui.GetMainViewport().Size;
-
- var windowSize = ImGui.CalcTextSize(objectText);
-
- // Add some extra safety padding
- windowSize.X += ImGui.GetStyle().WindowPadding.X + 10;
- windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10;
-
- if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X ||
- screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y)
- continue;
-
- if (obj.YalmDistanceX > this.maxCharaDrawDistance)
- continue;
-
- ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y));
-
- ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f));
- if (ImGui.Begin(
- $"Actor{i}##ActorWindow{i}",
- ImGuiWindowFlags.NoDecoration |
- ImGuiWindowFlags.AlwaysAutoResize |
- ImGuiWindowFlags.NoSavedSettings |
- ImGuiWindowFlags.NoMove |
- ImGuiWindowFlags.NoMouseInputs |
- ImGuiWindowFlags.NoDocking |
- ImGuiWindowFlags.NoFocusOnAppearing |
- ImGuiWindowFlags.NoNav))
- ImGui.Text(objectText);
- ImGui.End();
- }
- }
- }
- }
-
- private void DrawFateTable()
- {
- var fateTable = Service.Get();
- var framework = Service.Get();
-
- var stateString = string.Empty;
- if (fateTable.Length == 0)
- {
- ImGui.TextUnformatted("No fates or data not ready.");
- }
- else
- {
- stateString += $"FateTableLen: {fateTable.Length}\n";
-
- ImGui.TextUnformatted(stateString);
-
- for (var i = 0; i < fateTable.Length; i++)
- {
- var fate = fateTable[i];
- if (fate == null)
- continue;
-
- var fateString = $"{fate.Address.ToInt64():X}:[{i}]" +
- $" - Lv.{fate.Level} {fate.Name} ({fate.Progress}%)" +
- $" - X{fate.Position.X} Y{fate.Position.Y} Z{fate.Position.Z}" +
- $" - Territory {(this.resolveGameData ? (fate.TerritoryType.GameData?.Name ?? fate.TerritoryType.Id.ToString()) : fate.TerritoryType.Id.ToString())}\n";
-
- fateString += $" StartTimeEpoch: {fate.StartTimeEpoch}" +
- $" - Duration: {fate.Duration}" +
- $" - State: {fate.State}" +
- $" - GameData name: {(this.resolveGameData ? (fate.GameData?.Name ?? fate.FateId.ToString()) : fate.FateId.ToString())}";
-
- ImGui.TextUnformatted(fateString);
- ImGui.SameLine();
- if (ImGui.Button("C"))
- {
- ImGui.SetClipboardText(fate.Address.ToString("X"));
- }
- }
- }
- }
-
- private void DrawSEFontTest()
- {
- var specialChars = string.Empty;
-
- for (var i = 0xE020; i <= 0xE0DB; i++)
- specialChars += $"0x{i:X} - {(SeIconChar)i} - {(char)i}\n";
-
- ImGui.TextUnformatted(specialChars);
- }
-
- private void DrawFontAwesomeTest()
- {
- this.iconCategories ??= FontAwesomeHelpers.GetCategories();
-
- if (this.iconSearchChanged)
- {
- this.icons = FontAwesomeHelpers.SearchIcons(this.iconSearchInput, this.iconCategories[this.selectedIconCategory]);
- this.iconNames = this.icons.Select(icon => Enum.GetName(icon)!).ToList();
- this.iconSearchChanged = false;
- }
-
- ImGui.SetNextItemWidth(160f);
- var categoryIndex = this.selectedIconCategory;
- if (ImGui.Combo("####FontAwesomeCategorySearch", ref categoryIndex, this.iconCategories, this.iconCategories.Length))
- {
- this.selectedIconCategory = categoryIndex;
- this.iconSearchChanged = true;
- }
-
- ImGui.SameLine(170f);
- ImGui.SetNextItemWidth(180f);
- if (ImGui.InputTextWithHint($"###FontAwesomeInputSearch", "search icons", ref this.iconSearchInput, 50))
- {
- this.iconSearchChanged = true;
- }
-
- ImGuiHelpers.ScaledDummy(10f);
- for (var i = 0; i < this.icons?.Count; i++)
- {
- ImGui.Text($"0x{(int)this.icons[i].ToIconChar():X}");
- ImGuiHelpers.ScaledRelativeSameLine(50f);
- ImGui.Text($"{this.iconNames[i]}");
- ImGuiHelpers.ScaledRelativeSameLine(280f);
- ImGui.PushFont(UiBuilder.IconFont);
- ImGui.Text(this.icons[i].ToIconString());
- ImGui.PopFont();
- ImGuiHelpers.ScaledDummy(2f);
- }
- }
-
- private void DrawPartyList()
- {
- var partyList = Service.Get();
-
- ImGui.Checkbox("Resolve Actors", ref this.resolveObjects);
-
- ImGui.Text($"GroupManager: {partyList.GroupManagerAddress.ToInt64():X}");
- ImGui.Text($"GroupList: {partyList.GroupListAddress.ToInt64():X}");
- ImGui.Text($"AllianceList: {partyList.AllianceListAddress.ToInt64():X}");
-
- ImGui.Text($"{partyList.Length} Members");
-
- for (var i = 0; i < partyList.Length; i++)
- {
- var member = partyList[i];
- if (member == null)
- {
- ImGui.Text($"[{i}] was null");
- continue;
- }
-
- ImGui.Text($"[{i}] {member.Address.ToInt64():X} - {member.Name} - {member.GameObject.ObjectId}");
- if (this.resolveObjects)
- {
- var actor = member.GameObject;
- if (actor == null)
- {
- ImGui.Text("Actor was null");
- }
- else
- {
- this.PrintGameObject(actor, "-");
- }
- }
- }
- }
-
- private void DrawBuddyList()
- {
- var buddyList = Service.Get();
-
- ImGui.Checkbox("Resolve Actors", ref this.resolveObjects);
-
- ImGui.Text($"BuddyList: {buddyList.BuddyListAddress.ToInt64():X}");
- {
- var member = buddyList.CompanionBuddy;
- if (member == null)
- {
- ImGui.Text("[Companion] null");
- }
- else
- {
- ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
- if (this.resolveObjects)
- {
- var gameObject = member.GameObject;
- if (gameObject == null)
- {
- ImGui.Text("GameObject was null");
- }
- else
- {
- this.PrintGameObject(gameObject, "-");
- }
- }
- }
- }
-
- {
- var member = buddyList.PetBuddy;
- if (member == null)
- {
- ImGui.Text("[Pet] null");
- }
- else
- {
- ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
- if (this.resolveObjects)
- {
- var gameObject = member.GameObject;
- if (gameObject == null)
- {
- ImGui.Text("GameObject was null");
- }
- else
- {
- this.PrintGameObject(gameObject, "-");
- }
- }
- }
- }
-
- {
- var count = buddyList.Length;
- if (count == 0)
- {
- ImGui.Text("[BattleBuddy] None present");
- }
- else
- {
- for (var i = 0; i < count; i++)
- {
- var member = buddyList[i];
- ImGui.Text($"[BattleBuddy] [{i}] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}");
- if (this.resolveObjects)
- {
- var gameObject = member.GameObject;
- if (gameObject == null)
- {
- ImGui.Text("GameObject was null");
- }
- else
- {
- this.PrintGameObject(gameObject, "-");
- }
- }
- }
- }
- }
- }
-
- private void DrawPluginIPC()
- {
- if (this.ipcPub == null)
- {
- this.ipcPub = new CallGatePubSub("dataDemo1");
-
- this.ipcPub.RegisterAction((msg) =>
- {
- Log.Information($"Data action was called: {msg}");
- });
-
- this.ipcPub.RegisterFunc((msg) =>
- {
- Log.Information($"Data func was called: {msg}");
- return Guid.NewGuid().ToString();
- });
- }
-
- if (this.ipcSub == null)
- {
- this.ipcSub = new CallGatePubSub("dataDemo1");
- this.ipcSub.Subscribe((msg) =>
- {
- Log.Information("PONG1");
- });
- this.ipcSub.Subscribe((msg) =>
- {
- Log.Information("PONG2");
- });
- this.ipcSub.Subscribe((msg) =>
- {
- throw new Exception("PONG3");
- });
- }
-
- if (ImGui.Button("PING"))
- {
- this.ipcPub.SendMessage("PING");
- }
-
- if (ImGui.Button("Action"))
- {
- this.ipcSub.InvokeAction("button1");
- }
-
- if (ImGui.Button("Func"))
- {
- this.callGateResponse = this.ipcSub.InvokeFunc("button2");
- }
-
- if (!this.callGateResponse.IsNullOrEmpty())
- ImGui.Text($"Response: {this.callGateResponse}");
- }
-
- private void DrawCondition()
- {
- var condition = Service.Get();
-
-#if DEBUG
- ImGui.Text($"ptr: 0x{condition.Address.ToInt64():X}");
-#endif
-
- ImGui.Text("Current Conditions:");
- ImGui.Separator();
-
- var didAny = false;
-
- for (var i = 0; i < Condition.MaxConditionEntries; i++)
- {
- var typedCondition = (ConditionFlag)i;
- var cond = condition[typedCondition];
-
- if (!cond) continue;
-
- didAny = true;
-
- ImGui.Text($"ID: {i} Enum: {typedCondition}");
- }
-
- if (!didAny)
- ImGui.Text("None. Talk to a shop NPC or visit a market board to find out more!!!!!!!");
- }
-
- private void DrawGauge()
- {
- var clientState = Service.Get();
- var jobGauges = Service.Get();
-
- var player = clientState.LocalPlayer;
- if (player == null)
- {
- ImGui.Text("Player is not present");
- return;
- }
-
- var jobID = player.ClassJob.Id;
- JobGaugeBase? gauge = jobID switch
- {
- 19 => jobGauges.Get(),
- 20 => jobGauges.Get(),
- 21 => jobGauges.Get(),
- 22 => jobGauges.Get(),
- 23 => jobGauges.Get(),
- 24 => jobGauges.Get(),
- 25 => jobGauges.Get(),
- 27 => jobGauges.Get(),
- 28 => jobGauges.Get(),
- 30 => jobGauges.Get(),
- 31 => jobGauges.Get(),
- 32 => jobGauges.Get(),
- 33 => jobGauges.Get(),
- 34 => jobGauges.Get(),
- 35 => jobGauges.Get(),
- 37 => jobGauges.Get(),
- 38 => jobGauges.Get(),
- 39 => jobGauges.Get(),
- 40 => jobGauges.Get(),
- _ => null,
- };
-
- if (gauge == null)
- {
- ImGui.Text("No supported gauge exists for this job.");
- return;
- }
-
- Util.ShowObject(gauge);
- }
-
- private void DrawCommand()
- {
- var commandManager = Service.Get();
-
- foreach (var command in commandManager.Commands)
- {
- ImGui.Text($"{command.Key}\n -> {command.Value.HelpMessage}\n -> In help: {command.Value.ShowInHelp}\n\n");
- }
- }
-
- private unsafe void DrawAddon()
- {
- var gameGui = Service.Get();
-
- ImGui.InputText("Addon name", ref this.inputAddonName, 256);
- ImGui.InputInt("Addon Index", ref this.inputAddonIndex);
-
- if (this.inputAddonName.IsNullOrEmpty())
- return;
-
- var address = gameGui.GetAddonByName(this.inputAddonName, this.inputAddonIndex);
-
- if (address == IntPtr.Zero)
- {
- ImGui.Text("Null");
- return;
- }
-
- var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
- var name = MemoryHelper.ReadStringNullTerminated((IntPtr)addon->Name);
- ImGui.TextUnformatted($"{name} - 0x{address.ToInt64():x}\n v:{addon->IsVisible} x:{addon->X} y:{addon->Y} s:{addon->Scale}, w:{addon->RootNode->Width}, h:{addon->RootNode->Height}");
-
- if (ImGui.Button("Find Agent"))
- {
- this.findAgentInterfacePtr = gameGui.FindAgentInterface(address);
- }
-
- if (this.findAgentInterfacePtr != IntPtr.Zero)
- {
- ImGui.TextUnformatted($"Agent: 0x{this.findAgentInterfacePtr.ToInt64():x}");
- ImGui.SameLine();
-
- if (ImGui.Button("C"))
- ImGui.SetClipboardText(this.findAgentInterfacePtr.ToInt64().ToString("x"));
- }
- }
-
- private void DrawAddonInspector()
- {
- this.addonInspector ??= new UiDebug();
- this.addonInspector.Draw();
- }
-
- private unsafe void DrawAtkArrayDataBrowser()
- {
- var fontWidth = ImGui.CalcTextSize("A").X;
- var fontHeight = ImGui.GetTextLineHeightWithSpacing();
- var uiModule = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->GetUiModule();
-
- if (uiModule == null)
- {
- ImGui.Text("UIModule unavailable.");
- return;
- }
-
- var atkArrayDataHolder = &uiModule->GetRaptureAtkModule()->AtkModule.AtkArrayDataHolder;
-
- if (ImGui.BeginTabBar("AtkArrayDataBrowserTabBar"))
- {
- if (ImGui.BeginTabItem($"NumberArrayData [{atkArrayDataHolder->NumberArrayCount}]"))
- {
- if (ImGui.BeginTable("NumberArrayDataTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY))
- {
- ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
- ImGui.TableSetupColumn("Size", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
- ImGui.TableSetupColumn("Pointer", ImGuiTableColumnFlags.WidthStretch);
- ImGui.TableHeadersRow();
- for (var numberArrayIndex = 0; numberArrayIndex < atkArrayDataHolder->NumberArrayCount; numberArrayIndex++)
- {
- ImGui.TableNextRow();
- ImGui.TableNextColumn();
- ImGui.Text($"{numberArrayIndex} [{numberArrayIndex * 8:X}]");
- ImGui.TableNextColumn();
- var numberArrayData = atkArrayDataHolder->NumberArrays[numberArrayIndex];
- if (numberArrayData != null)
- {
- ImGui.Text($"{numberArrayData->AtkArrayData.Size}");
- ImGui.TableNextColumn();
- if (ImGui.TreeNodeEx($"{(long)numberArrayData:X}###{numberArrayIndex}", ImGuiTreeNodeFlags.SpanFullWidth))
- {
- ImGui.NewLine();
- var tableHeight = Math.Min(40, numberArrayData->AtkArrayData.Size + 4);
- if (ImGui.BeginTable($"NumberArrayDataTable", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, new Vector2(0.0F, fontHeight * tableHeight)))
- {
- ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 6);
- ImGui.TableSetupColumn("Hex", ImGuiTableColumnFlags.WidthFixed, fontWidth * 9);
- ImGui.TableSetupColumn("Integer", ImGuiTableColumnFlags.WidthFixed, fontWidth * 12);
- ImGui.TableSetupColumn("Float", ImGuiTableColumnFlags.WidthFixed, fontWidth * 20);
- ImGui.TableHeadersRow();
- for (var numberIndex = 0; numberIndex < numberArrayData->AtkArrayData.Size; numberIndex++)
- {
- ImGui.TableNextRow();
- ImGui.TableNextColumn();
- ImGui.Text($"{numberIndex}");
- ImGui.TableNextColumn();
- ImGui.Text($"{numberArrayData->IntArray[numberIndex]:X}");
- ImGui.TableNextColumn();
- ImGui.Text($"{numberArrayData->IntArray[numberIndex]}");
- ImGui.TableNextColumn();
- ImGui.Text($"{*(float*)&numberArrayData->IntArray[numberIndex]}");
- }
-
- ImGui.EndTable();
- }
-
- ImGui.TreePop();
- }
- }
- else
- {
- ImGui.TextDisabled("--");
- ImGui.TableNextColumn();
- ImGui.TextDisabled("--");
- }
- }
-
- ImGui.EndTable();
- }
-
- ImGui.EndTabItem();
- }
-
- if (ImGui.BeginTabItem($"StringArrayData [{atkArrayDataHolder->StringArrayCount}]"))
- {
- if (ImGui.BeginTable("StringArrayDataTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY))
- {
- ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
- ImGui.TableSetupColumn("Size", ImGuiTableColumnFlags.WidthFixed, fontWidth * 10);
- ImGui.TableSetupColumn("Pointer", ImGuiTableColumnFlags.WidthStretch);
- ImGui.TableHeadersRow();
- for (var stringArrayIndex = 0; stringArrayIndex < atkArrayDataHolder->StringArrayCount; stringArrayIndex++)
- {
- ImGui.TableNextRow();
- ImGui.TableNextColumn();
- ImGui.Text($"{stringArrayIndex} [{stringArrayIndex * 8:X}]");
- ImGui.TableNextColumn();
- var stringArrayData = atkArrayDataHolder->StringArrays[stringArrayIndex];
- if (stringArrayData != null)
- {
- ImGui.Text($"{stringArrayData->AtkArrayData.Size}");
- ImGui.TableNextColumn();
- if (ImGui.TreeNodeEx($"{(long)stringArrayData:X}###{stringArrayIndex}", ImGuiTreeNodeFlags.SpanFullWidth))
- {
- ImGui.NewLine();
- var tableHeight = Math.Min(40, stringArrayData->AtkArrayData.Size + 4);
- if (ImGui.BeginTable($"StringArrayDataTable", 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY, new Vector2(0.0F, fontHeight * tableHeight)))
- {
- ImGui.TableSetupColumn("Index", ImGuiTableColumnFlags.WidthFixed, fontWidth * 6);
- ImGui.TableSetupColumn("String", ImGuiTableColumnFlags.WidthStretch);
- ImGui.TableHeadersRow();
- for (var stringIndex = 0; stringIndex < stringArrayData->AtkArrayData.Size; stringIndex++)
- {
- ImGui.TableNextRow();
- ImGui.TableNextColumn();
- ImGui.Text($"{stringIndex}");
- ImGui.TableNextColumn();
- if (stringArrayData->StringArray[stringIndex] != null)
- {
- ImGui.Text($"{MemoryHelper.ReadSeStringNullTerminated(new IntPtr(stringArrayData->StringArray[stringIndex]))}");
- }
- else
- {
- ImGui.TextDisabled("--");
- }
- }
-
- ImGui.EndTable();
- }
-
- ImGui.TreePop();
- }
- }
- else
- {
- ImGui.TextDisabled("--");
- ImGui.TableNextColumn();
- ImGui.TextDisabled("--");
- }
- }
-
- ImGui.EndTable();
- }
-
- ImGui.EndTabItem();
- }
-
- ImGui.EndTabBar();
- }
- }
-
- private void DrawStartInfo()
- {
- var startInfo = Service.Get();
-
- ImGui.Text(JsonConvert.SerializeObject(startInfo, Formatting.Indented));
- }
-
- private void DrawTarget()
- {
- var clientState = Service.Get();
- var targetMgr = Service.Get();
-
- if (targetMgr.Target != null)
- {
- this.PrintGameObject(targetMgr.Target, "CurrentTarget");
-
- ImGui.Text("Target");
- Util.ShowGameObjectStruct(targetMgr.Target);
-
- var tot = targetMgr.Target.TargetObject;
- if (tot != null)
- {
- ImGuiHelpers.ScaledDummy(10);
-
- ImGui.Separator();
- ImGui.Text("ToT");
- Util.ShowGameObjectStruct(tot);
- }
-
- ImGuiHelpers.ScaledDummy(10);
- }
-
- if (targetMgr.FocusTarget != null)
- this.PrintGameObject(targetMgr.FocusTarget, "FocusTarget");
-
- if (targetMgr.MouseOverTarget != null)
- this.PrintGameObject(targetMgr.MouseOverTarget, "MouseOverTarget");
-
- if (targetMgr.PreviousTarget != null)
- this.PrintGameObject(targetMgr.PreviousTarget, "PreviousTarget");
-
- if (targetMgr.SoftTarget != null)
- this.PrintGameObject(targetMgr.SoftTarget, "SoftTarget");
-
- if (ImGui.Button("Clear CT"))
- targetMgr.ClearTarget();
-
- if (ImGui.Button("Clear FT"))
- targetMgr.ClearFocusTarget();
-
- var localPlayer = clientState.LocalPlayer;
-
- if (localPlayer != null)
- {
- if (ImGui.Button("Set CT"))
- targetMgr.SetTarget(localPlayer);
-
- if (ImGui.Button("Set FT"))
- targetMgr.SetFocusTarget(localPlayer);
- }
- else
- {
- ImGui.Text("LocalPlayer is null.");
- }
- }
-
- private void DrawToast()
- {
- var toastGui = Service.Get();
-
- ImGui.InputText("Toast text", ref this.inputTextToast, 200);
-
- ImGui.Combo("Toast Position", ref this.toastPosition, new[] { "Bottom", "Top", }, 2);
- ImGui.Combo("Toast Speed", ref this.toastSpeed, new[] { "Slow", "Fast", }, 2);
- ImGui.Combo("Quest Toast Position", ref this.questToastPosition, new[] { "Centre", "Right", "Left" }, 3);
- ImGui.Checkbox("Quest Checkmark", ref this.questToastCheckmark);
- ImGui.Checkbox("Quest Play Sound", ref this.questToastSound);
- ImGui.InputInt("Quest Icon ID", ref this.questToastIconId);
-
- ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
-
- if (ImGui.Button("Show toast"))
- {
- toastGui.ShowNormal(this.inputTextToast, new ToastOptions
- {
- Position = (ToastPosition)this.toastPosition,
- Speed = (ToastSpeed)this.toastSpeed,
- });
- }
-
- if (ImGui.Button("Show Quest toast"))
- {
- toastGui.ShowQuest(this.inputTextToast, new QuestToastOptions
- {
- Position = (QuestToastPosition)this.questToastPosition,
- DisplayCheckmark = this.questToastCheckmark,
- IconId = (uint)this.questToastIconId,
- PlaySound = this.questToastSound,
- });
- }
-
- if (ImGui.Button("Show Error toast"))
- {
- toastGui.ShowError(this.inputTextToast);
- }
- }
-
- private void DrawFlyText()
- {
- if (ImGui.BeginCombo("Kind", this.flyKind.ToString()))
- {
- var names = Enum.GetNames(typeof(FlyTextKind));
- for (var i = 0; i < names.Length; i++)
- {
- if (ImGui.Selectable($"{names[i]} ({i})"))
- this.flyKind = (FlyTextKind)i;
- }
-
- ImGui.EndCombo();
- }
-
- ImGui.InputText("Text1", ref this.flyText1, 200);
- ImGui.InputText("Text2", ref this.flyText2, 200);
-
- ImGui.InputInt("Val1", ref this.flyVal1);
- ImGui.InputInt("Val2", ref this.flyVal2);
-
- ImGui.InputInt("Icon ID", ref this.flyIcon);
- ImGui.InputInt("Damage Icon ID", ref this.flyDmgIcon);
- ImGui.ColorEdit4("Color", ref this.flyColor);
- ImGui.InputInt("Actor Index", ref this.flyActor);
- var sendColor = ImGui.ColorConvertFloat4ToU32(this.flyColor);
-
- if (ImGui.Button("Send"))
- {
- Service.Get().AddFlyText(
- this.flyKind,
- unchecked((uint)this.flyActor),
- unchecked((uint)this.flyVal1),
- unchecked((uint)this.flyVal2),
- this.flyText1,
- this.flyText2,
- sendColor,
- unchecked((uint)this.flyIcon),
- unchecked((uint)this.flyDmgIcon));
- }
- }
-
- private void DrawImGui()
- {
- var interfaceManager = Service.Get();
- var notifications = Service.Get();
-
- ImGui.Text("Monitor count: " + ImGui.GetPlatformIO().Monitors.Size);
- ImGui.Text("OverrideGameCursor: " + interfaceManager.OverrideGameCursor);
-
- ImGui.Button("THIS IS A BUTTON###hoverTestButton");
- interfaceManager.OverrideGameCursor = !ImGui.IsItemHovered();
-
- ImGui.Separator();
-
- ImGui.TextUnformatted($"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
-
- ImGui.Separator();
-
- if (ImGui.Button("Add random notification"))
- {
- var rand = new Random();
-
- var title = rand.Next(0, 5) switch
- {
- 0 => "This is a toast",
- 1 => "Truly, a toast",
- 2 => "I am testing this toast",
- 3 => "I hope this looks right",
- 4 => "Good stuff",
- 5 => "Nice",
- _ => null,
- };
-
- var type = rand.Next(0, 4) switch
- {
- 0 => Notifications.NotificationType.Error,
- 1 => Notifications.NotificationType.Warning,
- 2 => Notifications.NotificationType.Info,
- 3 => Notifications.NotificationType.Success,
- 4 => Notifications.NotificationType.None,
- _ => Notifications.NotificationType.None,
- };
-
- var text = "Bla bla bla bla bla bla bla bla bla bla bla.\nBla bla bla bla bla bla bla bla bla bla bla bla bla bla.";
-
- notifications.AddNotification(text, title, type);
- }
- }
-
- private void DrawTex()
- {
- var dataManager = Service.Get();
-
- ImGui.InputText("Tex Path", ref this.inputTexPath, 255);
- ImGui.InputFloat2("UV0", ref this.inputTexUv0);
- ImGui.InputFloat2("UV1", ref this.inputTexUv1);
- ImGui.InputFloat4("Tint", ref this.inputTintCol);
- ImGui.InputFloat2("Scale", ref this.inputTexScale);
-
- if (ImGui.Button("Load Tex"))
- {
- try
- {
- this.debugTex = dataManager.GetImGuiTexture(this.inputTexPath);
- this.inputTexScale = new Vector2(this.debugTex.Width, this.debugTex.Height);
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Could not load tex.");
- }
- }
-
- ImGuiHelpers.ScaledDummy(10);
-
- if (this.debugTex != null)
- {
- ImGui.Image(this.debugTex.ImGuiHandle, this.inputTexScale, this.inputTexUv0, this.inputTexUv1, this.inputTintCol);
- ImGuiHelpers.ScaledDummy(5);
- Util.ShowObject(this.debugTex);
- }
- }
-
- private void DrawKeyState()
- {
- var keyState = Service.Get();
-
- ImGui.Columns(4);
-
- var i = 0;
- foreach (var vkCode in keyState.GetValidVirtualKeys())
- {
- var code = (int)vkCode;
- var value = keyState[code];
-
- ImGui.PushStyleColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed);
-
- ImGui.Text($"{vkCode} ({code})");
-
- ImGui.PopStyleColor();
-
- i++;
- if (i % 24 == 0)
- ImGui.NextColumn();
- }
-
- ImGui.Columns(1);
- }
-
- private void DrawGamepad()
- {
- var gamepadState = Service.Get();
-
- static void DrawHelper(string text, uint mask, Func resolve)
- {
- ImGui.Text($"{text} {mask:X4}");
- ImGui.Text($"DPadLeft {resolve(GamepadButtons.DpadLeft)} " +
- $"DPadUp {resolve(GamepadButtons.DpadUp)} " +
- $"DPadRight {resolve(GamepadButtons.DpadRight)} " +
- $"DPadDown {resolve(GamepadButtons.DpadDown)} ");
- ImGui.Text($"West {resolve(GamepadButtons.West)} " +
- $"North {resolve(GamepadButtons.North)} " +
- $"East {resolve(GamepadButtons.East)} " +
- $"South {resolve(GamepadButtons.South)} ");
- ImGui.Text($"L1 {resolve(GamepadButtons.L1)} " +
- $"L2 {resolve(GamepadButtons.L2)} " +
- $"R1 {resolve(GamepadButtons.R1)} " +
- $"R2 {resolve(GamepadButtons.R2)} ");
- ImGui.Text($"Select {resolve(GamepadButtons.Select)} " +
- $"Start {resolve(GamepadButtons.Start)} " +
- $"L3 {resolve(GamepadButtons.L3)} " +
- $"R3 {resolve(GamepadButtons.R3)} ");
- }
-
- ImGui.Text($"GamepadInput 0x{gamepadState.GamepadInputAddress.ToInt64():X}");
-
-#if DEBUG
- if (ImGui.IsItemHovered())
- ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
-
- if (ImGui.IsItemClicked())
- ImGui.SetClipboardText($"0x{gamepadState.GamepadInputAddress.ToInt64():X}");
-#endif
-
- DrawHelper(
- "Buttons Raw",
- gamepadState.ButtonsRaw,
- gamepadState.Raw);
- DrawHelper(
- "Buttons Pressed",
- gamepadState.ButtonsPressed,
- gamepadState.Pressed);
- DrawHelper(
- "Buttons Repeat",
- gamepadState.ButtonsRepeat,
- gamepadState.Repeat);
- DrawHelper(
- "Buttons Released",
- gamepadState.ButtonsReleased,
- gamepadState.Released);
- ImGui.Text($"LeftStickLeft {gamepadState.LeftStickLeft:0.00} " +
- $"LeftStickUp {gamepadState.LeftStickUp:0.00} " +
- $"LeftStickRight {gamepadState.LeftStickRight:0.00} " +
- $"LeftStickDown {gamepadState.LeftStickDown:0.00} ");
- ImGui.Text($"RightStickLeft {gamepadState.RightStickLeft:0.00} " +
- $"RightStickUp {gamepadState.RightStickUp:0.00} " +
- $"RightStickRight {gamepadState.RightStickRight:0.00} " +
- $"RightStickDown {gamepadState.RightStickDown:0.00} ");
- }
-
- private void DrawConfiguration()
- {
- var config = Service.Get();
- Util.ShowObject(config);
- }
-
- private void DrawTaskSched()
- {
- if (ImGui.Button("Clear list"))
- {
- TaskTracker.Clear();
- }
-
- ImGui.SameLine();
- ImGuiHelpers.ScaledDummy(10);
- ImGui.SameLine();
-
- if (ImGui.Button("Cancel using CancellationTokenSource"))
- {
- this.taskSchedCancelSource.Cancel();
- this.taskSchedCancelSource = new();
- }
-
- ImGui.Text("Run in any thread: ");
- ImGui.SameLine();
-
- if (ImGui.Button("Short Task.Run"))
- {
- Task.Run(() => { Thread.Sleep(500); });
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("Task in task(Delay)"))
- {
- var token = this.taskSchedCancelSource.Token;
- Task.Run(async () => await this.TestTaskInTaskDelay(token));
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("Task in task(Sleep)"))
- {
- Task.Run(async () => await this.TestTaskInTaskSleep());
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("Faulting task"))
- {
- Task.Run(() =>
- {
- Thread.Sleep(200);
-
- string a = null;
- a.Contains("dalamud");
- });
- }
-
- ImGui.Text("Run in Framework.Update: ");
- ImGui.SameLine();
-
- if (ImGui.Button("ASAP"))
- {
- Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedCancelSource.Token));
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("In 1s"))
- {
- Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("In 60f"))
- {
- Task.Run(async () => await Service.Get().RunOnTick(() => { }, cancellationToken: this.taskSchedCancelSource.Token, delayTicks: 60));
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("Error in 1s"))
- {
- Task.Run(async () => await Service.Get().RunOnTick(() => throw new Exception("Test Exception"), cancellationToken: this.taskSchedCancelSource.Token, delay: TimeSpan.FromSeconds(1)));
- }
-
- ImGui.SameLine();
-
- if (ImGui.Button("As long as it's in Framework Thread"))
- {
- Task.Run(async () => await Service.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from non-framework.update thread"); }));
- Service.Get().RunOnFrameworkThread(() => { Log.Information("Task dispatched from framework.update thread"); }).Wait();
- }
-
- if (ImGui.Button("Drown in tasks"))
- {
- var token = this.taskSchedCancelSource.Token;
- Task.Run(() =>
- {
- for (var i = 0; i < 100; i++)
- {
- token.ThrowIfCancellationRequested();
- Task.Run(() =>
- {
- for (var i = 0; i < 100; i++)
- {
- token.ThrowIfCancellationRequested();
- Task.Run(() =>
- {
- for (var i = 0; i < 100; i++)
- {
- token.ThrowIfCancellationRequested();
- Task.Run(() =>
- {
- for (var i = 0; i < 100; i++)
- {
- token.ThrowIfCancellationRequested();
- Task.Run(async () =>
- {
- for (var i = 0; i < 100; i++)
- {
- token.ThrowIfCancellationRequested();
- await Task.Delay(1);
- }
- });
- }
- });
- }
- });
- }
- });
- }
- });
- }
-
- ImGui.SameLine();
-
- ImGuiHelpers.ScaledDummy(20);
-
- // Needed to init the task tracker, if we're not on a debug build
- Service.Get().Enable();
-
- for (var i = 0; i < TaskTracker.Tasks.Count; i++)
- {
- var task = TaskTracker.Tasks[i];
- var subTime = DateTime.Now;
- if (task.Task == null)
- subTime = task.FinishTime;
-
- switch (task.Status)
- {
- case TaskStatus.Created:
- case TaskStatus.WaitingForActivation:
- case TaskStatus.WaitingToRun:
- ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudGrey);
- break;
- case TaskStatus.Running:
- case TaskStatus.WaitingForChildrenToComplete:
- ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedBlue);
- break;
- case TaskStatus.RanToCompletion:
- ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.ParsedGreen);
- break;
- case TaskStatus.Canceled:
- case TaskStatus.Faulted:
- ImGui.PushStyleColor(ImGuiCol.Header, ImGuiColors.DalamudRed);
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
-
- if (ImGui.CollapsingHeader($"#{task.Id} - {task.Status} {(subTime - task.StartTime).TotalMilliseconds}ms###task{i}"))
- {
- task.IsBeingViewed = true;
-
- if (ImGui.Button("CANCEL (May not work)"))
- {
- try
- {
- var cancelFunc =
- typeof(Task).GetMethod("InternalCancel", BindingFlags.NonPublic | BindingFlags.Instance);
- cancelFunc.Invoke(task, null);
- }
- catch (Exception ex)
- {
- Log.Error(ex, "Could not cancel task.");
- }
- }
-
- ImGuiHelpers.ScaledDummy(10);
-
- ImGui.TextUnformatted(task.StackTrace.ToString());
-
- if (task.Exception != null)
- {
- ImGuiHelpers.ScaledDummy(15);
- ImGui.TextColored(ImGuiColors.DalamudRed, "EXCEPTION:");
- ImGui.TextUnformatted(task.Exception.ToString());
- }
- }
- else
- {
- task.IsBeingViewed = false;
- }
-
- ImGui.PopStyleColor(1);
- }
- }
-
- private void DrawHook()
- {
- try
- {
- ImGui.Checkbox("Use MinHook", ref this.hookUseMinHook);
-
- if (ImGui.Button("Create"))
- this.messageBoxMinHook = Hook.FromSymbol("User32", "MessageBoxW", this.MessageBoxWDetour, this.hookUseMinHook);
-
- if (ImGui.Button("Enable"))
- this.messageBoxMinHook?.Enable();
-
- if (ImGui.Button("Disable"))
- this.messageBoxMinHook?.Disable();
-
- if (ImGui.Button("Call Original"))
- this.messageBoxMinHook?.Original(IntPtr.Zero, "Hello from .Original", "Hook Test", NativeFunctions.MessageBoxType.Ok);
-
- if (ImGui.Button("Dispose"))
- {
- this.messageBoxMinHook?.Dispose();
- this.messageBoxMinHook = null;
- }
-
- if (ImGui.Button("Test"))
- _ = NativeFunctions.MessageBoxW(IntPtr.Zero, "Hi", "Hello", NativeFunctions.MessageBoxType.Ok);
-
- if (this.messageBoxMinHook != null)
- ImGui.Text("Enabled: " + this.messageBoxMinHook?.IsEnabled);
- }
- catch (Exception ex)
- {
- Log.Error(ex, "MinHook error caught");
- }
- }
-
- private void DrawAetherytes()
- {
- if (!ImGui.BeginTable("##aetheryteTable", 11, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders))
- return;
-
- ImGui.TableSetupScrollFreeze(0, 1);
- ImGui.TableSetupColumn("Idx", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("ID", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Zone", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Ward", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Plot", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Sub", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Gil", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Fav", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Shared", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableSetupColumn("Appartment", ImGuiTableColumnFlags.WidthFixed);
- ImGui.TableHeadersRow();
-
- var tpList = Service.Get();
-
- for (var i = 0; i < tpList.Length; i++)
- {
- var info = tpList[i];
- if (info == null)
- continue;
-
- ImGui.TableNextColumn(); // Idx
- ImGui.TextUnformatted($"{i}");
-
- ImGui.TableNextColumn(); // Name
- ImGui.TextUnformatted($"{info.AetheryteData.GameData.PlaceName.Value?.Name}");
-
- ImGui.TableNextColumn(); // ID
- ImGui.TextUnformatted($"{info.AetheryteId}");
-
- ImGui.TableNextColumn(); // Zone
- ImGui.TextUnformatted($"{info.TerritoryId}");
-
- ImGui.TableNextColumn(); // Ward
- ImGui.TextUnformatted($"{info.Ward}");
-
- ImGui.TableNextColumn(); // Plot
- ImGui.TextUnformatted($"{info.Plot}");
-
- ImGui.TableNextColumn(); // Sub
- ImGui.TextUnformatted($"{info.SubIndex}");
-
- ImGui.TableNextColumn(); // Gil
- ImGui.TextUnformatted($"{info.GilCost}");
-
- ImGui.TableNextColumn(); // Favourite
- ImGui.TextUnformatted($"{info.IsFavourite}");
-
- ImGui.TableNextColumn(); // Shared
- ImGui.TextUnformatted($"{info.IsSharedHouse}");
-
- ImGui.TableNextColumn(); // Appartment
- ImGui.TextUnformatted($"{info.IsAppartment}");
- }
-
- ImGui.EndTable();
- }
-
- private void DrawDtr()
- {
- this.DrawDtrTestEntry(ref this.dtrTest1, "DTR Test #1");
- ImGui.Separator();
- this.DrawDtrTestEntry(ref this.dtrTest2, "DTR Test #2");
- ImGui.Separator();
- this.DrawDtrTestEntry(ref this.dtrTest3, "DTR Test #3");
- ImGui.Separator();
-
- var configuration = Service.Get();
- if (configuration.DtrOrder != null)
- {
- ImGui.Separator();
-
- foreach (var order in configuration.DtrOrder)
- {
- ImGui.Text(order);
- }
- }
- }
-
- private void DrawDtrTestEntry(ref DtrBarEntry? entry, string title)
- {
- var dtrBar = Service.Get();
-
- if (entry != null)
- {
- ImGui.Text(title);
-
- var text = entry.Text?.TextValue ?? string.Empty;
- if (ImGui.InputText($"Text###{entry.Title}t", ref text, 255))
- entry.Text = text;
-
- var shown = entry.Shown;
- if (ImGui.Checkbox($"Shown###{entry.Title}s", ref shown))
- entry.Shown = shown;
-
- if (ImGui.Button($"Remove###{entry.Title}r"))
- {
- entry.Remove();
- entry = null;
- }
- }
- else
- {
- if (ImGui.Button($"Add###{title}"))
- {
- entry = dtrBar.Get(title, title);
- }
- }
- }
-
- private void DrawDataShareTab()
- {
- if (!ImGui.BeginTable("###DataShareTable", 4, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg))
- return;
-
- try
- {
- ImGui.TableSetupColumn("Shared Tag");
- ImGui.TableSetupColumn("Creator Assembly");
- ImGui.TableSetupColumn("#", ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
- ImGui.TableSetupColumn("Consumers");
- ImGui.TableHeadersRow();
- foreach (var share in Service.Get().GetAllShares())
- {
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(share.Tag);
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(share.CreatorAssembly);
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(share.Users.Length.ToString());
- ImGui.TableNextColumn();
- ImGui.TextUnformatted(string.Join(", ", share.Users));
- }
- }
- finally
- {
- ImGui.EndTable();
- }
- }
-
- private void DrawUIColor()
- {
- var colorSheet = Service.Get().GetExcelSheet();
- if (colorSheet is null) return;
-
- foreach (var color in colorSheet)
- {
- this.DrawUiColor(color);
- }
- }
-
- private void DrawUiColor(UIColor color)
- {
- ImGui.Text($"[{color.RowId:D3}] ");
- ImGui.SameLine();
- ImGui.TextColored(this.ConvertToVector4(color.Unknown2), $"Unknown2 ");
- ImGui.SameLine();
- ImGui.TextColored(this.ConvertToVector4(color.UIForeground), "UIForeground ");
- ImGui.SameLine();
- ImGui.TextColored(this.ConvertToVector4(color.Unknown3), "Unknown3 ");
- ImGui.SameLine();
- ImGui.TextColored(this.ConvertToVector4(color.UIGlow), "UIGlow");
- }
-
- private Vector4 ConvertToVector4(uint color)
- {
- var r = (byte)(color >> 24);
- var g = (byte)(color >> 16);
- var b = (byte)(color >> 8);
- var a = (byte)color;
-
- return new Vector4(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
- }
-
- private async Task TestTaskInTaskDelay(CancellationToken token)
- {
- await Task.Delay(5000, token);
- }
-
-#pragma warning disable 1998
- private async Task TestTaskInTaskSleep()
-#pragma warning restore 1998
- {
- Thread.Sleep(5000);
- }
-
- private void Load()
- {
- var dataManager = Service.Get();
-
- if (dataManager.IsDataReady)
- {
- this.serverOpString = JsonConvert.SerializeObject(dataManager.ServerOpCodes, Formatting.Indented);
- this.wasReady = true;
- }
- }
-
- private void PrintGameObject(GameObject actor, string tag)
- {
- var actorString =
- $"{actor.Address.ToInt64():X}:{actor.ObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n";
-
- if (actor is Npc npc)
- actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n";
-
- if (actor is Character chara)
- {
- actorString +=
- $" Level: {chara.Level} ClassJob: {(this.resolveGameData ? chara.ClassJob.GameData.Name : chara.ClassJob.Id.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n";
- }
-
- if (actor is PlayerCharacter pc)
- {
- actorString +=
- $" HomeWorld: {(this.resolveGameData ? pc.HomeWorld.GameData.Name : pc.HomeWorld.Id.ToString())} CurrentWorld: {(this.resolveGameData ? pc.CurrentWorld.GameData.Name : pc.CurrentWorld.Id.ToString())} FC: {pc.CompanyTag}\n";
- }
-
- ImGui.TextUnformatted(actorString);
- ImGui.SameLine();
- if (ImGui.Button($"C##{this.copyButtonIndex++}"))
- {
- ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X"));
- }
- }
-}
diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs
index 4b8fe6822..60b1901d0 100644
--- a/Dalamud/Utility/Util.cs
+++ b/Dalamud/Utility/Util.cs
@@ -13,6 +13,7 @@ using System.Text;
using Dalamud.Configuration.Internal;
using Dalamud.Data;
using Dalamud.Game;
+using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Interface.Colors;
@@ -36,7 +37,7 @@ public static class Util
private static ulong moduleStartAddr;
private static ulong moduleEndAddr;
-
+
///
/// Gets an httpclient for usage.
/// Do NOT await this.
@@ -672,6 +673,40 @@ public static class Util
return names.ElementAt(rng.Next(0, names.Count() - 1)).Singular.RawString;
}
+ ///
+ /// Print formatted GameObject Information to ImGui
+ ///
+ /// Game Object to Display.
+ /// Display Tag.
+ /// If the GameObjects data should be resolved.
+ internal static void PrintGameObject(GameObject actor, string tag, bool resolveGameData)
+ {
+ var actorString =
+ $"{actor.Address.ToInt64():X}:{actor.ObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n";
+
+ if (actor is Npc npc)
+ actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n";
+
+ if (actor is Character chara)
+ {
+ actorString +=
+ $" Level: {chara.Level} ClassJob: {(resolveGameData ? chara.ClassJob.GameData?.Name : chara.ClassJob.Id.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n";
+ }
+
+ if (actor is PlayerCharacter pc)
+ {
+ actorString +=
+ $" HomeWorld: {(resolveGameData ? pc.HomeWorld.GameData?.Name : pc.HomeWorld.Id.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.GameData?.Name : pc.CurrentWorld.Id.ToString())} FC: {pc.CompanyTag}\n";
+ }
+
+ ImGui.TextUnformatted(actorString);
+ ImGui.SameLine();
+ if (ImGui.Button($"C##{actor.Address.ToInt64()}"))
+ {
+ ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X"));
+ }
+ }
+
private static unsafe void ShowValue(ulong addr, IEnumerable path, Type type, object value)
{
if (type.IsPointer)