Minor interface adjustments (#2121)

- Further ImRaii safety in UiDebug2
- Set some mistakenly internal methods in ImGuiComponents to public
- Added SpanFullWidth flag to trees in Util.ShowStruct
This commit is contained in:
ItsBexy 2024-11-24 13:37:39 -07:00 committed by GitHub
parent de999b7895
commit c950b15a22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 282 additions and 264 deletions

View file

@ -270,7 +270,7 @@ public static partial class ImGuiComponents
/// <param name="icon">Icon to use.</param> /// <param name="icon">Icon to use.</param>
/// <param name="text">Text to use.</param> /// <param name="text">Text to use.</param>
/// <returns>Width.</returns> /// <returns>Width.</returns>
internal static float GetIconButtonWithTextWidth(FontAwesomeIcon icon, string text) public static float GetIconButtonWithTextWidth(FontAwesomeIcon icon, string text)
{ {
using (ImRaii.PushFont(UiBuilder.IconFont)) using (ImRaii.PushFont(UiBuilder.IconFont))
{ {

View file

@ -24,7 +24,7 @@ public static partial class ImGuiComponents
/// <param name="activeColor">The color of the actively-selected button.</param> /// <param name="activeColor">The color of the actively-selected button.</param>
/// <param name="hoveredColor">The color of the buttons when hovered.</param> /// <param name="hoveredColor">The color of the buttons when hovered.</param>
/// <returns>True if any button is clicked.</returns> /// <returns>True if any button is clicked.</returns>
internal static bool IconButtonSelect<T>(string label, ref T val, IEnumerable<FontAwesomeIcon> optionIcons, IEnumerable<T> optionValues, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) public static bool IconButtonSelect<T>(string label, ref T val, IEnumerable<FontAwesomeIcon> optionIcons, IEnumerable<T> optionValues, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null)
{ {
var options = optionIcons.Zip(optionValues, static (icon, value) => new KeyValuePair<FontAwesomeIcon, T>(icon, value)); var options = optionIcons.Zip(optionValues, static (icon, value) => new KeyValuePair<FontAwesomeIcon, T>(icon, value));
return IconButtonSelect(label, ref val, options, columns, buttonSize, defaultColor, activeColor, hoveredColor); return IconButtonSelect(label, ref val, options, columns, buttonSize, defaultColor, activeColor, hoveredColor);
@ -43,7 +43,7 @@ public static partial class ImGuiComponents
/// <param name="activeColor">The color of the actively-selected button.</param> /// <param name="activeColor">The color of the actively-selected button.</param>
/// <param name="hoveredColor">The color of the buttons when hovered.</param> /// <param name="hoveredColor">The color of the buttons when hovered.</param>
/// <returns>True if any button is clicked.</returns> /// <returns>True if any button is clicked.</returns>
internal static unsafe bool IconButtonSelect<T>(string label, ref T val, IEnumerable<KeyValuePair<FontAwesomeIcon, T>> options, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null) public static unsafe bool IconButtonSelect<T>(string label, ref T val, IEnumerable<KeyValuePair<FontAwesomeIcon, T>> options, uint columns = 0, Vector2? buttonSize = null, Vector4? defaultColor = null, Vector4? activeColor = null, Vector4? hoveredColor = null)
{ {
defaultColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.Button); defaultColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.Button);
activeColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonActive); activeColor ??= *ImGui.GetStyleColorVec4(ImGuiCol.ButtonActive);

View file

@ -11,6 +11,7 @@ using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
/// <inheritdoc cref="AddonTree"/>
public unsafe partial class AddonTree public unsafe partial class AddonTree
{ {
/// <summary> /// <summary>
@ -23,12 +24,11 @@ public unsafe partial class AddonTree
if (addon->AtkValuesCount > 0 && atkValue != null) if (addon->AtkValuesCount > 0 && atkValue != null)
{ {
using var tree = ImRaii.TreeNode($"Atk Values [{addon->AtkValuesCount}]###atkValues_{addon->NameString}"); using var tree = ImRaii.TreeNode($"Atk Values [{addon->AtkValuesCount}]###atkValues_{addon->NameString}");
if (tree) if (tree.Success)
{ {
using (ImRaii.Table( using var tbl = ImRaii.Table("atkUnitBase_atkValueTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
"atkUnitBase_atkValueTable",
3, if (tbl.Success)
ImGuiTableFlags.Borders | ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg))
{ {
ImGui.TableSetupColumn("Index"); ImGui.TableSetupColumn("Index");
ImGui.TableSetupColumn("Type"); ImGui.TableSetupColumn("Type");

View file

@ -30,9 +30,11 @@ public static class Events
using var tree = ImRaii.TreeNode($"Events##{(nint)node:X}eventTree"); using var tree = ImRaii.TreeNode($"Events##{(nint)node:X}eventTree");
if (tree) if (tree.Success)
{ {
using (ImRaii.Table($"##{(nint)node:X}eventTable", 7, Resizable | SizingFixedFit | Borders | RowBg)) using var tbl = ImRaii.Table($"##{(nint)node:X}eventTable", 7, Resizable | SizingFixedFit | Borders | RowBg);
if (tbl.Success)
{ {
ImGui.TableSetupColumn("#", WidthFixed); ImGui.TableSetupColumn("#", WidthFixed);
ImGui.TableSetupColumn("Type", WidthFixed); ImGui.TableSetupColumn("Type", WidthFixed);

View file

@ -27,7 +27,8 @@ internal unsafe partial class ResNodeTree
/// </summary> /// </summary>
private protected void DrawNodeEditorTable() private protected void DrawNodeEditorTable()
{ {
using (ImRaii.Table($"###Editor{(nint)this.Node}", 2, SizingStretchProp | NoHostExtendX)) using var tbl = ImRaii.Table($"###Editor{(nint)this.Node}", 2, SizingStretchProp | NoHostExtendX);
if (tbl.Success)
{ {
this.DrawEditorRows(); this.DrawEditorRows();
} }

View file

@ -65,7 +65,7 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
using var tree = ImRaii.TreeNode($"Texture##texture{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", SpanFullWidth); using var tree = ImRaii.TreeNode($"Texture##texture{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", SpanFullWidth);
if (tree) if (tree.Success)
{ {
PrintFieldValuePairs( PrintFieldValuePairs(
("Texture Type", $"{this.TexData.TexType}"), ("Texture Type", $"{this.TexData.TexType}"),
@ -189,7 +189,8 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
private void PrintPartsTable() private void PrintPartsTable()
{ {
using (ImRaii.Table($"partsTable##{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", 3, Borders | RowBg | Reorderable)) using var tbl = ImRaii.Table($"partsTable##{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", 3, Borders | RowBg | Reorderable);
if (tbl.Success)
{ {
ImGui.TableSetupColumn("Part ID", WidthFixed); ImGui.TableSetupColumn("Part ID", WidthFixed);
ImGui.TableSetupColumn("Part Texture", WidthFixed); ImGui.TableSetupColumn("Part Texture", WidthFixed);

View file

@ -1,69 +0,0 @@
using System.Numerics;
using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
/// <inheritdoc cref="NineGridNodeTree"/>
internal unsafe partial class NineGridNodeTree
{
/// <summary>
/// A struct representing the four offsets of an <see cref="AtkNineGridNode"/>.
/// </summary>
internal struct NineGridOffsets
{
/// <summary>Top offset.</summary>
internal int Top;
/// <summary>Left offset.</summary>
internal int Left;
/// <summary>Right offset.</summary>
internal int Right;
/// <summary>Bottom offset.</summary>
internal int Bottom;
/// <summary>
/// Initializes a new instance of the <see cref="NineGridOffsets"/> struct.
/// </summary>
/// <param name="top">The top offset.</param>
/// <param name="right">The right offset.</param>
/// <param name="bottom">The bottom offset.</param>
/// <param name="left">The left offset.</param>
internal NineGridOffsets(int top, int right, int bottom, int left)
{
this.Top = top;
this.Right = right;
this.Left = left;
this.Bottom = bottom;
}
/// <summary>
/// Initializes a new instance of the <see cref="NineGridOffsets"/> struct.
/// </summary>
/// <param name="ngNode">The node using these offsets.</param>
internal NineGridOffsets(AtkNineGridNode* ngNode)
: this(ngNode->TopOffset, ngNode->RightOffset, ngNode->BottomOffset, ngNode->LeftOffset)
{
}
private NineGridOffsets(Vector4 v)
: this((int)v.X, (int)v.Y, (int)v.Z, (int)v.W)
{
}
public static implicit operator NineGridOffsets(Vector4 v) => new(v);
public static implicit operator Vector4(NineGridOffsets v) => new(v.Top, v.Right, v.Bottom, v.Left);
public static NineGridOffsets operator *(float n, NineGridOffsets a) => n * (Vector4)a;
public static NineGridOffsets operator *(NineGridOffsets a, float n) => n * a;
/// <summary>Prints the offsets in ImGui.</summary>
internal readonly void Print() => PrintFieldValuePairs(("Top", $"{this.Top}"), ("Bottom", $"{this.Bottom}"), ("Left", $"{this.Left}"), ("Right", $"{this.Right}"));
}
}

View file

@ -1,3 +1,5 @@
using Dalamud.Interface.Internal.UiDebug2.Utility;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET; using ImGuiNET;
@ -85,4 +87,62 @@ internal unsafe partial class NineGridNodeTree : ImageNodeTree
this.DrawTextureAndParts(); this.DrawTextureAndParts();
} }
/// <summary>
/// A struct representing the four offsets of an <see cref="AtkNineGridNode"/>.
/// </summary>
internal struct NineGridOffsets
{
/// <summary>Top offset.</summary>
internal int Top;
/// <summary>Left offset.</summary>
internal int Left;
/// <summary>Right offset.</summary>
internal int Right;
/// <summary>Bottom offset.</summary>
internal int Bottom;
/// <summary>
/// Initializes a new instance of the <see cref="NineGridOffsets"/> struct.
/// </summary>
/// <param name="top">The top offset.</param>
/// <param name="right">The right offset.</param>
/// <param name="bottom">The bottom offset.</param>
/// <param name="left">The left offset.</param>
internal NineGridOffsets(int top, int right, int bottom, int left)
{
this.Top = top;
this.Right = right;
this.Left = left;
this.Bottom = bottom;
}
/// <summary>
/// Initializes a new instance of the <see cref="NineGridOffsets"/> struct.
/// </summary>
/// <param name="ngNode">The node using these offsets.</param>
internal NineGridOffsets(AtkNineGridNode* ngNode)
: this(ngNode->TopOffset, ngNode->RightOffset, ngNode->BottomOffset, ngNode->LeftOffset)
{
}
private NineGridOffsets(Vector4 v)
: this((int)v.X, (int)v.Y, (int)v.Z, (int)v.W)
{
}
public static implicit operator NineGridOffsets(Vector4 v) => new(v);
public static implicit operator Vector4(NineGridOffsets v) => new(v.Top, v.Right, v.Bottom, v.Left);
public static NineGridOffsets operator *(float n, NineGridOffsets a) => n * (Vector4)a;
public static NineGridOffsets operator *(NineGridOffsets a, float n) => n * a;
/// <summary>Prints the offsets in ImGui.</summary>
internal readonly void Print() => Gui.PrintFieldValuePairs(("Top", $"{this.Top}"), ("Bottom", $"{this.Bottom}"), ("Left", $"{this.Left}"), ("Right", $"{this.Right}"));
}
} }

View file

@ -128,11 +128,11 @@ internal unsafe partial class ResNodeTree : IDisposable
return; return;
} }
using var c = ImRaii.PushColor(Text, color); using var col = ImRaii.PushColor(Text, color);
using var tree = ImRaii.TreeNode($"{label}##{(nint)nodeList:X}", SpanFullWidth); using var tree = ImRaii.TreeNode($"{label}##{(nint)nodeList:X}", SpanFullWidth);
c.Pop(); col.Pop();
if (tree) if (tree.Success)
{ {
var lineStart = ImGui.GetCursorScreenPos() + new Vector2(-10, 2); var lineStart = ImGui.GetCursorScreenPos() + new Vector2(-10, 2);
@ -319,7 +319,7 @@ internal unsafe partial class ResNodeTree : IDisposable
col.Pop(); col.Pop();
if (tree) if (tree.Success)
{ {
var lineStart = ImGui.GetCursorScreenPos() + new Vector2(-10, 2); var lineStart = ImGui.GetCursorScreenPos() + new Vector2(-10, 2);
try try

View file

@ -85,7 +85,7 @@ internal unsafe partial class TextNodeTree : ResNodeTree
{ {
using var tree = ImRaii.TreeNode($"Text Payloads##{(nint)this.Node:X}"); using var tree = ImRaii.TreeNode($"Text Payloads##{(nint)this.Node:X}");
if (tree) if (tree.Success)
{ {
var utf8String = this.NodeText; var utf8String = this.NodeText;
var seStringBytes = new byte[utf8String.BufUsed]; var seStringBytes = new byte[utf8String.BufUsed];

View file

@ -58,7 +58,7 @@ public readonly unsafe partial struct TimelineTree
{ {
using var tree = ImRaii.TreeNode($"Timeline##{(nint)this.node:X}timeline", SpanFullWidth); using var tree = ImRaii.TreeNode($"Timeline##{(nint)this.node:X}timeline", SpanFullWidth);
if (tree) if (tree.Success)
{ {
PrintFieldValuePair("Timeline", $"{(nint)this.NodeTimeline:X}"); PrintFieldValuePair("Timeline", $"{(nint)this.NodeTimeline:X}");
@ -312,7 +312,7 @@ public readonly unsafe partial struct TimelineTree
{ {
using var tree = ImRaii.TreeNode($"[#{a}] [Frames {animation.StartFrameIdx}-{animation.EndFrameIdx}] {(isActive ? " (Active)" : string.Empty)}###{(nint)this.node}animTree{a}"); using var tree = ImRaii.TreeNode($"[#{a}] [Frames {animation.StartFrameIdx}-{animation.EndFrameIdx}] {(isActive ? " (Active)" : string.Empty)}###{(nint)this.node}animTree{a}");
if (tree) if (tree.Success)
{ {
PrintFieldValuePair("Animation", $"{address:X}"); PrintFieldValuePair("Animation", $"{address:X}");
@ -320,10 +320,9 @@ public readonly unsafe partial struct TimelineTree
if (columns.Count > 0) if (columns.Count > 0)
{ {
using (ImRaii.Table( using var tbl = ImRaii.Table($"##{(nint)this.node}animTable{a}", columns.Count, Borders | SizingFixedFit | RowBg | NoHostExtendX);
$"##{(nint)this.node}animTable{a}",
columns.Count, if (tbl.Success)
Borders | SizingFixedFit | RowBg | NoHostExtendX))
{ {
foreach (var c in columns) foreach (var c in columns)
{ {

View file

@ -79,7 +79,9 @@ internal unsafe class ElementSelector : IDisposable
/// </summary> /// </summary>
internal void DrawInterface() internal void DrawInterface()
{ {
using (ImRaii.Child("###sidebar_elementSelector", new(250, -1), true)) using var ch = ImRaii.Child("###sidebar_elementSelector", new(250, -1), true);
if (ch.Success)
{ {
using (ImRaii.PushFont(IconFont)) using (ImRaii.PushFont(IconFont))
{ {
@ -153,9 +155,11 @@ internal unsafe class ElementSelector : IDisposable
using (ImRaii.PushColor(WindowBg, new Vector4(0.5f))) using (ImRaii.PushColor(WindowBg, new Vector4(0.5f)))
{ {
using (ImRaii.Child("noClick", new(800, 2000), false, NoInputs | NoBackground | NoScrollWithMouse)) using var ch = ImRaii.Child("noClick", new(800, 2000), false, NoInputs | NoBackground | NoScrollWithMouse);
if (ch.Success)
{ {
using (ImRaii.Group()) using var gr = ImRaii.Group();
if (gr.Success)
{ {
Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}"); Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}");
ImGui.Spacing(); ImGui.Spacing();

View file

@ -39,7 +39,8 @@ internal class AddonPopoutWindow : Window, IDisposable
/// <inheritdoc/> /// <inheritdoc/>
public override void Draw() public override void Draw()
{ {
using (ImRaii.Child($"{this.WindowName}child", new(-1, -1), true)) using var ch = ImRaii.Child($"{this.WindowName}child", new(-1, -1), true);
if (ch.Success)
{ {
this.addonTree.Draw(); this.addonTree.Draw();
} }

View file

@ -50,7 +50,8 @@ internal unsafe class NodePopoutWindow : Window, IDisposable
{ {
if (this.Node != null && this.AddonTree.ContainsNode(this.Node)) if (this.Node != null && this.AddonTree.ContainsNode(this.Node))
{ {
using (ImRaii.Child($"{(nint)this.Node:X}popoutChild", new(-1, -1), true)) using var ch = ImRaii.Child($"{(nint)this.Node:X}popoutChild", new(-1, -1), true);
if (ch.Success)
{ {
ResNodeTree.GetOrCreate(this.Node, this.AddonTree).Print(null, this.firstDraw); ResNodeTree.GetOrCreate(this.Node, this.AddonTree).Print(null, this.firstDraw);
this.firstDraw = false; this.firstDraw = false;

View file

@ -53,7 +53,8 @@ internal unsafe partial class UiDebug2
private void DrawSidebar() private void DrawSidebar()
{ {
using (ImRaii.Group()) using var gr = ImRaii.Group();
if (gr.Success)
{ {
this.DrawNameSearch(); this.DrawNameSearch();
this.DrawAddonSelectionList(); this.DrawAddonSelectionList();
@ -63,7 +64,9 @@ internal unsafe partial class UiDebug2
private void DrawNameSearch() private void DrawNameSearch()
{ {
using (ImRaii.Child("###sidebar_nameSearch", new(250, 40), true)) using var ch = ImRaii.Child("###sidebar_nameSearch", new(250, 40), true);
if (ch.Success)
{ {
var atkUnitBaseSearch = this.addonNameSearch; var atkUnitBaseSearch = this.addonNameSearch;
@ -90,7 +93,8 @@ internal unsafe partial class UiDebug2
private void DrawAddonSelectionList() private void DrawAddonSelectionList()
{ {
using (ImRaii.Child("###sideBar_addonList", new(250, -44), true, ImGuiWindowFlags.AlwaysVerticalScrollbar)) using var ch = ImRaii.Child("###sideBar_addonList", new(250, -44), true, ImGuiWindowFlags.AlwaysVerticalScrollbar);
if (ch.Success)
{ {
var unitListBaseAddr = GetUnitListBaseAddr(); var unitListBaseAddr = GetUnitListBaseAddr();
@ -146,11 +150,11 @@ internal unsafe partial class UiDebug2
var countStr = $"{(usingFilter ? $"{matchCount}/" : string.Empty)}{totalCount}"; var countStr = $"{(usingFilter ? $"{matchCount}/" : string.Empty)}{totalCount}";
using var col1 = ImRaii.PushColor(ImGuiCol.Text, anyVisible ? new Vector4(1) : new Vector4(0.6f, 0.6f, 0.6f, 1)); using var col = ImRaii.PushColor(ImGuiCol.Text, anyVisible ? new Vector4(1) : new Vector4(0.6f, 0.6f, 0.6f, 1));
using var tree = ImRaii.TreeNode($"{unit.Name} [{countStr}]###unitListTree{unit.Index}"); using var tree = ImRaii.TreeNode($"{unit.Name} [{countStr}]###unitListTree{unit.Index}");
col1.Pop(); col.Pop();
if (tree) if (tree.Success)
{ {
foreach (var option in options) foreach (var option in options)
{ {

View file

@ -83,7 +83,9 @@ internal partial class UiDebug2 : IDisposable
{ {
ImGui.SameLine(); ImGui.SameLine();
using (ImRaii.Child("###uiDebugMainPanel", new(-1, -1), true, HorizontalScrollbar)) using var ch = ImRaii.Child("###uiDebugMainPanel", new(-1, -1), true, HorizontalScrollbar);
if (ch.Success)
{ {
if (this.elementSelector.Active) if (this.elementSelector.Active)
{ {

View file

@ -68,10 +68,10 @@ internal static class Gui
/// <remarks>Colors the text itself either white or black, depending on the luminosity of the background color.</remarks> /// <remarks>Colors the text itself either white or black, depending on the luminosity of the background color.</remarks>
internal static void PrintColor(Vector4 color, string fmt) internal static void PrintColor(Vector4 color, string fmt)
{ {
using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)) using (ImRaii.PushColor(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1))
.Push(Button, color) .Push(Button, color)
.Push(ButtonActive, color) .Push(ButtonActive, color)
.Push(ButtonHovered, color)) .Push(ButtonHovered, color))
{ {
ImGui.SmallButton(fmt); ImGui.SmallButton(fmt);
} }
@ -105,7 +105,9 @@ internal static class Gui
var index = (int)Math.Floor(prog * tooltips.Length); var index = (int)Math.Floor(prog * tooltips.Length);
using (ImRaii.Tooltip()) using var tt = ImRaii.Tooltip();
if (tt.Success)
{ {
ImGui.TextUnformatted(tooltips[index]); ImGui.TextUnformatted(tooltips[index]);
} }

View file

@ -28,6 +28,8 @@ using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Memory; using Windows.Win32.System.Memory;
using Windows.Win32.System.Ole; using Windows.Win32.System.Ole;
using Dalamud.Interface.Utility.Raii;
using static TerraFX.Interop.Windows.Windows; using static TerraFX.Interop.Windows.Windows;
using Win32_PInvoke = Windows.Win32.PInvoke; using Win32_PInvoke = Windows.Win32.PInvoke;
@ -1028,74 +1030,71 @@ public static class Util
dm.Invoke(null, new[] { obj, path, addr }); dm.Invoke(null, new[] { obj, path, addr });
} }
#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
private static unsafe void ShowSpanPrivate<T>(ulong addr, IList<string> path, int offset, bool isTop, in Span<T> spanobj) private static unsafe void ShowSpanPrivate<T>(ulong addr, IList<string> path, int offset, bool isTop, in Span<T> spanobj)
{ {
#pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
if (isTop) if (isTop)
{ {
fixed (void* p = spanobj) fixed (void* p = spanobj)
{ {
if (!ImGui.TreeNode( using var tree = ImRaii.TreeNode($"Span<{typeof(T).Name}> of length {spanobj.Length:n0} (0x{spanobj.Length:X})" + $"##print-obj-{addr:X}-{string.Join("-", path)}-head", ImGuiTreeNodeFlags.SpanFullWidth);
$"Span<{typeof(T).Name}> of length {spanobj.Length:n0} (0x{spanobj.Length:X})" + if (tree.Success)
$"##print-obj-{addr:X}-{string.Join("-", path)}-head"))
{ {
return; ShowSpanEntryPrivate(addr, path, offset, spanobj);
} }
} }
} }
else
try
{ {
const int batchSize = 20; ShowSpanEntryPrivate(addr, path, offset, spanobj);
if (spanobj.Length > batchSize)
{
var skip = batchSize;
while ((spanobj.Length + skip - 1) / skip > batchSize)
skip *= batchSize;
for (var i = 0; i < spanobj.Length; i += skip)
{
var next = Math.Min(i + skip, spanobj.Length);
path.Add($"{offset + i:X}_{skip}");
if (ImGui.TreeNode(
$"{offset + i:n0} ~ {offset + next - 1:n0} (0x{offset + i:X} ~ 0x{offset + next - 1:X})" +
$"##print-obj-{addr:X}-{string.Join("-", path)}"))
{
try
{
ShowSpanPrivate(addr, path, offset + i, false, spanobj[i..next]);
}
finally
{
ImGui.TreePop();
}
}
path.RemoveAt(path.Count - 1);
}
}
else
{
fixed (T* p = spanobj)
{
var pointerType = typeof(T*);
for (var i = 0; i < spanobj.Length; i++)
{
ImGui.TextUnformatted($"[{offset + i:n0} (0x{offset + i:X})] ");
ImGui.SameLine();
path.Add($"{offset + i}");
ShowValue(addr, path, pointerType, Pointer.Box(p + i, pointerType), true);
}
}
}
} }
finally
{
if (isTop)
ImGui.TreePop();
}
#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
} }
private static unsafe void ShowSpanEntryPrivate<T>(ulong addr, IList<string> path, int offset, Span<T> spanobj) {
const int batchSize = 20;
if (spanobj.Length > batchSize)
{
var skip = batchSize;
while ((spanobj.Length + skip - 1) / skip > batchSize)
{
skip *= batchSize;
}
for (var i = 0; i < spanobj.Length; i += skip)
{
var next = Math.Min(i + skip, spanobj.Length);
path.Add($"{offset + i:X}_{skip}");
using (var tree = ImRaii.TreeNode($"{offset + i:n0} ~ {offset + next - 1:n0} (0x{offset + i:X} ~ 0x{offset + next - 1:X})" + $"##print-obj-{addr:X}-{string.Join("-", path)}", ImGuiTreeNodeFlags.SpanFullWidth))
{
if (tree.Success)
{
ShowSpanEntryPrivate(addr, path, offset + i, spanobj[i..next]);
}
}
path.RemoveAt(path.Count - 1);
}
}
else
{
fixed (T* p = spanobj)
{
var pointerType = typeof(T*);
for (var i = 0; i < spanobj.Length; i++)
{
ImGui.TextUnformatted($"[{offset + i:n0} (0x{offset + i:X})] ");
ImGui.SameLine();
path.Add($"{offset + i}");
ShowValue(addr, path, pointerType, Pointer.Box(p + i, pointerType), true);
}
}
}
}
#pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type
private static unsafe void ShowValue(ulong addr, IList<string> path, Type type, object value, bool hideAddress) private static unsafe void ShowValue(ulong addr, IList<string> path, Type type, object value, bool hideAddress)
{ {
if (type.IsPointer) if (type.IsPointer)
@ -1111,9 +1110,10 @@ public static class Util
if (moduleStartAddr > 0 && unboxedAddr >= moduleStartAddr && unboxedAddr <= moduleEndAddr) if (moduleStartAddr > 0 && unboxedAddr >= moduleStartAddr && unboxedAddr <= moduleEndAddr)
{ {
ImGui.SameLine(); ImGui.SameLine();
ImGui.PushStyleColor(ImGuiCol.Text, 0xffcbc0ff); using (ImRaii.PushColor(ImGuiCol.Text, 0xffcbc0ff))
ImGuiHelpers.ClickToCopyText($"ffxiv_dx11.exe+{unboxedAddr - moduleStartAddr:X}"); {
ImGui.PopStyleColor(); ImGuiHelpers.ClickToCopyText($"ffxiv_dx11.exe+{unboxedAddr - moduleStartAddr:X}");
}
} }
ImGui.SameLine(); ImGui.SameLine();
@ -1165,125 +1165,135 @@ public static class Util
/// <param name="hideAddress">Do not print addresses. Use when displaying a copied value.</param> /// <param name="hideAddress">Do not print addresses. Use when displaying a copied value.</param>
private static void ShowStructInternal(object obj, ulong addr, bool autoExpand = false, IEnumerable<string>? path = null, bool hideAddress = false) private static void ShowStructInternal(object obj, ulong addr, bool autoExpand = false, IEnumerable<string>? path = null, bool hideAddress = false)
{ {
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(3, 2)); using (ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(3, 2)))
path ??= new List<string>();
var pathList = path is List<string> ? (List<string>)path : path.ToList();
if (moduleEndAddr == 0 && moduleStartAddr == 0)
{ {
try path ??= new List<string>();
var pathList = path as List<string> ?? path.ToList();
if (moduleEndAddr == 0 && moduleStartAddr == 0)
{ {
var processModule = Process.GetCurrentProcess().MainModule; try
if (processModule != null)
{ {
moduleStartAddr = (ulong)processModule.BaseAddress.ToInt64(); var processModule = Process.GetCurrentProcess().MainModule;
moduleEndAddr = moduleStartAddr + (ulong)processModule.ModuleMemorySize; if (processModule != null)
{
moduleStartAddr = (ulong)processModule.BaseAddress.ToInt64();
moduleEndAddr = moduleStartAddr + (ulong)processModule.ModuleMemorySize;
}
else
{
moduleEndAddr = 1;
}
} }
else catch
{ {
moduleEndAddr = 1; moduleEndAddr = 1;
} }
} }
catch
if (autoExpand)
{ {
moduleEndAddr = 1; ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
} }
}
ImGui.PushStyleColor(ImGuiCol.Text, 0xFF00FFFF); using var col = ImRaii.PushColor(ImGuiCol.Text, 0xFF00FFFF);
if (autoExpand) using var tree = ImRaii.TreeNode($"{obj}##print-obj-{addr:X}-{string.Join("-", pathList)}", ImGuiTreeNodeFlags.SpanFullWidth);
{ col.Pop();
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
}
if (ImGui.TreeNode($"{obj}##print-obj-{addr:X}-{string.Join("-", pathList)}")) if (tree.Success)
{
ImGui.PopStyleColor();
foreach (var f in obj.GetType()
.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance))
{ {
var fixedBuffer = (FixedBufferAttribute)f.GetCustomAttribute(typeof(FixedBufferAttribute)); ImGui.PopStyleColor();
var offset = (FieldOffsetAttribute)f.GetCustomAttribute(typeof(FieldOffsetAttribute)); foreach (var f in obj.GetType()
.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance))
{
var fixedBuffer = (FixedBufferAttribute)f.GetCustomAttribute(typeof(FixedBufferAttribute));
var offset = (FieldOffsetAttribute)f.GetCustomAttribute(typeof(FieldOffsetAttribute));
if (fixedBuffer != null) if (fixedBuffer != null)
{
ImGui.Text($"fixed");
ImGui.SameLine();
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1),
$"{fixedBuffer.ElementType.Name}[0x{fixedBuffer.Length:X}]");
}
else
{
if (offset != null)
{ {
ImGui.TextDisabled($"[0x{offset.Value:X}]"); ImGui.Text("fixed");
ImGui.SameLine(); ImGui.SameLine();
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{fixedBuffer.ElementType.Name}[0x{fixedBuffer.Length:X}]");
} }
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{f.FieldType.Name}");
}
ImGui.SameLine();
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.4f, 1), $"{f.Name}: ");
ImGui.SameLine();
pathList.Add(f.Name);
try
{
if (f.FieldType.IsGenericType && (f.FieldType.IsByRef || f.FieldType.IsByRefLike))
ImGui.Text("Cannot preview ref typed fields."); // object never contains ref struct
else if (f.FieldType == typeof(bool) && offset != null)
ShowValue(addr, pathList, f.FieldType, Marshal.ReadByte((nint)addr + offset.Value) > 0, hideAddress);
else else
ShowValue(addr, pathList, f.FieldType, f.GetValue(obj), hideAddress); {
if (offset != null)
{
ImGui.TextDisabled($"[0x{offset.Value:X}]");
ImGui.SameLine();
}
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{f.FieldType.Name}");
}
ImGui.SameLine();
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.4f, 1), $"{f.Name}: ");
ImGui.SameLine();
pathList.Add(f.Name);
try
{
if (f.FieldType.IsGenericType && (f.FieldType.IsByRef || f.FieldType.IsByRefLike))
{
ImGui.Text("Cannot preview ref typed fields."); // object never contains ref struct
}
else if (f.FieldType == typeof(bool) && offset != null)
{
ShowValue(addr, pathList, f.FieldType, Marshal.ReadByte((nint)addr + offset.Value) > 0, hideAddress);
}
else
{
ShowValue(addr, pathList, f.FieldType, f.GetValue(obj), hideAddress);
}
}
catch (Exception ex)
{
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1f, 0.4f, 0.4f, 1f)))
{
ImGui.TextUnformatted($"Error: {ex.GetType().Name}: {ex.Message}");
}
}
finally
{
pathList.RemoveAt(pathList.Count - 1);
}
} }
catch (Exception ex)
foreach (var p in obj.GetType().GetProperties().Where(static p => p.GetGetMethod()?.GetParameters().Length == 0))
{ {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.4f, 0.4f, 1f)); ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{p.PropertyType.Name}");
ImGui.TextUnformatted($"Error: {ex.GetType().Name}: {ex.Message}"); ImGui.SameLine();
ImGui.PopStyleColor(); ImGui.TextColored(new Vector4(0.2f, 0.6f, 0.4f, 1), $"{p.Name}: ");
} ImGui.SameLine();
finally
{ pathList.Add(p.Name);
pathList.RemoveAt(pathList.Count - 1); try
{
if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == GenericSpanType)
{
ShowSpanProperty(addr, pathList, p, obj);
}
else if (p.PropertyType.IsGenericType && (p.PropertyType.IsByRef || p.PropertyType.IsByRefLike))
{
ImGui.Text("Cannot preview ref typed properties.");
}
else
{
ShowValue(addr, pathList, p.PropertyType, p.GetValue(obj), hideAddress);
}
}
catch (Exception ex)
{
using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1f, 0.4f, 0.4f, 1f)))
{
ImGui.TextUnformatted($"Error: {ex.GetType().Name}: {ex.Message}");
}
}
finally
{
pathList.RemoveAt(pathList.Count - 1);
}
} }
} }
foreach (var p in obj.GetType().GetProperties().Where(p => p.GetGetMethod()?.GetParameters().Length == 0))
{
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{p.PropertyType.Name}");
ImGui.SameLine();
ImGui.TextColored(new Vector4(0.2f, 0.6f, 0.4f, 1), $"{p.Name}: ");
ImGui.SameLine();
pathList.Add(p.Name);
try
{
if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == GenericSpanType)
ShowSpanProperty(addr, pathList, p, obj);
else if (p.PropertyType.IsGenericType && (p.PropertyType.IsByRef || p.PropertyType.IsByRefLike))
ImGui.Text("Cannot preview ref typed properties.");
else
ShowValue(addr, pathList, p.PropertyType, p.GetValue(obj), hideAddress);
}
catch (Exception ex)
{
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0.4f, 0.4f, 1f));
ImGui.TextUnformatted($"Error: {ex.GetType().Name}: {ex.Message}");
ImGui.PopStyleColor();
}
finally
{
pathList.RemoveAt(pathList.Count - 1);
}
}
ImGui.TreePop();
} }
else
{
ImGui.PopStyleColor();
}
ImGui.PopStyleVar();
} }
} }