mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 21:24:16 +01:00
Merge pull request #245 from Caraxi/ui-debug
Add UiDebug to data window
This commit is contained in:
commit
87bac19d0b
2 changed files with 627 additions and 2 deletions
|
|
@ -36,6 +36,8 @@ namespace Dalamud.Interface
|
|||
|
||||
private bool resolveGameData = false;
|
||||
|
||||
private UIDebug UIDebug = null;
|
||||
|
||||
public DalamudDataWindow(Dalamud dalamud) {
|
||||
this.dalamud = dalamud;
|
||||
|
||||
|
|
@ -66,8 +68,8 @@ namespace Dalamud.Interface
|
|||
ImGui.SameLine();
|
||||
var copy = ImGui.Button("Copy all");
|
||||
ImGui.SameLine();
|
||||
ImGui.Combo("Data kind", ref this.currentKind, new[] {"ServerOpCode", "Address", "Actor Table", "Font Test", "Party List", "Plugin IPC", "Condition", "Gauge", "Command", "Addon", "StartInfo", "Target"},
|
||||
12);
|
||||
ImGui.Combo("Data kind", ref this.currentKind, new[] {"ServerOpCode", "Address", "Actor Table", "Font Test", "Party List", "Plugin IPC", "Condition", "Gauge", "Command", "Addon", "StartInfo", "Target", "UI Debug"},
|
||||
13);
|
||||
ImGui.Checkbox("Resolve GameData", ref this.resolveGameData);
|
||||
|
||||
ImGui.BeginChild("scrolling", new Vector2(0, 0), false, ImGuiWindowFlags.HorizontalScrollbar);
|
||||
|
|
@ -361,6 +363,11 @@ namespace Dalamud.Interface
|
|||
}
|
||||
|
||||
break;
|
||||
case 12: {
|
||||
this.UIDebug ??= new UIDebug(this.dalamud);
|
||||
this.UIDebug.Draw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ImGui.TextUnformatted("Data not ready.");
|
||||
|
|
|
|||
618
Dalamud/Interface/UiDebug.cs
Normal file
618
Dalamud/Interface/UiDebug.cs
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFXIVClientStructs.Component.GUI;
|
||||
using FFXIVClientStructs.Component.GUI.ULD;
|
||||
using ImGuiNET;
|
||||
|
||||
// Customised version of https://github.com/aers/FFXIVUIDebug
|
||||
|
||||
namespace Dalamud.Interface {
|
||||
|
||||
internal unsafe class UIDebug {
|
||||
private AtkUnitBase* selectedUnitBase = null;
|
||||
|
||||
private delegate AtkStage* GetAtkStageSingleton();
|
||||
private readonly GetAtkStageSingleton getAtkStageSingleton;
|
||||
|
||||
private const int UnitListCount = 18;
|
||||
private readonly bool[] selectedInList = new bool[UnitListCount];
|
||||
private readonly string[] listNames = new string[UnitListCount]{
|
||||
"Depth Layer 1",
|
||||
"Depth Layer 2",
|
||||
"Depth Layer 3",
|
||||
"Depth Layer 4",
|
||||
"Depth Layer 5",
|
||||
"Depth Layer 6",
|
||||
"Depth Layer 7",
|
||||
"Depth Layer 8",
|
||||
"Depth Layer 9",
|
||||
"Depth Layer 10",
|
||||
"Depth Layer 11",
|
||||
"Depth Layer 12",
|
||||
"Depth Layer 13",
|
||||
"Loaded Units",
|
||||
"Focused Units",
|
||||
"Units 16",
|
||||
"Units 17",
|
||||
"Units 18"
|
||||
};
|
||||
|
||||
private string searchInput = string.Empty;
|
||||
|
||||
|
||||
public UIDebug(Dalamud dalamud) {
|
||||
var getSingletonAddr = dalamud.SigScanner.ScanText("E8 ?? ?? ?? ?? 41 B8 01 00 00 00 48 8D 15 ?? ?? ?? ?? 48 8B 48 20 E8 ?? ?? ?? ?? 48 8B CF");
|
||||
this.getAtkStageSingleton = Marshal.GetDelegateForFunctionPointer<GetAtkStageSingleton>(getSingletonAddr);
|
||||
}
|
||||
|
||||
|
||||
public void Draw() {
|
||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(3, 2));
|
||||
ImGui.BeginChild("st_uiDebug_unitBaseSelect", new Vector2(250, -1), true);
|
||||
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
ImGui.InputTextWithHint("###atkUnitBaseSearch", "Search", ref this.searchInput, 0x20);
|
||||
|
||||
DrawUnitBaseList();
|
||||
ImGui.EndChild();
|
||||
if (selectedUnitBase != null) {
|
||||
ImGui.SameLine();
|
||||
ImGui.BeginChild("st_uiDebug_selectedUnitBase", new Vector2(-1, -1), true);
|
||||
DrawUnitBase(selectedUnitBase);
|
||||
ImGui.EndChild();
|
||||
}
|
||||
ImGui.PopStyleVar();
|
||||
}
|
||||
|
||||
private void DrawUnitBase(AtkUnitBase* atkUnitBase) {
|
||||
var isVisible = (atkUnitBase->Flags & 0x20) == 0x20;
|
||||
var addonName = Marshal.PtrToStringAnsi(new IntPtr(atkUnitBase->Name));
|
||||
|
||||
ImGui.Text($"{addonName}");
|
||||
ImGui.SameLine();
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, isVisible ? 0xFF00FF00 : 0xFF0000FF);
|
||||
ImGui.Text(isVisible ? "Visible" : "Not Visible");
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
ImGui.SameLine(ImGui.GetWindowContentRegionWidth() - 25);
|
||||
if (ImGui.SmallButton("V")) {
|
||||
atkUnitBase->Flags ^= 0x20;
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ClickToCopyText($"Address: {(ulong) atkUnitBase:X}", $"{(ulong) atkUnitBase:X}");
|
||||
ImGui.Separator();
|
||||
|
||||
ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]");
|
||||
ImGui.Text($"Scale: {atkUnitBase->Scale*100}%%");
|
||||
ImGui.Text($"Widget Count {atkUnitBase->ULDData.ObjectCount}");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
object addonObj = *atkUnitBase;
|
||||
|
||||
|
||||
PrintOutObject(addonObj, (ulong) atkUnitBase, new List<string>());
|
||||
|
||||
ImGui.Dummy(new Vector2(25 * ImGui.GetIO().FontGlobalScale));
|
||||
ImGui.Separator();
|
||||
if (atkUnitBase->RootNode != null)
|
||||
PrintNode(atkUnitBase->RootNode);
|
||||
|
||||
|
||||
if (atkUnitBase->ULDData.NodeListCount > 0) {
|
||||
ImGui.Dummy(new Vector2(25 * ImGui.GetIO().FontGlobalScale));
|
||||
ImGui.Separator();
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, 0xFFFFAAAA);
|
||||
if (ImGui.TreeNode($"Node List##{(ulong)atkUnitBase:X}")) {
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
for (var j = 0; j < atkUnitBase->ULDData.NodeListCount; j++) {
|
||||
PrintNode(atkUnitBase->ULDData.NodeList[j], false, $"[{j}] ");
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
} else {
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void PrintNode(AtkResNode* node, bool printSiblings = true, string treePrefix = "")
|
||||
{
|
||||
if (node == null)
|
||||
return;
|
||||
|
||||
if ((int)node->Type < 1000)
|
||||
PrintSimpleNode(node, treePrefix);
|
||||
else
|
||||
PrintComponentNode(node, treePrefix);
|
||||
|
||||
if (printSiblings)
|
||||
{
|
||||
var prevNode = node;
|
||||
while ((prevNode = prevNode->PrevSiblingNode) != null)
|
||||
PrintNode(prevNode, false, "prev ");
|
||||
|
||||
var nextNode = node;
|
||||
while ((nextNode = nextNode->NextSiblingNode) != null)
|
||||
PrintNode(nextNode, false, "next ");
|
||||
}
|
||||
}
|
||||
|
||||
private void PrintSimpleNode(AtkResNode* node, string treePrefix)
|
||||
{
|
||||
var popped = false;
|
||||
var isVisible = (node->Flags & 0x10) == 0x10;
|
||||
|
||||
if (isVisible)
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255));
|
||||
|
||||
if (ImGui.TreeNode($"{treePrefix}{node->Type} Node (ptr = {(long)node:X})###{(long)node}"))
|
||||
{
|
||||
if (ImGui.IsItemHovered()) DrawOutline(node);
|
||||
if (isVisible)
|
||||
{
|
||||
ImGui.PopStyleColor();
|
||||
popped = true;
|
||||
}
|
||||
|
||||
ImGui.Text("Node: ");
|
||||
ImGui.SameLine();
|
||||
ClickToCopyText($"{(ulong)node:X}");
|
||||
ImGui.SameLine();
|
||||
switch (node->Type) {
|
||||
case NodeType.Text: PrintOutObject(*(AtkTextNode*)node, (ulong) node, new List<string>()); break;
|
||||
case NodeType.Image: PrintOutObject(*(AtkImageNode*)node, (ulong) node, new List<string>()); break;
|
||||
case NodeType.Collision: PrintOutObject(*(AtkCollisionNode*)node, (ulong) node, new List<string>()); break;
|
||||
case NodeType.NineGrid: PrintOutObject(*(AtkNineGridNode*)node, (ulong) node, new List<string>()); break;
|
||||
case NodeType.Counter: PrintOutObject(*(AtkCounterNode*)node, (ulong) node, new List<string>()); break;
|
||||
default: PrintOutObject(*node, (ulong) node, new List<string>()); break;
|
||||
}
|
||||
|
||||
PrintResNode(node);
|
||||
|
||||
if (node->ChildNode != null)
|
||||
{
|
||||
PrintNode(node->ChildNode);
|
||||
}
|
||||
|
||||
switch (node->Type)
|
||||
{
|
||||
case NodeType.Text:
|
||||
var textNode = (AtkTextNode*)node;
|
||||
ImGui.Text($"text: {Marshal.PtrToStringAnsi(new IntPtr(textNode->NodeText.StringPtr))}");
|
||||
|
||||
ImGui.InputText($"Replace Text##{(ulong) textNode:X}", new IntPtr(textNode->NodeText.StringPtr), (uint) textNode->NodeText.BufSize);
|
||||
|
||||
|
||||
ImGui.Text($"AlignmentType: {(AlignmentType)textNode->AlignmentFontType} FontSize: {textNode->FontSize}");
|
||||
int b = textNode->AlignmentFontType;
|
||||
if (ImGui.InputInt($"###setAlignment{(ulong) textNode:X}", ref b, 1)) {
|
||||
while (b > byte.MaxValue) b -= byte.MaxValue;
|
||||
while (b < byte.MinValue) b += byte.MaxValue;
|
||||
textNode->AlignmentFontType = (byte) b;
|
||||
textNode->AtkResNode.Flags_2 |= 0x1;
|
||||
}
|
||||
|
||||
ImGui.Text($"Color: #{textNode->TextColor.R:X2}{textNode->TextColor.G:X2}{textNode->TextColor.B:X2}{textNode->TextColor.A:X2}");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text($"EdgeColor: #{textNode->EdgeColor.R:X2}{textNode->EdgeColor.G:X2}{textNode->EdgeColor.B:X2}{textNode->EdgeColor.A:X2}");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text($"BGColor: #{textNode->BackgroundColor.R:X2}{textNode->BackgroundColor.G:X2}{textNode->BackgroundColor.B:X2}{textNode->BackgroundColor.A:X2}");
|
||||
|
||||
ImGui.Text($"TextFlags: {textNode->TextFlags}");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text($"TextFlags2: {textNode->TextFlags2}");
|
||||
|
||||
|
||||
|
||||
break;
|
||||
case NodeType.Counter:
|
||||
var counterNode = (AtkCounterNode*)node;
|
||||
ImGui.Text($"text: {Marshal.PtrToStringAnsi(new IntPtr(counterNode->NodeText.StringPtr))}");
|
||||
break;
|
||||
case NodeType.Image:
|
||||
var imageNode = (AtkImageNode*)node;
|
||||
if (imageNode->PartsList != null) {
|
||||
if (imageNode->PartId > imageNode->PartsList->PartCount) {
|
||||
ImGui.Text("part id > part count?");
|
||||
} else {
|
||||
var textureInfo = imageNode->PartsList->Parts[imageNode->PartId].ULDTexture;
|
||||
var texType = textureInfo->AtkTexture.TextureType;
|
||||
ImGui.Text($"texture type: {texType} part_id={imageNode->PartId} part_id_count={imageNode->PartsList->PartCount}");
|
||||
if (texType == TextureType.Resource) {
|
||||
var texFileNamePtr = textureInfo->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName;
|
||||
var texString = Marshal.PtrToStringAnsi(new IntPtr(texFileNamePtr));
|
||||
ImGui.Text($"texture path: {texString}");
|
||||
var kernelTexture = textureInfo->AtkTexture.Resource->KernelTextureObject;
|
||||
|
||||
if (ImGui.TreeNode($"Texture##{(ulong) kernelTexture->D3D11ShaderResourceView:X}")) {
|
||||
ImGui.Image(new IntPtr(kernelTexture->D3D11ShaderResourceView), new Vector2(kernelTexture->Width, kernelTexture->Height));
|
||||
ImGui.TreePop();
|
||||
}
|
||||
} else if (texType == TextureType.KernelTexture) {
|
||||
if (ImGui.TreeNode($"Texture##{(ulong) textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView:X}")) {
|
||||
ImGui.Image(new IntPtr(textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView), new Vector2(textureInfo->AtkTexture.KernelTexture->Width, textureInfo->AtkTexture.KernelTexture->Height));
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ImGui.Text("no texture loaded");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
else if(ImGui.IsItemHovered()) DrawOutline(node);
|
||||
|
||||
if (isVisible && !popped)
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
private void PrintComponentNode(AtkResNode* node, string treePrefix)
|
||||
{
|
||||
var compNode = (AtkComponentNode*)node;
|
||||
|
||||
bool popped = false;
|
||||
bool isVisible = (node->Flags & 0x10) == 0x10;
|
||||
|
||||
if (isVisible)
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255));
|
||||
|
||||
var componentInfo = compNode->Component->ULDData;
|
||||
|
||||
var childCount = componentInfo.NodeListCount;
|
||||
|
||||
var objectInfo = (ULDComponentInfo*)componentInfo.Objects;
|
||||
if (ImGui.TreeNode($"{treePrefix}{objectInfo->ComponentType} Component Node (ptr = {(long)node:X}, component ptr = {(long)compNode->Component:X}) child count = {childCount} ###{(long)node}"))
|
||||
{
|
||||
if (ImGui.IsItemHovered()) DrawOutline(node);
|
||||
if (isVisible)
|
||||
{
|
||||
ImGui.PopStyleColor();
|
||||
popped = true;
|
||||
}
|
||||
|
||||
ImGui.Text("Node: ");
|
||||
ImGui.SameLine();
|
||||
ClickToCopyText($"{(ulong)node:X}");
|
||||
ImGui.SameLine();
|
||||
PrintOutObject(*compNode, (ulong) compNode, new List<string>());
|
||||
ImGui.Text("Component: ");
|
||||
ImGui.SameLine();
|
||||
ClickToCopyText($"{(ulong)compNode->Component:X}");
|
||||
ImGui.SameLine();
|
||||
|
||||
switch (objectInfo->ComponentType) {
|
||||
case ComponentType.Button: PrintOutObject(*(AtkComponentButton*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.Slider: PrintOutObject(*(AtkComponentSlider*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.Window: PrintOutObject(*(AtkComponentWindow*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.CheckBox: PrintOutObject(*(AtkComponentCheckBox*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.GaugeBar: PrintOutObject(*(AtkComponentGaugeBar*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.RadioButton: PrintOutObject(*(AtkComponentRadioButton*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.TextInput: PrintOutObject(*(AtkComponentTextInput*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
case ComponentType.Icon: PrintOutObject(*(AtkComponentIcon*)compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
default: PrintOutObject(*compNode->Component, (ulong) compNode->Component, new List<string>()); break;
|
||||
}
|
||||
|
||||
PrintResNode(node);
|
||||
PrintNode(componentInfo.RootNode);
|
||||
|
||||
switch (objectInfo->ComponentType)
|
||||
{
|
||||
case ComponentType.TextInput:
|
||||
var textInputComponent = (AtkComponentTextInput*)compNode->Component;
|
||||
ImGui.Text($"InputBase Text1: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->AtkComponentInputBase.UnkText1.StringPtr))}");
|
||||
ImGui.Text($"InputBase Text2: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->AtkComponentInputBase.UnkText2.StringPtr))}");
|
||||
ImGui.Text($"Text1: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->UnkText1.StringPtr))}");
|
||||
ImGui.Text($"Text2: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->UnkText2.StringPtr))}");
|
||||
ImGui.Text($"Text3: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->UnkText3.StringPtr))}");
|
||||
ImGui.Text($"Text4: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->UnkText4.StringPtr))}");
|
||||
ImGui.Text($"Text5: {Marshal.PtrToStringAnsi(new IntPtr(textInputComponent->UnkText5.StringPtr))}");
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, 0xFFFFAAAA);
|
||||
if (ImGui.TreeNode($"Node List##{(ulong) node:X}")) {
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
for (var i = 0; i < compNode->Component->ULDData.NodeListCount; i++) {
|
||||
PrintNode(compNode->Component->ULDData.NodeList[i], false, $"[{i}] ");
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
} else {
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
}
|
||||
else if (ImGui.IsItemHovered()) DrawOutline(node);
|
||||
|
||||
|
||||
if (isVisible && !popped)
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
private void PrintResNode(AtkResNode* node)
|
||||
{
|
||||
ImGui.Text($"NodeID: {node->NodeID}");
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton($"T:Visible##{(ulong)node:X}")) {
|
||||
node->Flags ^= 0x10;
|
||||
}
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton($"C:Ptr##{(ulong)node:X}")) {
|
||||
ImGui.SetClipboardText($"{(ulong)node:X}");
|
||||
}
|
||||
|
||||
|
||||
ImGui.Text(
|
||||
$"X: {node->X} Y: {node->Y} " +
|
||||
$"ScaleX: {node->ScaleX} ScaleY: {node->ScaleY} " +
|
||||
$"Rotation: {node->Rotation} " +
|
||||
$"Width: {node->Width} Height: {node->Height} " +
|
||||
$"OriginX: {node->OriginX} OriginY: {node->OriginY}");
|
||||
ImGui.Text(
|
||||
$"RGBA: 0x{node->Color.R:X2}{node->Color.G:X2}{node->Color.B:X2}{node->Color.A:X2} " +
|
||||
$"AddRGB: {node->AddRed} {node->AddGreen} {node->AddBlue} " +
|
||||
$"MultiplyRGB: {node->MultiplyRed} {node->MultiplyGreen} {node->MultiplyBlue}");
|
||||
}
|
||||
|
||||
|
||||
private bool doingSearch;
|
||||
|
||||
private bool DrawUnitListHeader(int index, uint count, ulong ptr, bool highlight) {
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, highlight ? 0xFFAAAA00 : 0xFFFFFFFF);
|
||||
if (!string.IsNullOrEmpty(this.searchInput) && !doingSearch) {
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Always);
|
||||
} else if (doingSearch && string.IsNullOrEmpty(this.searchInput)) {
|
||||
ImGui.SetNextItemOpen(false, ImGuiCond.Always);
|
||||
}
|
||||
var treeNode = ImGui.TreeNode($"{listNames[index]}##unitList_{index}");
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextDisabled($"C:{count} {ptr:X}");
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
private void DrawUnitBaseList() {
|
||||
|
||||
bool foundSelected = false;
|
||||
bool noResults = true;
|
||||
var stage = getAtkStageSingleton();
|
||||
|
||||
var unitManagers = &stage->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList;
|
||||
|
||||
var searchStr = this.searchInput;
|
||||
var searching = !string.IsNullOrEmpty(searchStr);
|
||||
|
||||
for (var i = 0; i < UnitListCount; i++) {
|
||||
|
||||
var headerDrawn = false;
|
||||
|
||||
var highlight = selectedUnitBase != null && selectedInList[i];
|
||||
selectedInList[i] = false;
|
||||
var unitManager = &unitManagers[i];
|
||||
|
||||
var unitBaseArray = &(unitManager->AtkUnitEntries);
|
||||
|
||||
var headerOpen = true;
|
||||
|
||||
if (!searching) {
|
||||
headerOpen = DrawUnitListHeader(i, unitManager->Count, (ulong) unitManager, highlight);
|
||||
headerDrawn = true;
|
||||
noResults = false;
|
||||
}
|
||||
|
||||
for (var j = 0; j < unitManager->Count && headerOpen; j++) {
|
||||
var unitBase = unitBaseArray[j];
|
||||
if (selectedUnitBase != null && unitBase == selectedUnitBase) {
|
||||
selectedInList[i] = true;
|
||||
foundSelected = true;
|
||||
}
|
||||
var name = Marshal.PtrToStringAnsi(new IntPtr(unitBase->Name));
|
||||
if (searching) {
|
||||
if (name == null || !name.ToLower().Contains(searchStr.ToLower())) continue;
|
||||
}
|
||||
noResults = false;
|
||||
if (!headerDrawn) {
|
||||
headerOpen = DrawUnitListHeader(i, unitManager->Count, (ulong) unitManager, highlight);
|
||||
headerDrawn = true;
|
||||
}
|
||||
|
||||
if (headerOpen) {
|
||||
var visible = (unitBase->Flags & 0x20) == 0x20;
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, visible ? 0xFF00FF00 : 0xFF999999);
|
||||
|
||||
if (ImGui.Selectable($"{name}##list{i}-{(ulong) unitBase:X}_{j}", selectedUnitBase == unitBase)) {
|
||||
selectedUnitBase = unitBase;
|
||||
foundSelected = true;
|
||||
selectedInList[i] = true;
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (headerDrawn && headerOpen) {
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
if (selectedInList[i] == false && selectedUnitBase != null) {
|
||||
for (var j = 0; j < unitManager->Count; j++) {
|
||||
if (selectedUnitBase == null || unitBaseArray[j] != selectedUnitBase) continue;
|
||||
selectedInList[i] = true;
|
||||
foundSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (noResults) {
|
||||
ImGui.TextDisabled("No Results");
|
||||
}
|
||||
|
||||
if (!foundSelected) {
|
||||
selectedUnitBase = null;
|
||||
}
|
||||
|
||||
|
||||
if (doingSearch && string.IsNullOrEmpty(this.searchInput)) {
|
||||
doingSearch = false;
|
||||
} else if (!doingSearch && !string.IsNullOrEmpty(this.searchInput)) {
|
||||
doingSearch = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Vector2 GetNodePosition(AtkResNode* node) {
|
||||
var pos = new Vector2(node->X, node->Y);
|
||||
var par = node->ParentNode;
|
||||
while (par != null) {
|
||||
pos *= new Vector2(par->ScaleX, par->ScaleY);
|
||||
pos += new Vector2(par->X, par->Y);
|
||||
par = par->ParentNode;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private Vector2 GetNodeScale(AtkResNode* node) {
|
||||
if (node == null) return new Vector2(1, 1);
|
||||
var scale = new Vector2(node->ScaleX, node->ScaleY);
|
||||
while (node->ParentNode != null) {
|
||||
node = node->ParentNode;
|
||||
scale *= new Vector2(node->ScaleX, node->ScaleY);
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
private bool GetNodeVisible(AtkResNode* node) {
|
||||
if (node == null) return false;
|
||||
while (node != null) {
|
||||
if ((node->Flags & (short)NodeFlags.Visible) != (short)NodeFlags.Visible) return false;
|
||||
node = node->ParentNode;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DrawOutline(AtkResNode* node) {
|
||||
var position = GetNodePosition(node);
|
||||
var scale = GetNodeScale(node);
|
||||
var size = new Vector2(node->Width, node->Height) * scale;
|
||||
|
||||
var nodeVisible = GetNodeVisible(node);
|
||||
ImGui.GetForegroundDrawList().AddRect(position, position + size, nodeVisible ? 0xFF00FF00 : 0xFF0000FF);
|
||||
}
|
||||
|
||||
private static void ClickToCopyText(string text, string textCopy = null) {
|
||||
textCopy ??= text;
|
||||
ImGui.Text($"{text}");
|
||||
if (ImGui.IsItemHovered()) {
|
||||
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
|
||||
if (textCopy != text) ImGui.SetTooltip(textCopy);
|
||||
}
|
||||
if (ImGui.IsItemClicked()) ImGui.SetClipboardText($"{textCopy}");
|
||||
}
|
||||
|
||||
private void PrintOutValue(ulong addr, IEnumerable<string> path, Type type, object value) {
|
||||
if (type.IsPointer) {
|
||||
var val = (Pointer) value;
|
||||
var unboxed = Pointer.Unbox(val);
|
||||
if (unboxed != null) {
|
||||
var unboxedAddr = (ulong) unboxed;
|
||||
ClickToCopyText($"{(ulong)unboxed:X}");
|
||||
if (beginModule > 0 && unboxedAddr >= this.beginModule && unboxedAddr <= this.endModule) {
|
||||
ImGui.SameLine();
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, 0xffcbc0ff);
|
||||
ClickToCopyText($"ffxiv_dx11.exe+{(unboxedAddr-this.beginModule):X}");
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
try {
|
||||
var eType = type.GetElementType();
|
||||
var ptrObj = Marshal.PtrToStructure(new IntPtr(unboxed), eType);
|
||||
ImGui.SameLine();
|
||||
PrintOutObject(ptrObj, (ulong) unboxed, new List<string>(path));
|
||||
} catch {
|
||||
// Ignored
|
||||
}
|
||||
} else {
|
||||
ImGui.Text("null");
|
||||
}
|
||||
} else {
|
||||
if (!type.IsPrimitive) {
|
||||
PrintOutObject(value, addr, new List<string>(path));
|
||||
} else {
|
||||
ImGui.Text($"{value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ulong beginModule;
|
||||
private ulong endModule;
|
||||
|
||||
|
||||
private void PrintOutObject(object obj, ulong addr, List<string> path, bool autoExpand = false) {
|
||||
if (this.endModule == 0 && this.beginModule == 0) {
|
||||
try {
|
||||
var processModule = Process.GetCurrentProcess().MainModule;
|
||||
if (processModule != null) {
|
||||
this.beginModule = (ulong) processModule.BaseAddress.ToInt64();
|
||||
this.endModule = (this.beginModule + (ulong) processModule.ModuleMemorySize);
|
||||
} else {
|
||||
this.endModule = 1;
|
||||
}
|
||||
} catch {
|
||||
this.endModule = 1;
|
||||
}
|
||||
}
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, 0xFF00FFFF);
|
||||
if (autoExpand) {
|
||||
ImGui.SetNextItemOpen(true, ImGuiCond.Appearing);
|
||||
}
|
||||
if (ImGui.TreeNode($"{obj}##print-obj-{addr:X}-{string.Join("-", path)}")) {
|
||||
ImGui.PopStyleColor();
|
||||
foreach (var f in obj.GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance)) {
|
||||
|
||||
var fixedBuffer = (FixedBufferAttribute) f.GetCustomAttribute(typeof(FixedBufferAttribute));
|
||||
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 {
|
||||
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();
|
||||
|
||||
PrintOutValue(addr, new List<string>(path) { f.Name }, f.FieldType, f.GetValue(obj));
|
||||
}
|
||||
|
||||
foreach (var p in obj.GetType().GetProperties()) {
|
||||
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();
|
||||
|
||||
PrintOutValue(addr, new List<string>(path) { p.Name }, p.PropertyType, p.GetValue(obj));
|
||||
}
|
||||
|
||||
ImGui.TreePop();
|
||||
} else {
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue