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)