Merge branch 'master' into Adjustments

This commit is contained in:
MidoriKami 2026-01-27 12:49:29 -08:00
commit 0e3126f160
67 changed files with 813 additions and 1631 deletions

View file

@ -184,18 +184,18 @@ Global
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88FB719B-EB41-73C5-8D25-C03E0C69904F}.Release|Any CPU.Build.0 = Release|Any CPU
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.ActiveCfg = Debug|x64
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.Build.0 = Debug|x64
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.ActiveCfg = Release|x64
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.Build.0 = Release|x64
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.ActiveCfg = Debug|x64
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.Build.0 = Debug|x64
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.ActiveCfg = Release|x64
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.Build.0 = Release|x64
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.ActiveCfg = Debug|x64
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.Build.0 = Debug|x64
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.ActiveCfg = Release|x64
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.Build.0 = Release|x64
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{27AA9F87-D2AA-41D9-A559-0F1EBA38C5F8}.Release|Any CPU.Build.0 = Release|Any CPU
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CDAEB2D-5022-450A-A97F-181C6270185F}.Release|Any CPU.Build.0 = Release|Any CPU
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5D92D2D-D36F-4471-B657-8B9AA6C98AD6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -65,7 +65,6 @@
<PackageReference Include="CheapLoc" />
<PackageReference Include="DotNet.ReproducibleBuilds" PrivateAssets="all" />
<PackageReference Include="goatcorp.Reloaded.Hooks" />
<PackageReference Include="goatcorp.Reloaded.Assembler" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Lumina" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" />

View file

@ -81,7 +81,7 @@ public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable<Agent
/// Focuses the AtkUnitBase.
/// </summary>
/// <returns> <c>true</c> when the addon was focused, <c>false</c> otherwise. </returns>
public readonly bool FocusAddon() => this.IsNull && this.Struct->FocusAddon();
public readonly bool FocusAddon() => !this.IsNull && this.Struct->FocusAddon();
/// <summary>Determines whether the specified AgentInterfacePtr is equal to the current AgentInterfacePtr.</summary>
/// <param name="other">The AgentInterfacePtr to compare with the current AgentInterfacePtr.</param>

View file

@ -1,147 +0,0 @@
using System.Runtime.InteropServices;
using Dalamud.Configuration.Internal;
using Dalamud.Hooking;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Network;
using Serilog;
namespace Dalamud.Game.Network;
/// <summary>
/// This class handles interacting with game network events.
/// </summary>
[ServiceManager.EarlyLoadedService]
internal sealed unsafe class GameNetwork : IInternalDisposableService
{
private readonly GameNetworkAddressResolver address;
private readonly Hook<PacketDispatcher.Delegates.OnReceivePacket> processZonePacketDownHook;
private readonly Hook<ProcessZonePacketUpDelegate> processZonePacketUpHook;
private readonly HitchDetector hitchDetectorUp;
private readonly HitchDetector hitchDetectorDown;
[ServiceManager.ServiceDependency]
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
[ServiceManager.ServiceConstructor]
private unsafe GameNetwork(TargetSigScanner sigScanner)
{
this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch);
this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch);
this.address = new GameNetworkAddressResolver();
this.address.Setup(sigScanner);
var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket;
Log.Verbose("===== G A M E N E T W O R K =====");
Log.Verbose($"OnReceivePacket address {Util.DescribeAddress(onReceivePacketAddress)}");
Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}");
this.processZonePacketDownHook = Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour);
this.processZonePacketUpHook = Hook<ProcessZonePacketUpDelegate>.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);
this.processZonePacketDownHook.Enable();
this.processZonePacketUpHook.Enable();
}
/// <summary>
/// The delegate type of a network message event.
/// </summary>
/// <param name="dataPtr">The pointer to the raw data.</param>
/// <param name="opCode">The operation ID code.</param>
/// <param name="sourceActorId">The source actor ID.</param>
/// <param name="targetActorId">The taret actor ID.</param>
/// <param name="direction">The direction of the packed.</param>
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
/// <summary>
/// Event that is called when a network message is sent/received.
/// </summary>
public event OnNetworkMessageDelegate? NetworkMessage;
/// <inheritdoc/>
void IInternalDisposableService.DisposeService()
{
this.processZonePacketDownHook.Dispose();
this.processZonePacketUpHook.Dispose();
}
private void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr)
{
this.hitchDetectorDown.Start();
// Go back 0x10 to get back to the start of the packet header
dataPtr -= 0x10;
foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage))
{
try
{
d.Invoke(
dataPtr + 0x20,
(ushort)Marshal.ReadInt16(dataPtr, 0x12),
0,
targetId,
NetworkMessageDirection.ZoneDown);
}
catch (Exception ex)
{
string header;
try
{
var data = new byte[32];
Marshal.Copy(dataPtr, data, 0, 32);
header = BitConverter.ToString(data);
}
catch (Exception)
{
header = "failed";
}
Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header);
}
}
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
this.hitchDetectorDown.Stop();
}
private byte ProcessZonePacketUpDetour(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4)
{
this.hitchDetectorUp.Start();
try
{
// Call events
// TODO: Implement actor IDs
this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr), 0x0, 0x0, NetworkMessageDirection.ZoneUp);
}
catch (Exception ex)
{
string header;
try
{
var data = new byte[32];
Marshal.Copy(dataPtr, data, 0, 32);
header = BitConverter.ToString(data);
}
catch (Exception)
{
header = "failed";
}
Log.Error(ex, "Exception on ProcessZonePacketUp hook. Header: " + header);
}
this.hitchDetectorUp.Stop();
return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4);
}
}

View file

@ -1,20 +0,0 @@
using Dalamud.Plugin.Services;
namespace Dalamud.Game.Network;
/// <summary>
/// The address resolver for the <see cref="GameNetwork"/> class.
/// </summary>
internal sealed class GameNetworkAddressResolver : BaseAddressResolver
{
/// <summary>
/// Gets the address of the ProcessZonePacketUp method.
/// </summary>
public IntPtr ProcessZonePacketUp { get; private set; }
/// <inheritdoc/>
protected override void Setup64Bit(ISigScanner sig)
{
this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); // unnamed in cs
}
}

View file

@ -55,10 +55,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService
private bool disposing;
[ServiceManager.ServiceConstructor]
private NetworkHandlers(
GameNetwork gameNetwork,
TargetSigScanner sigScanner,
HappyHttpClient happyHttpClient)
private NetworkHandlers(TargetSigScanner sigScanner, HappyHttpClient happyHttpClient)
{
this.uploader = new UniversalisMarketBoardUploader(happyHttpClient);

View file

@ -1,17 +0,0 @@
namespace Dalamud.Game.Network;
/// <summary>
/// This represents the direction of a network message.
/// </summary>
public enum NetworkMessageDirection
{
/// <summary>
/// A zone down message.
/// </summary>
ZoneDown,
/// <summary>
/// A zone up message.
/// </summary>
ZoneUp,
}

View file

@ -311,9 +311,12 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
}
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row)
public bool IsLeveCompleted(Leve row)
{
return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId);
if (!this.IsLoaded)
return false;
return QuestManager.Instance()->IsLevequestComplete((ushort)row.RowId);
}
/// <inheritdoc/>
@ -328,6 +331,15 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return this.IsUnlockLinkUnlocked(row.UnlockLink);
}
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row)
{
if (!this.IsLoaded)
return false;
return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId);
}
/// <inheritdoc/>
public bool IsMountUnlocked(Mount row)
{
@ -376,9 +388,21 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return UIState.IsPublicContentUnlocked(row.RowId);
}
/// <inheritdoc/>
public bool IsQuestCompleted(Quest row)
{
if (!this.IsLoaded)
return false;
return QuestManager.IsQuestComplete(row.RowId);
}
/// <inheritdoc/>
public bool IsRecipeUnlocked(Recipe row)
{
if (!this.IsLoaded)
return false;
return this.recipeData.IsRecipeUnlocked(row);
}
@ -509,6 +533,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (rowRef.TryGetValue<Item>(out var itemRow))
return this.IsItemUnlocked(itemRow);
if (rowRef.TryGetValue<Leve>(out var leveRow))
return this.IsLeveCompleted(leveRow);
if (rowRef.TryGetValue<MJILandmark>(out var mjiLandmarkRow))
return this.IsMJILandmarkUnlocked(mjiLandmarkRow);
@ -536,6 +563,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (rowRef.TryGetValue<PublicContentSheet>(out var publicContentRow))
return this.IsPublicContentUnlocked(publicContentRow);
if (rowRef.TryGetValue<Quest>(out var questRow))
return this.IsQuestCompleted(questRow);
if (rowRef.TryGetValue<Recipe>(out var recipeRow))
return this.IsRecipeUnlocked(recipeRow);
@ -596,6 +626,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (!this.IsLoaded)
return;
Log.Verbose("Checking for new unlocks...");
this.UpdateUnlocksForSheet<ActionSheet>();
this.UpdateUnlocksForSheet<AetherCurrent>();
this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>();
@ -629,6 +661,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
this.UpdateUnlocksForSheet<Ornament>();
this.UpdateUnlocksForSheet<Perform>();
this.UpdateUnlocksForSheet<PublicContentSheet>();
this.UpdateUnlocksForSheet<Quest>();
this.UpdateUnlocksForSheet<Recipe>();
this.UpdateUnlocksForSheet<SecretRecipeBook>();
this.UpdateUnlocksForSheet<Trait>();
@ -637,6 +670,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
// Not implemented:
// - DescriptionPage: quite complex
// - QuestAcceptAdditionCondition: ignored
// - Leve: AgentUpdateFlag.UnlocksUpdate is not set and the completed status can be unset again!
// For some other day:
// - FishingSpot
@ -676,7 +710,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
unlockedRowIds.Add(row.RowId);
Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}");
// Log.Verbose($"Unlock detected: {typeof(T).Name}#{row.RowId}");
foreach (var action in Delegate.EnumerateInvocationList(this.Unlock))
{
@ -796,7 +830,7 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
public bool IsItemUnlocked(Item row) => this.unlockStateService.IsItemUnlocked(row);
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row);
public bool IsLeveCompleted(Leve row) => this.unlockStateService.IsLeveCompleted(row);
/// <inheritdoc/>
public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row);
@ -804,6 +838,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
/// <inheritdoc/>
public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row);
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row);
/// <inheritdoc/>
public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row);
@ -822,6 +859,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
/// <inheritdoc/>
public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row);
/// <inheritdoc/>
public bool IsQuestCompleted(Quest row) => this.unlockStateService.IsQuestCompleted(row);
/// <inheritdoc/>
public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row);

View file

@ -1,8 +1,13 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Game;
using Dalamud.Logging.Internal;
using InteropGenerator.Runtime;
namespace Dalamud.Hooking.Internal.Verification;
/// <summary>
@ -19,11 +24,13 @@ internal static class HookVerifier
new(
"ActorControlSelf",
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64",
typeof(ActorControlSelfDelegate),
typeof(ActorControlSelfDelegate), // TODO: change this to CS delegate
"Signature changed in Patch 7.4") // 7.4 (new parameters)
];
private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9);
private static readonly string ClientStructsInteropNamespacePrefix = string.Join(".", nameof(FFXIVClientStructs), nameof(FFXIVClientStructs.Interop));
private delegate void ActorControlSelfDelegate(uint category, uint eventId, uint param1, uint param2, uint param3, uint param4, uint param5, uint param6, uint param7, uint param8, ulong targetId, byte param9); // TODO: change this to CS delegate
/// <summary>
/// Initializes a new instance of the <see cref="HookVerifier"/> class.
@ -71,7 +78,7 @@ internal static class HookVerifier
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
// Compare Return Type
var mismatch = passedInvoke.ReturnType != enforcedInvoke.ReturnType;
var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType);
// Compare Parameter Count
var passedParams = passedInvoke.GetParameters();
@ -86,7 +93,7 @@ internal static class HookVerifier
// Compare Parameter Types
for (var i = 0; i < passedParams.Length; i++)
{
if (passedParams[i].ParameterType != enforcedParams[i].ParameterType)
if (!CheckParam(passedParams[i].ParameterType, enforcedParams[i].ParameterType))
{
mismatch = true;
break;
@ -100,6 +107,45 @@ internal static class HookVerifier
}
}
private static bool CheckParam(Type paramLeft, Type paramRight)
{
var sameType = paramLeft == paramRight;
return sameType || SizeOf(paramLeft) == SizeOf(paramRight);
}
private static int SizeOf(Type type)
{
return type switch {
_ when type == typeof(sbyte) || type == typeof(byte) || type == typeof(bool) => 1,
_ when type == typeof(char) || type == typeof(short) || type == typeof(ushort) || type == typeof(Half) => 2,
_ when type == typeof(int) || type == typeof(uint) || type == typeof(float) => 4,
_ when type == typeof(long) || type == typeof(ulong) || type == typeof(double) || type.IsPointer || type.IsFunctionPointer || type.IsUnmanagedFunctionPointer || (type.Name == "Pointer`1" && type.Namespace.AsSpan().SequenceEqual(ClientStructsInteropNamespacePrefix)) || type == typeof(CStringPointer) => 8,
_ when type.Name.StartsWith("FixedSizeArray") => SizeOf(type.GetGenericArguments()[0]) * int.Parse(type.Name[14..type.Name.IndexOf('`')]),
_ when type.GetCustomAttribute<InlineArrayAttribute>() is { Length: var length } => SizeOf(type.GetGenericArguments()[0]) * length,
_ when IsStruct(type) && !type.IsGenericType && (type.StructLayoutAttribute?.Value ?? LayoutKind.Sequential) != LayoutKind.Sequential => type.StructLayoutAttribute?.Size ?? (int?)typeof(Unsafe).GetMethod("SizeOf")?.MakeGenericMethod(type).Invoke(null, null) ?? 0,
_ when type.IsEnum => SizeOf(Enum.GetUnderlyingType(type)),
_ when type.IsGenericType => Marshal.SizeOf(Activator.CreateInstance(type)!),
_ => GetSizeOf(type),
};
}
private static int GetSizeOf(Type type)
{
try
{
return Marshal.SizeOf(Activator.CreateInstance(type)!);
}
catch
{
return 0;
}
}
private static bool IsStruct(Type type)
{
return type != typeof(decimal) && type is { IsValueType: true, IsPrimitive: false, IsEnum: false };
}
private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message)
{
public nint Address { get; set; }

View file

@ -1,675 +0,0 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game;
using Dalamud.Game.Gui;
using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.System.String;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.GUI;
// Customised version of https://github.com/aers/FFXIVUIDebug
namespace Dalamud.Interface.Internal;
/// <summary>
/// This class displays a debug window to inspect native addons.
/// </summary>
internal unsafe class UiDebug
{
private const int UnitListCount = 18;
private readonly bool[] selectedInList = new bool[UnitListCount];
private readonly string[] listNames =
[
"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 bool doingSearch;
private string searchInput = string.Empty;
private AtkUnitBase* selectedUnitBase = null;
/// <summary>
/// Initializes a new instance of the <see cref="UiDebug"/> class.
/// </summary>
public UiDebug()
{
}
/// <summary>
/// Renders this window.
/// </summary>
public void Draw()
{
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, new Vector2(3, 2));
ImGui.BeginChild("st_uiDebug_unitBaseSelect"u8, new Vector2(250, -1), true);
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("###atkUnitBaseSearch"u8, "Search"u8, ref this.searchInput, 0x20);
this.DrawUnitBaseList();
ImGui.EndChild();
if (this.selectedUnitBase != null)
{
ImGui.SameLine();
ImGui.BeginChild("st_uiDebug_selectedUnitBase"u8, new Vector2(-1, -1), true);
this.DrawUnitBase(this.selectedUnitBase);
ImGui.EndChild();
}
ImGui.PopStyleVar();
}
private void DrawUnitBase(AtkUnitBase* atkUnitBase)
{
var isVisible = atkUnitBase->IsVisible;
var addonName = atkUnitBase->NameString;
var agent = Service<GameGui>.Get().FindAgentInterface(atkUnitBase);
ImGui.Text(addonName);
ImGui.SameLine();
ImGui.PushStyleColor(ImGuiCol.Text, isVisible ? 0xFF00FF00 : 0xFF0000FF);
ImGui.Text(isVisible ? "Visible" : "Not Visible");
ImGui.PopStyleColor();
ImGui.SameLine(ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X - 25);
if (ImGui.SmallButton("V"u8))
{
atkUnitBase->IsVisible = !atkUnitBase->IsVisible;
}
ImGui.Separator();
ImGuiHelpers.ClickToCopyText($"Address: {(nint)atkUnitBase:X}", $"{(nint)atkUnitBase:X}");
ImGuiHelpers.ClickToCopyText($"Agent: {(nint)agent:X}", $"{(nint)agent:X}");
ImGui.Separator();
ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]");
ImGui.Text($"Scale: {atkUnitBase->Scale * 100}%");
ImGui.Text($"Widget Count {atkUnitBase->UldManager.ObjectCount}");
ImGui.Separator();
object addonObj = *atkUnitBase;
Util.ShowStruct(addonObj, (ulong)atkUnitBase);
ImGui.Dummy(new Vector2(25 * ImGui.GetIO().FontGlobalScale));
ImGui.Separator();
if (atkUnitBase->RootNode != null)
this.PrintNode(atkUnitBase->RootNode);
if (atkUnitBase->UldManager.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->UldManager.NodeListCount; j++)
{
this.PrintNode(atkUnitBase->UldManager.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)
this.PrintSimpleNode(node, treePrefix);
else
this.PrintComponentNode(node, treePrefix);
if (printSiblings)
{
var prevNode = node;
while ((prevNode = prevNode->PrevSiblingNode) != null)
this.PrintNode(prevNode, false, "prev ");
var nextNode = node;
while ((nextNode = nextNode->NextSiblingNode) != null)
this.PrintNode(nextNode, false, "next ");
}
}
private void PrintSimpleNode(AtkResNode* node, string treePrefix)
{
var popped = false;
var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible);
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())
this.DrawOutline(node);
if (isVisible)
{
ImGui.PopStyleColor();
popped = true;
}
ImGui.Text("Node: "u8);
ImGui.SameLine();
ImGuiHelpers.ClickToCopyText($"{(ulong)node:X}");
ImGui.SameLine();
switch (node->Type)
{
case NodeType.Text: Util.ShowStruct(*(AtkTextNode*)node, (ulong)node); break;
case NodeType.Image: Util.ShowStruct(*(AtkImageNode*)node, (ulong)node); break;
case NodeType.Collision: Util.ShowStruct(*(AtkCollisionNode*)node, (ulong)node); break;
case NodeType.NineGrid: Util.ShowStruct(*(AtkNineGridNode*)node, (ulong)node); break;
case NodeType.ClippingMask: Util.ShowStruct(*(AtkClippingMaskNode*)node, (ulong)node); break;
case NodeType.Counter: Util.ShowStruct(*(AtkCounterNode*)node, (ulong)node); break;
default: Util.ShowStruct(*node, (ulong)node); break;
}
this.PrintResNode(node);
if (node->ChildNode != null)
this.PrintNode(node->ChildNode);
switch (node->Type)
{
case NodeType.Text:
var textNode = (AtkTextNode*)node;
ImGui.Text("text: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textNode->NodeText);
ImGui.InputText($"Replace Text##{(ulong)textNode:X}", new(textNode->NodeText.StringPtr, (int)textNode->NodeText.BufSize));
ImGui.SameLine();
if (ImGui.Button($"Encode##{(ulong)textNode:X}"))
{
using var tmp = new Utf8String();
RaptureTextModule.Instance()->MacroEncoder.EncodeString(&tmp, textNode->NodeText.StringPtr);
textNode->NodeText.Copy(&tmp);
}
ImGui.SameLine();
if (ImGui.Button($"Decode##{(ulong)textNode:X}"))
textNode->NodeText.SetString(textNode->NodeText.StringPtr.AsReadOnlySeStringSpan().ToString());
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.DrawFlags |= 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}");
break;
case NodeType.Counter:
var counterNode = (AtkCounterNode*)node;
ImGui.Text("text: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(counterNode->NodeText);
break;
case NodeType.Image:
var imageNode = (AtkImageNode*)node;
PrintTextureInfo(imageNode->PartsList, imageNode->PartId);
break;
case NodeType.NineGrid:
var ngNode = (AtkNineGridNode*)node;
PrintTextureInfo(ngNode->PartsList, ngNode->PartId);
break;
case NodeType.ClippingMask:
var cmNode = (AtkClippingMaskNode*)node;
PrintTextureInfo(cmNode->PartsList, cmNode->PartId);
break;
}
ImGui.TreePop();
}
else if (ImGui.IsItemHovered())
{
this.DrawOutline(node);
}
if (isVisible && !popped)
ImGui.PopStyleColor();
static void PrintTextureInfo(AtkUldPartsList* partsList, uint partId)
{
if (partsList != null)
{
if (partId > partsList->PartCount)
{
ImGui.Text("part id > part count?"u8);
}
else
{
var textureInfo = partsList->Parts[partId].UldAsset;
var texType = textureInfo->AtkTexture.TextureType;
ImGui.Text(
$"texture type: {texType} part_id={partId} part_id_count={partsList->PartCount}");
if (texType == TextureType.Resource)
{
ImGui.Text(
$"texture path: {textureInfo->AtkTexture.Resource->TexFileResourceHandle->ResourceHandle.FileName}");
var kernelTexture = textureInfo->AtkTexture.Resource->KernelTextureObject;
if (ImGui.TreeNode($"Texture##{(ulong)kernelTexture->D3D11ShaderResourceView:X}"))
{
ImGui.Image(
new ImTextureID(kernelTexture->D3D11ShaderResourceView),
new Vector2(kernelTexture->ActualWidth, kernelTexture->ActualHeight));
ImGui.TreePop();
}
}
else if (texType == TextureType.KernelTexture)
{
if (ImGui.TreeNode(
$"Texture##{(ulong)textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView:X}"))
{
ImGui.Image(
new ImTextureID(textureInfo->AtkTexture.KernelTexture->D3D11ShaderResourceView),
new Vector2(
textureInfo->AtkTexture.KernelTexture->ActualWidth,
textureInfo->AtkTexture.KernelTexture->ActualHeight));
ImGui.TreePop();
}
}
if (ImGui.Button($"Replace with a random image##{(ulong)textureInfo:X}"))
{
var texm = Service<TextureManager>.Get();
texm.Shared
.GetFromGame(
Random.Shared.Next(0, 1) == 0
? $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}.tex"
: $"ui/loadingimage/-nowloading_base{Random.Shared.Next(1, 33)}_hr1.tex")
.RentAsync()
.ContinueWith(
r => Service<Framework>.Get().RunOnFrameworkThread(
() =>
{
if (!r.IsCompletedSuccessfully)
return;
using (r.Result)
{
textureInfo->AtkTexture.ReleaseTexture();
textureInfo->AtkTexture.KernelTexture =
texm.ConvertToKernelTexture(r.Result);
textureInfo->AtkTexture.TextureType = TextureType.KernelTexture;
}
}));
}
}
}
else
{
ImGui.Text("no texture loaded"u8);
}
}
}
private void PrintComponentNode(AtkResNode* node, string treePrefix)
{
var compNode = (AtkComponentNode*)node;
var popped = false;
var isVisible = node->NodeFlags.HasFlag(NodeFlags.Visible);
var componentInfo = compNode->Component->UldManager;
var childCount = componentInfo.NodeListCount;
var objectInfo = (AtkUldComponentInfo*)componentInfo.Objects;
if (objectInfo == null)
{
return;
}
if (isVisible)
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(0, 255, 0, 255));
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())
this.DrawOutline(node);
if (isVisible)
{
ImGui.PopStyleColor();
popped = true;
}
ImGui.Text("Node: "u8);
ImGui.SameLine();
ImGuiHelpers.ClickToCopyText($"{(ulong)node:X}");
ImGui.SameLine();
Util.ShowStruct(*compNode, (ulong)compNode);
ImGui.Text("Component: "u8);
ImGui.SameLine();
ImGuiHelpers.ClickToCopyText($"{(ulong)compNode->Component:X}");
ImGui.SameLine();
switch (objectInfo->ComponentType)
{
case ComponentType.Button: Util.ShowStruct(*(AtkComponentButton*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.Slider: Util.ShowStruct(*(AtkComponentSlider*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.Window: Util.ShowStruct(*(AtkComponentWindow*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.CheckBox: Util.ShowStruct(*(AtkComponentCheckBox*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.GaugeBar: Util.ShowStruct(*(AtkComponentGaugeBar*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.RadioButton: Util.ShowStruct(*(AtkComponentRadioButton*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.TextInput: Util.ShowStruct(*(AtkComponentTextInput*)compNode->Component, (ulong)compNode->Component); break;
case ComponentType.Icon: Util.ShowStruct(*(AtkComponentIcon*)compNode->Component, (ulong)compNode->Component); break;
default: Util.ShowStruct(*compNode->Component, (ulong)compNode->Component); break;
}
this.PrintResNode(node);
this.PrintNode(componentInfo.RootNode);
switch (objectInfo->ComponentType)
{
case ComponentType.TextInput:
var textInputComponent = (AtkComponentTextInput*)compNode->Component;
ImGui.Text("InputBase Text1: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->AtkComponentInputBase.EvaluatedString);
ImGui.Text("InputBase Text2: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->AtkComponentInputBase.RawString);
// ImGui.Text("Text1: "u8);
// ImGui.SameLine();
// Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText01);
//
// ImGui.Text("Text2: "u8);
// ImGui.SameLine();
// Service<SeStringRenderer>.Get().Draw(textInputComponent->UnkText02);
ImGui.Text("AvailableLines: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->AvailableLines);
ImGui.Text("HighlightedAutoTranslateOptionColorPrefix: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->HighlightedAutoTranslateOptionColorPrefix);
ImGui.Text("HighlightedAutoTranslateOptionColorSuffix: "u8);
ImGui.SameLine();
Service<SeStringRenderer>.Get().Draw(textInputComponent->HighlightedAutoTranslateOptionColorSuffix);
break;
}
ImGui.PushStyleColor(ImGuiCol.Text, 0xFFFFAAAA);
if (ImGui.TreeNode($"Node List##{(ulong)node:X}"))
{
ImGui.PopStyleColor();
for (var i = 0; i < compNode->Component->UldManager.NodeListCount; i++)
{
this.PrintNode(compNode->Component->UldManager.NodeList[i], false, $"[{i}] ");
}
ImGui.TreePop();
}
else
{
ImGui.PopStyleColor();
}
ImGui.TreePop();
}
else if (ImGui.IsItemHovered())
{
this.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->NodeFlags ^= NodeFlags.Visible;
}
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 DrawUnitListHeader(int index, ushort count, ulong ptr, bool highlight)
{
ImGui.PushStyleColor(ImGuiCol.Text, highlight ? 0xFFAAAA00 : 0xFFFFFFFF);
if (!string.IsNullOrEmpty(this.searchInput) && !this.doingSearch)
{
ImGui.SetNextItemOpen(true, ImGuiCond.Always);
}
else if (this.doingSearch && string.IsNullOrEmpty(this.searchInput))
{
ImGui.SetNextItemOpen(false, ImGuiCond.Always);
}
var treeNode = ImGui.TreeNode($"{this.listNames[index]}##unitList_{index}");
ImGui.PopStyleColor();
ImGui.SameLine();
ImGui.TextDisabled($"C:{count} {ptr:X}");
return treeNode;
}
private void DrawUnitBaseList()
{
var foundSelected = false;
var noResults = true;
var stage = AtkStage.Instance();
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 = this.selectedUnitBase != null && this.selectedInList[i];
this.selectedInList[i] = false;
var unitManager = &unitManagers[i];
var headerOpen = true;
if (!searching)
{
headerOpen = this.DrawUnitListHeader(i, unitManager->Count, (ulong)unitManager, highlight);
headerDrawn = true;
noResults = false;
}
for (var j = 0; j < unitManager->Count && headerOpen; j++)
{
AtkUnitBase* unitBase = unitManager->Entries[j];
if (this.selectedUnitBase != null && unitBase == this.selectedUnitBase)
{
this.selectedInList[i] = true;
foundSelected = true;
}
var name = unitBase->NameString;
if (searching)
{
if (name == null || !name.Contains(searchStr, StringComparison.InvariantCultureIgnoreCase)) continue;
}
noResults = false;
if (!headerDrawn)
{
headerOpen = this.DrawUnitListHeader(i, unitManager->Count, (ulong)unitManager, highlight);
headerDrawn = true;
}
if (headerOpen)
{
var visible = unitBase->IsVisible;
ImGui.PushStyleColor(ImGuiCol.Text, visible ? 0xFF00FF00 : 0xFF999999);
if (ImGui.Selectable($"{name}##list{i}-{(ulong)unitBase:X}_{j}", this.selectedUnitBase == unitBase))
{
this.selectedUnitBase = unitBase;
foundSelected = true;
this.selectedInList[i] = true;
}
ImGui.PopStyleColor();
}
}
if (headerDrawn && headerOpen)
{
ImGui.TreePop();
}
if (this.selectedInList[i] == false && this.selectedUnitBase != null)
{
for (var j = 0; j < unitManager->Count; j++)
{
AtkUnitBase* unitBase = unitManager->Entries[j];
if (this.selectedUnitBase == null || unitBase != this.selectedUnitBase) continue;
this.selectedInList[i] = true;
foundSelected = true;
}
}
}
if (noResults)
{
ImGui.TextDisabled("No Results"u8);
}
if (!foundSelected)
{
this.selectedUnitBase = null;
}
if (this.doingSearch && string.IsNullOrEmpty(this.searchInput))
{
this.doingSearch = false;
}
else if (!this.doingSearch && !string.IsNullOrEmpty(this.searchInput))
{
this.doingSearch = true;
}
}
private Vector2 GetNodePosition(AtkResNode* node)
{
var pos = new Vector2(node->X, node->Y);
pos -= new Vector2(node->OriginX * (node->ScaleX - 1), node->OriginY * (node->ScaleY - 1));
var par = node->ParentNode;
while (par != null)
{
pos *= new Vector2(par->ScaleX, par->ScaleY);
pos += new Vector2(par->X, par->Y);
pos -= new Vector2(par->OriginX * (par->ScaleX - 1), par->OriginY * (par->ScaleY - 1));
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->NodeFlags.HasFlag(NodeFlags.Visible)) return false;
node = node->ParentNode;
}
return true;
}
private void DrawOutline(AtkResNode* node)
{
var position = this.GetNodePosition(node);
var scale = this.GetNodeScale(node);
var size = new Vector2(node->Width, node->Height) * scale;
var nodeVisible = this.GetNodeVisible(node);
position += ImGuiHelpers.MainViewport.Pos;
ImGui.GetForegroundDrawList(ImGuiHelpers.MainViewport).AddRect(position, position + size, nodeVisible ? 0xFF00FF00 : 0xFF0000FF);
}
}

View file

@ -1,7 +1,7 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Utility;
using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
@ -9,7 +9,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="AddonTree"/>
public unsafe partial class AddonTree

View file

@ -7,9 +7,9 @@ using FFXIVClientStructs.Attributes;
using FFXIVClientStructs.FFXIV.Component.GUI;
using static System.Reflection.BindingFlags;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2;
using static Dalamud.Interface.Internal.UiDebug.UiDebug;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="AddonTree"/>
public unsafe partial class AddonTree

View file

@ -8,12 +8,12 @@ using Dalamud.Interface.Components;
using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug.UiDebug;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A class representing an <see cref="AtkUnitBase"/>, allowing it to be browsed within an ImGui window.

View file

@ -9,7 +9,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// Class that prints the events table for a node, where applicable.

View file

@ -2,7 +2,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkClippingMaskNode"/> that can be printed and browsed via ImGui.

View file

@ -2,7 +2,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkCollisionNode"/> that can be printed and browsed via ImGui.

View file

@ -6,11 +6,11 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Text.ReadOnly;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.ComponentType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkComponentNode"/> that can be printed and browsed via ImGui.

View file

@ -2,10 +2,10 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Text.ReadOnly;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkCounterNode"/> that can be printed and browsed via ImGui.

View file

@ -3,7 +3,7 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Utility;
using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -16,10 +16,10 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Interface.Utility.ImGuiHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="ResNodeTree"/>
internal unsafe partial class ResNodeTree

View file

@ -11,11 +11,11 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.TextureType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkImageNode"/> that can be printed and browsed via ImGui.

View file

@ -1,5 +1,5 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Utility;
using Dalamud.Interface.Internal.UiDebug.Utility;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -9,7 +9,7 @@ using static Dalamud.Utility.Util;
using Vector2 = System.Numerics.Vector2;
using Vector4 = System.Numerics.Vector4;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkNineGridNode"/> that can be printed and browsed via ImGui.

View file

@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Utility;
using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -13,14 +13,14 @@ using static Dalamud.Bindings.ImGui.ImGuiCol;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.Browsing.Events;
using static Dalamud.Interface.Internal.UiDebug2.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Browsing.Events;
using static Dalamud.Interface.Internal.UiDebug.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug.UiDebug;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkResNode"/> that can be printed and browsed via ImGui.

View file

@ -12,10 +12,10 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Text.ReadOnly;
using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A tree for an <see cref="AtkTextNode"/> that can be printed and browsed via ImGui.

View file

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Dalamud.Bindings.ImGui;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="TimelineTree"/>
public readonly partial struct TimelineTree

View file

@ -13,12 +13,12 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui;
using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType;
// ReSharper disable SuggestBaseTypeForParameter
namespace Dalamud.Interface.Internal.UiDebug2.Browsing;
namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary>
/// A struct allowing a node's animation timeline to be printed and browsed.

View file

@ -5,8 +5,8 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Browsing;
using Dalamud.Interface.Internal.UiDebug2.Utility;
using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -16,7 +16,7 @@ using static System.Globalization.NumberFormatInfo;
using static Dalamud.Bindings.ImGui.ImGuiCol;
using static Dalamud.Bindings.ImGui.ImGuiWindowFlags;
using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2;
using static Dalamud.Interface.Internal.UiDebug.UiDebug;
using static Dalamud.Interface.UiBuilder;
using static Dalamud.Interface.Utility.ImGuiHelpers;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
@ -25,7 +25,7 @@ using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
#pragma warning disable CS0659
namespace Dalamud.Interface.Internal.UiDebug2;
namespace Dalamud.Interface.Internal.UiDebug;
/// <summary>
/// A tool that enables the user to select UI elements within the inspector by mousing over them onscreen.
@ -34,7 +34,7 @@ internal unsafe class ElementSelector : IDisposable
{
private const int UnitListCount = 18;
private readonly UiDebug2 uiDebug2;
private readonly UiDebug uiDebug;
private string addressSearchInput = string.Empty;
@ -43,10 +43,10 @@ internal unsafe class ElementSelector : IDisposable
/// <summary>
/// Initializes a new instance of the <see cref="ElementSelector"/> class.
/// </summary>
/// <param name="uiDebug2">The instance of <see cref="UiDebug2"/> this Element Selector belongs to.</param>
internal ElementSelector(UiDebug2 uiDebug2)
/// <param name="uiDebug">The instance of <see cref="UiDebug"/> this Element Selector belongs to.</param>
internal ElementSelector(UiDebug uiDebug)
{
this.uiDebug2 = uiDebug2;
this.uiDebug = uiDebug;
}
/// <summary>
@ -181,7 +181,7 @@ internal unsafe class ElementSelector : IDisposable
{
this.Active = false;
this.uiDebug2.SelectedAddonName = a.Addon->NameString;
this.uiDebug.SelectedAddonName = a.Addon->NameString;
var ptrList = new List<nint> { (nint)n.Node };
@ -420,7 +420,7 @@ internal unsafe class ElementSelector : IDisposable
var addon = unitManager->Entries[j].Value;
if ((nint)addon == address || FindByAddress(addon, address))
{
this.uiDebug2.SelectedAddonName = addon->NameString;
this.uiDebug.SelectedAddonName = addon->NameString;
return;
}
}

View file

@ -1,11 +1,11 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Browsing;
using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
namespace Dalamud.Interface.Internal.UiDebug2;
namespace Dalamud.Interface.Internal.UiDebug;
/// <summary>
/// A popout window for an <see cref="AddonTree"/>.

View file

@ -1,15 +1,15 @@
using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Browsing;
using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2;
using static Dalamud.Interface.Internal.UiDebug.UiDebug;
namespace Dalamud.Interface.Internal.UiDebug2;
namespace Dalamud.Interface.Internal.UiDebug;
/// <summary>
/// A popout window for a <see cref="ResNodeTree"/>.

View file

@ -12,10 +12,10 @@ using static System.StringComparison;
using static Dalamud.Interface.FontAwesomeIcon;
namespace Dalamud.Interface.Internal.UiDebug2;
namespace Dalamud.Interface.Internal.UiDebug;
/// <inheritdoc cref="UiDebug2"/>
internal unsafe partial class UiDebug2
/// <inheritdoc cref="UiDebug"/>
internal unsafe partial class UiDebug
{
/// <summary>
/// All unit lists to check for addons.

View file

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Gui;
using Dalamud.Interface.Internal.UiDebug2.Browsing;
using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal;
@ -12,7 +12,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Bindings.ImGui.ImGuiWindowFlags;
namespace Dalamud.Interface.Internal.UiDebug2;
namespace Dalamud.Interface.Internal.UiDebug;
// Original version by aers https://github.com/aers/FFXIVUIDebug
// Also incorporates features from Caraxi's fork https://github.com/Caraxi/SimpleTweaksPlugin/blob/main/Debugging/UIDebug.cs
@ -20,17 +20,17 @@ namespace Dalamud.Interface.Internal.UiDebug2;
/// <summary>
/// A tool for browsing the contents and structure of UI elements.
/// </summary>
internal partial class UiDebug2 : IDisposable
internal partial class UiDebug : IDisposable
{
/// <inheritdoc cref="ModuleLog"/>
internal static readonly ModuleLog Log = ModuleLog.Create<UiDebug2>();
internal static readonly ModuleLog Log = ModuleLog.Create<UiDebug>();
private readonly ElementSelector elementSelector;
/// <summary>
/// Initializes a new instance of the <see cref="UiDebug2"/> class.
/// Initializes a new instance of the <see cref="UiDebug"/> class.
/// </summary>
internal UiDebug2()
internal UiDebug()
{
this.elementSelector = new(this);
}

View file

@ -9,10 +9,10 @@ using FFXIVClientStructs.FFXIV.Client.Graphics;
using static Dalamud.Bindings.ImGui.ImGuiCol;
using static Dalamud.Interface.ColorHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Utility;
namespace Dalamud.Interface.Internal.UiDebug.Utility;
/// <summary>
/// Miscellaneous ImGui tools used by <see cref="UiDebug2"/>.
/// Miscellaneous ImGui tools used by <see cref="UiDebug"/>.
/// </summary>
internal static class Gui
{

View file

@ -11,7 +11,7 @@ using static System.MathF;
using static Dalamud.Interface.ColorHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Utility;
namespace Dalamud.Interface.Internal.UiDebug.Utility;
/// <summary>
/// A struct representing the perimeter of an <see cref="AtkResNode"/>, accounting for all transformations.

View file

@ -21,7 +21,6 @@ internal class DataWindow : Window, IDisposable
private readonly IDataWindowWidget[] modules =
[
new AddonInspectorWidget(),
new AddonInspectorWidget2(),
new AddonLifecycleWidget(),
new AddonWidget(),
new AddressesWidget(),

View file

@ -5,8 +5,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class AddonInspectorWidget : IDataWindowWidget
{
private UiDebug? addonInspector;
private UiDebug.UiDebug? addonInspector;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["ai", "addoninspector"];
@ -19,7 +19,7 @@ internal class AddonInspectorWidget : IDataWindowWidget
/// <inheritdoc/>
public void Load()
{
this.addonInspector = new UiDebug();
this.addonInspector = new UiDebug.UiDebug();
if (this.addonInspector is not null)
{

View file

@ -1,35 +0,0 @@
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for displaying addon inspector.
/// </summary>
internal class AddonInspectorWidget2 : IDataWindowWidget
{
private UiDebug2.UiDebug2? addonInspector2;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["ai2", "addoninspector2"];
/// <inheritdoc/>
public string DisplayName { get; init; } = "Addon Inspector v2 (Testing)";
/// <inheritdoc/>
public bool Ready { get; set; }
/// <inheritdoc/>
public void Load()
{
this.addonInspector2 = new UiDebug2.UiDebug2();
if (this.addonInspector2 is not null)
{
this.Ready = true;
}
}
/// <inheritdoc/>
public void Draw()
{
this.addonInspector2?.Draw();
}
}

View file

@ -8,7 +8,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for displaying Addon Data.
/// </summary>
internal unsafe class AddonWidget : IDataWindowWidget
internal class AddonWidget : IDataWindowWidget
{
private string inputAddonName = string.Empty;
private int inputAddonIndex;

View file

@ -2,6 +2,7 @@ using System.Collections.Generic;
using Dalamud.Bindings.ImGui;
using Dalamud.Game;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -46,22 +47,24 @@ internal class AddressesWidget : IDataWindowWidget
}
}
ImGui.Text($"Result: {this.sigResult.ToInt64():X}");
ImGui.AlignTextToFramePadding();
ImGui.Text($"Result: {this.sigResult:X}");
ImGui.SameLine();
if (ImGui.Button($"C##{this.sigResult.ToInt64():X}"))
ImGui.SetClipboardText(this.sigResult.ToInt64().ToString("X"));
if (ImGui.Button($"C##{this.sigResult:X}"))
ImGui.SetClipboardText($"{this.sigResult:X}");
foreach (var debugScannedValue in BaseAddressResolver.DebugScannedValues)
{
ImGui.Text($"{debugScannedValue.Key}");
foreach (var valueTuple in debugScannedValue.Value)
{
ImGui.Text(
$" {valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}");
using var indent = ImRaii.PushIndent(10.0f);
ImGui.AlignTextToFramePadding();
ImGui.Text($"{valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}");
ImGui.SameLine();
if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}"))
ImGui.SetClipboardText(valueTuple.Address.ToInt64().ToString("X"));
if (ImGui.Button($"C##{valueTuple.Address:X}"))
ImGui.SetClipboardText($"{valueTuple.Address:X}");
}
}
}

View file

@ -1,5 +1,6 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Aetherytes;
using Dalamud.Interface.Utility.Raii;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -8,6 +9,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class AetherytesWidget : IDataWindowWidget
{
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders;
/// <inheritdoc/>
public bool Ready { get; set; }
@ -26,7 +29,8 @@ internal class AetherytesWidget : IDataWindowWidget
/// <inheritdoc/>
public void Draw()
{
if (!ImGui.BeginTable("##aetheryteTable"u8, 11, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders))
using var table = ImRaii.Table("##aetheryteTable"u8, 11, TableFlags);
if (!table.Success)
return;
ImGui.TableSetupScrollFreeze(0, 1);
@ -84,7 +88,5 @@ internal class AetherytesWidget : IDataWindowWidget
ImGui.TableNextColumn(); // Apartment
ImGui.Text($"{info.IsApartment}");
}
ImGui.EndTable();
}
}

View file

@ -25,10 +25,10 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
private int selectedExtendArray;
private string searchTerm = string.Empty;
private bool hideUnsetStringArrayEntries = false;
private bool hideUnsetExtendArrayEntries = false;
private bool showTextAddress = false;
private bool showMacroString = false;
private bool hideUnsetStringArrayEntries;
private bool hideUnsetExtendArrayEntries;
private bool showTextAddress;
private bool showMacroString;
/// <inheritdoc/>
public bool Ready { get; set; }
@ -155,17 +155,14 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
if (ImGui.IsItemHovered())
{
using var tooltip = ImRaii.Tooltip();
if (tooltip)
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
for (var j = 0; j < array->SubscribedAddonsCount; j++)
{
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
if (array->SubscribedAddons[j] == 0)
continue;
for (var j = 0; j < array->SubscribedAddonsCount; j++)
{
if (array->SubscribedAddons[j] == 0)
continue;
ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString);
}
ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString);
}
}
}
@ -244,9 +241,9 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
var atkArrayDataHolder = RaptureAtkModule.Instance()->AtkArrayDataHolder;
using (var sidebarchild = ImRaii.Child("StringArraySidebar"u8, new Vector2(300, -1), false, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings))
using (var sidebarChild = ImRaii.Child("StringArraySidebar"u8, new Vector2(300, -1), false, ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings))
{
if (sidebarchild)
if (sidebarChild)
{
ImGui.SetNextItemWidth(-1);
ImGui.InputTextWithHint("##TextSearch"u8, "Search..."u8, ref this.searchTerm, 256, ImGuiInputTextFlags.AutoSelectAll);

View file

@ -32,18 +32,65 @@ internal class BuddyListWidget : IDataWindowWidget
var buddyList = Service<BuddyList>.Get();
ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData);
var companionBuddy = buddyList.CompanionBuddy;
if (companionBuddy == null)
{
var member = buddyList.CompanionBuddy;
if (member == null)
ImGui.Text("[Companion] null"u8);
}
else
{
ImGui.Text($"[Companion] {companionBuddy.Address:X} - {companionBuddy.EntityId} - {companionBuddy.DataID}");
if (this.resolveGameData)
{
ImGui.Text("[Companion] null"u8);
var gameObject = companionBuddy.GameObject;
if (gameObject == null)
{
ImGui.Text("GameObject was null"u8);
}
else
{
Util.PrintGameObject(gameObject, "-", this.resolveGameData);
}
}
else
}
var petBuddy = buddyList.PetBuddy;
if (petBuddy == null)
{
ImGui.Text("[Pet] null"u8);
}
else
{
ImGui.Text($"[Pet] {petBuddy.Address:X} - {petBuddy.EntityId} - {petBuddy.DataID}");
if (this.resolveGameData)
{
ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}");
var gameObject = petBuddy.GameObject;
if (gameObject == null)
{
ImGui.Text("GameObject was null"u8);
}
else
{
Util.PrintGameObject(gameObject, "-", this.resolveGameData);
}
}
}
var count = buddyList.Length;
if (count == 0)
{
ImGui.Text("[BattleBuddy] None present"u8);
}
else
{
for (var i = 0; i < count; i++)
{
var member = buddyList[i];
ImGui.Text($"[BattleBuddy] [{i}] {member?.Address ?? 0:X} - {member?.EntityId ?? 0} - {member?.DataID ?? 0}");
if (this.resolveGameData)
{
var gameObject = member.GameObject;
var gameObject = member?.GameObject;
if (gameObject == null)
{
ImGui.Text("GameObject was null"u8);
@ -55,57 +102,5 @@ internal class BuddyListWidget : IDataWindowWidget
}
}
}
{
var member = buddyList.PetBuddy;
if (member == null)
{
ImGui.Text("[Pet] null"u8);
}
else
{
ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.EntityId} - {member.DataID}");
if (this.resolveGameData)
{
var gameObject = member.GameObject;
if (gameObject == null)
{
ImGui.Text("GameObject was null"u8);
}
else
{
Util.PrintGameObject(gameObject, "-", this.resolveGameData);
}
}
}
}
{
var count = buddyList.Length;
if (count == 0)
{
ImGui.Text("[BattleBuddy] None present"u8);
}
else
{
for (var i = 0; i < count; i++)
{
var member = buddyList[i];
ImGui.Text($"[BattleBuddy] [{i}] {member?.Address.ToInt64():X} - {member?.EntityId} - {member?.DataID}");
if (this.resolveGameData)
{
var gameObject = member?.GameObject;
if (gameObject == null)
{
ImGui.Text("GameObject was null"u8);
}
else
{
Util.PrintGameObject(gameObject, "-", this.resolveGameData);
}
}
}
}
}
}
}

View file

@ -11,6 +11,10 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class CommandWidget : IDataWindowWidget
{
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders |
ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Sortable |
ImGuiTableFlags.SortTristate;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["command"];
@ -31,9 +35,7 @@ internal class CommandWidget : IDataWindowWidget
{
var commandManager = Service<CommandManager>.Get();
var tableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders | ImGuiTableFlags.SizingStretchProp |
ImGuiTableFlags.Sortable | ImGuiTableFlags.SortTristate;
using var table = ImRaii.Table("CommandList"u8, 4, tableFlags);
using var table = ImRaii.Table("CommandList"u8, 4, TableFlags);
if (table)
{
ImGui.TableSetupScrollFreeze(0, 1);

View file

@ -10,9 +10,9 @@ internal class ConfigurationWidget : IDataWindowWidget
{
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["config", "configuration"];
/// <inheritdoc/>
public string DisplayName { get; init; } = "Configuration";
public string DisplayName { get; init; } = "Configuration";
/// <inheritdoc/>
public bool Ready { get; set; }

View file

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Reflection;
@ -21,13 +20,9 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for displaying plugin data share modules.
/// </summary>
[SuppressMessage(
"StyleCop.CSharp.LayoutRules",
"SA1519:Braces should not be omitted from multi-line child statement",
Justification = "Multiple fixed blocks")]
internal class DataShareWidget : IDataWindowWidget
{
private const ImGuiTabItemFlags NoCloseButton = (ImGuiTabItemFlags)(1 << 20);
private const ImGuiTabItemFlags NoCloseButton = (ImGuiTabItemFlags)ImGuiTabItemFlagsPrivate.NoCloseButton;
private readonly List<(string Name, byte[]? Data)> dataView = [];
private int nextTab = -1;
@ -50,42 +45,37 @@ internal class DataShareWidget : IDataWindowWidget
}
/// <inheritdoc/>
public unsafe void Draw()
public void Draw()
{
using var tabbar = ImRaii.TabBar("##tabbar"u8);
if (!tabbar.Success)
return;
var d = true;
using (var tabitem = ImRaii.TabItem(
"Data Share##tabbar-datashare"u8,
ref d,
NoCloseButton | (this.nextTab == 0 ? ImGuiTabItemFlags.SetSelected : 0)))
using (var tabItem = ImRaii.TabItem("Data Share##tabbar-datashare"u8, ref d, NoCloseButton | (this.nextTab == 0 ? ImGuiTabItemFlags.SetSelected : 0)))
{
if (tabitem.Success)
if (tabItem.Success)
this.DrawDataShare();
}
using (var tabitem = ImRaii.TabItem(
"Call Gate##tabbar-callgate"u8,
ref d,
NoCloseButton | (this.nextTab == 1 ? ImGuiTabItemFlags.SetSelected : 0)))
using (var tabItem = ImRaii.TabItem("Call Gate##tabbar-callgate"u8, ref d, NoCloseButton | (this.nextTab == 1 ? ImGuiTabItemFlags.SetSelected : 0)))
{
if (tabitem.Success)
if (tabItem.Success)
this.DrawCallGate();
}
for (var i = 0; i < this.dataView.Count; i++)
{
using var idpush = ImRaii.PushId($"##tabbar-data-{i}");
var (name, data) = this.dataView[i];
d = true;
using var tabitem = ImRaii.TabItem(
name,
ref d,
this.nextTab == 2 + i ? ImGuiTabItemFlags.SetSelected : 0);
using var tabitem = ImRaii.TabItem(name, ref d, this.nextTab == 2 + i ? ImGuiTabItemFlags.SetSelected : 0);
if (!d)
this.dataView.RemoveAt(i--);
if (!tabitem.Success)
continue;
@ -123,11 +113,7 @@ internal class DataShareWidget : IDataWindowWidget
if (ImGui.Button("Copy"u8))
ImGui.SetClipboardText(data);
ImGui.InputTextMultiline(
"text"u8,
data,
ImGui.GetContentRegionAvail(),
ImGuiInputTextFlags.ReadOnly);
ImGui.InputTextMultiline("text"u8, data, ImGui.GetContentRegionAvail(), ImGuiInputTextFlags.ReadOnly);
}
this.nextTab = -1;
@ -142,8 +128,10 @@ internal class DataShareWidget : IDataWindowWidget
sb.Append(ReprType(mi.DeclaringType))
.Append("::")
.Append(mi.Name);
if (!withParams)
return sb.ToString();
sb.Append('(');
var parfirst = true;
foreach (var par in mi.GetParameters())
@ -152,6 +140,7 @@ internal class DataShareWidget : IDataWindowWidget
sb.Append(", ");
else
parfirst = false;
sb.AppendLine()
.Append('\t')
.Append(ReprType(par.ParameterType))
@ -161,9 +150,11 @@ internal class DataShareWidget : IDataWindowWidget
if (!parfirst)
sb.AppendLine();
sb.Append(')');
if (mi.ReturnType != typeof(void))
sb.Append(" -> ").Append(ReprType(mi.ReturnType));
return sb.ToString();
static string WithoutGeneric(string s)
@ -172,8 +163,7 @@ internal class DataShareWidget : IDataWindowWidget
return i != -1 ? s[..i] : s;
}
static string ReprType(Type? t) =>
t switch
static string ReprType(Type? t) => t switch
{
null => "null",
_ when t == typeof(string) => "string",
@ -215,18 +205,19 @@ internal class DataShareWidget : IDataWindowWidget
var offset = ImGui.GetCursorScreenPos() + new Vector2(0, framepad ? ImGui.GetStyle().FramePadding.Y : 0);
if (framepad)
ImGui.AlignTextToFramePadding();
ImGui.Text(s);
if (ImGui.IsItemHovered())
{
ImGui.SetNextWindowPos(offset - ImGui.GetStyle().WindowPadding);
var vp = ImGui.GetWindowViewport();
var wrx = (vp.WorkPos.X + vp.WorkSize.X) - offset.X;
ImGui.SetNextWindowSizeConstraints(Vector2.One, new(wrx, float.MaxValue));
using (ImRaii.Tooltip())
{
ImGui.PushTextWrapPos(wrx);
using var pushedWrap = ImRaii.TextWrapPos(wrx);
ImGui.TextWrapped(tooltip?.Invoke() ?? s);
ImGui.PopTextWrapPos();
}
}
@ -247,6 +238,9 @@ internal class DataShareWidget : IDataWindowWidget
callGate.PurgeEmptyGates();
using var table = ImRaii.Table("##callgate-table"u8, 5);
if (!table.Success)
return;
ImGui.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.DefaultSort);
ImGui.TableSetupColumn("Action"u8);
ImGui.TableSetupColumn("Func"u8);
@ -268,12 +262,8 @@ internal class DataShareWidget : IDataWindowWidget
{
ImGui.TableNextRow();
this.DrawTextCell(item.Name);
this.DrawTextCell(
ReprMethod(item.Action?.Method, false),
() => ReprMethod(item.Action?.Method, true));
this.DrawTextCell(
ReprMethod(item.Func?.Method, false),
() => ReprMethod(item.Func?.Method, true));
this.DrawTextCell(ReprMethod(item.Action?.Method, false), () => ReprMethod(item.Action?.Method, true));
this.DrawTextCell(ReprMethod(item.Func?.Method, false), () => ReprMethod(item.Func?.Method, true));
if (subs.Count == 0)
{
this.DrawTextCell("0");
@ -288,47 +278,43 @@ internal class DataShareWidget : IDataWindowWidget
private void DrawDataShare()
{
if (!ImGui.BeginTable("###DataShareTable"u8, 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg))
using var table = ImRaii.Table("###DataShareTable"u8, 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
if (!table.Success)
return;
try
ImGui.TableSetupColumn("Shared Tag"u8);
ImGui.TableSetupColumn("Show"u8);
ImGui.TableSetupColumn("Creator Assembly"u8);
ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Consumers"u8);
ImGui.TableHeadersRow();
foreach (var share in Service<DataShare>.Get().GetAllShares())
{
ImGui.TableSetupColumn("Shared Tag"u8);
ImGui.TableSetupColumn("Show"u8);
ImGui.TableSetupColumn("Creator Assembly"u8);
ImGui.TableSetupColumn("#"u8, ImGuiTableColumnFlags.WidthFixed, 30 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Consumers"u8);
ImGui.TableHeadersRow();
foreach (var share in Service<DataShare>.Get().GetAllShares())
ImGui.TableNextRow();
this.DrawTextCell(share.Tag, null, true);
ImGui.TableNextColumn();
if (ImGui.Button($"Show##datasharetable-show-{share.Tag}"))
{
ImGui.TableNextRow();
this.DrawTextCell(share.Tag, null, true);
ImGui.TableNextColumn();
if (ImGui.Button($"Show##datasharetable-show-{share.Tag}"))
var index = 0;
for (; index < this.dataView.Count; index++)
{
var index = 0;
for (; index < this.dataView.Count; index++)
{
if (this.dataView[index].Name == share.Tag)
break;
}
if (index == this.dataView.Count)
this.dataView.Add((share.Tag, null));
else
this.dataView[index] = (share.Tag, null);
this.nextTab = 2 + index;
if (this.dataView[index].Name == share.Tag)
break;
}
this.DrawTextCell(share.CreatorAssembly, null, true);
this.DrawTextCell(share.Users.Length.ToString(), null, true);
this.DrawTextCell(string.Join(", ", share.Users), null, true);
if (index == this.dataView.Count)
this.dataView.Add((share.Tag, null));
else
this.dataView[index] = (share.Tag, null);
this.nextTab = 2 + index;
}
}
finally
{
ImGui.EndTable();
this.DrawTextCell(share.CreatorAssembly, null, true);
this.DrawTextCell(share.Users.Length.ToString(), null, true);
this.DrawTextCell(string.Join(", ", share.Users), null, true);
}
}
}

View file

@ -10,6 +10,9 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class FateTableWidget : IDataWindowWidget
{
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg |
ImGuiTableFlags.Borders | ImGuiTableFlags.NoSavedSettings;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["fate", "fatetable"];
@ -37,7 +40,7 @@ internal class FateTableWidget : IDataWindowWidget
return;
}
using var table = ImRaii.Table("FateTable"u8, 13, ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.NoSavedSettings);
using var table = ImRaii.Table("FateTable"u8, 13, TableFlags);
if (!table) return;
ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 40);
@ -97,11 +100,11 @@ internal class FateTableWidget : IDataWindowWidget
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
using var tooltip = ImRaii.Tooltip();
ImGui.Text("Click to copy IconId"u8);
ImGui.Text($"ID: {fate.IconId} Size: {texture.Width}x{texture.Height}");
ImGui.Image(texture.Handle, new(texture.Width, texture.Height));
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())
@ -122,11 +125,11 @@ internal class FateTableWidget : IDataWindowWidget
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
using var tooltip = ImRaii.Tooltip();
ImGui.Text("Click to copy MapIconId"u8);
ImGui.Text($"ID: {fate.MapIconId} Size: {texture.Width}x{texture.Height}");
ImGui.Image(texture.Handle, new(texture.Width, texture.Height));
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())

View file

@ -3,6 +3,7 @@ using System.Numerics;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Gui.FlyText;
using Dalamud.Interface.Utility.Raii;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -39,18 +40,18 @@ internal class FlyTextWidget : IDataWindowWidget
/// <inheritdoc/>
public void Draw()
{
if (ImGui.BeginCombo("Kind"u8, $"{this.flyKind} ({(int)this.flyKind})"))
using (var combo = ImRaii.Combo("Kind"u8, $"{this.flyKind} ({(int)this.flyKind})"))
{
var values = Enum.GetValues<FlyTextKind>().Distinct();
foreach (var value in values)
if (combo.Success)
{
if (ImGui.Selectable($"{value} ({(int)value})"))
foreach (var value in Enum.GetValues<FlyTextKind>().Distinct())
{
this.flyKind = value;
if (ImGui.Selectable($"{value} ({(int)value})"))
{
this.flyKind = value;
}
}
}
ImGui.EndCombo();
}
ImGui.InputText("Text1"u8, ref this.flyText1, 200);

View file

@ -13,6 +13,7 @@ using Dalamud.Interface.ImGuiFontChooserDialog;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using Serilog;
@ -248,7 +249,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{
ImGui.Text($"{gfs.SizePt}pt");
ImGui.SameLine(offsetX);
ImGui.PushTextWrapPos(this.useWordWrap ? 0f : -1f);
using var pushedWrap = ImRaii.TextWrapPos(this.useWordWrap ? 0f : -1f);
try
{
if (handle.Value.LoadException is { } exc)
@ -263,6 +265,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{
if (!this.atlasScaleMode)
ImGui.SetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
if (counter++ % 2 == 0)
{
using var pushPop = handle.Value.Push();
@ -279,7 +282,6 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
finally
{
ImGui.SetWindowFontScale(1);
ImGui.PopTextWrapPos();
}
}
}

View file

@ -39,22 +39,10 @@ internal class GamepadWidget : IDataWindowWidget
ImGui.SetClipboardText($"{Util.DescribeAddress(gamepadState.GamepadInputAddress)}");
#endif
this.DrawHelper(
"Buttons Raw",
(uint)gamepadState.ButtonsRaw,
gamepadState.Raw);
this.DrawHelper(
"Buttons Pressed",
(uint)gamepadState.ButtonsPressed,
gamepadState.Pressed);
this.DrawHelper(
"Buttons Repeat",
(uint)gamepadState.ButtonsRepeat,
gamepadState.Repeat);
this.DrawHelper(
"Buttons Released",
(uint)gamepadState.ButtonsReleased,
gamepadState.Released);
this.DrawHelper("Buttons Raw", (uint)gamepadState.ButtonsRaw, gamepadState.Raw);
this.DrawHelper("Buttons Pressed", (uint)gamepadState.ButtonsPressed, gamepadState.Pressed);
this.DrawHelper("Buttons Repeat", (uint)gamepadState.ButtonsRepeat, gamepadState.Repeat);
this.DrawHelper("Buttons Released", (uint)gamepadState.ButtonsReleased, gamepadState.Released);
ImGui.Text($"LeftStick {gamepadState.LeftStick}");
ImGui.Text($"RightStick {gamepadState.RightStick}");
}

View file

@ -39,8 +39,7 @@ internal class GaugeWidget : IDataWindowWidget
return;
}
var jobID = player.ClassJob.RowId;
JobGaugeBase? gauge = jobID switch
JobGaugeBase? gauge = player.ClassJob.RowId switch
{
19 => jobGauges.Get<PLDGauge>(),
20 => jobGauges.Get<MNKGauge>(),
@ -63,7 +62,7 @@ internal class GaugeWidget : IDataWindowWidget
40 => jobGauges.Get<SGEGauge>(),
41 => jobGauges.Get<VPRGauge>(),
42 => jobGauges.Get<PCTGauge>(),
_ => null,
_ => null
};
if (gauge == null)

View file

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Dalamud.Bindings.ImGui;
using Dalamud.Game;
using Dalamud.Hooking;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Serilog;
@ -26,12 +26,12 @@ internal unsafe class HookWidget : IDataWindowWidget
private Hook<MessageBoxWDelegate>? messageBoxMinHook;
private bool hookUseMinHook;
private int hookStressTestCount = 0;
private int hookStressTestCount;
private int hookStressTestMax = 1000;
private int hookStressTestWait = 100;
private int hookStressTestMaxDegreeOfParallelism = 10;
private StressTestHookTarget hookStressTestHookTarget = StressTestHookTarget.Random;
private bool hookStressTestRunning = false;
private bool hookStressTestRunning;
private MessageBoxWDelegate? messageBoxWOriginal;
private AddonFinalizeDelegate? addonFinalizeOriginal;
@ -50,7 +50,7 @@ internal unsafe class HookWidget : IDataWindowWidget
{
MessageBoxW,
AddonFinalize,
Random,
Random
}
/// <inheritdoc/>
@ -106,67 +106,68 @@ internal unsafe class HookWidget : IDataWindowWidget
ImGui.Separator();
ImGui.BeginDisabled(this.hookStressTestRunning);
ImGui.Text("Stress Test"u8);
if (ImGui.InputInt("Max"u8, ref this.hookStressTestMax))
this.hookStressTestCount = 0;
ImGui.InputInt("Wait (ms)"u8, ref this.hookStressTestWait);
ImGui.InputInt("Max Degree of Parallelism"u8, ref this.hookStressTestMaxDegreeOfParallelism);
if (ImGui.BeginCombo("Target"u8, HookTargetToString(this.hookStressTestHookTarget)))
using (ImRaii.Disabled(this.hookStressTestRunning))
{
foreach (var target in Enum.GetValues<StressTestHookTarget>())
ImGui.Text("Stress Test"u8);
if (ImGui.InputInt("Max"u8, ref this.hookStressTestMax))
this.hookStressTestCount = 0;
ImGui.InputInt("Wait (ms)"u8, ref this.hookStressTestWait);
ImGui.InputInt("Max Degree of Parallelism"u8, ref this.hookStressTestMaxDegreeOfParallelism);
using (var combo = ImRaii.Combo("Target"u8, HookTargetToString(this.hookStressTestHookTarget)))
{
if (ImGui.Selectable(HookTargetToString(target), this.hookStressTestHookTarget == target))
this.hookStressTestHookTarget = target;
if (combo.Success)
{
foreach (var target in Enum.GetValues<StressTestHookTarget>())
{
if (ImGui.Selectable(HookTargetToString(target), this.hookStressTestHookTarget == target))
this.hookStressTestHookTarget = target;
}
}
}
ImGui.EndCombo();
}
if (ImGui.Button("Stress Test"u8))
{
Task.Run(() =>
if (ImGui.Button("Stress Test"u8))
{
this.hookStressTestRunning = true;
this.hookStressTestCount = 0;
Parallel.For(
0,
this.hookStressTestMax,
new ParallelOptions
Task.Run(() =>
{
this.hookStressTestRunning = true;
this.hookStressTestCount = 0;
Parallel.For(
0,
this.hookStressTestMax,
new ParallelOptions
{
MaxDegreeOfParallelism = this.hookStressTestMaxDegreeOfParallelism,
},
_ =>
{
this.hookStressTestList.Add(this.HookTarget(this.hookStressTestHookTarget));
this.hookStressTestCount++;
Thread.Sleep(this.hookStressTestWait);
});
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
MaxDegreeOfParallelism = this.hookStressTestMaxDegreeOfParallelism,
},
_ =>
Log.Error(t.Exception, "Stress test failed");
}
else
{
this.hookStressTestList.Add(this.HookTarget(this.hookStressTestHookTarget));
this.hookStressTestCount++;
Thread.Sleep(this.hookStressTestWait);
Log.Information("Stress test completed");
}
this.hookStressTestRunning = false;
this.hookStressTestList.ForEach(hook =>
{
hook.Dispose();
});
}).ContinueWith(t =>
{
if (t.IsFaulted)
{
Log.Error(t.Exception, "Stress test failed");
}
else
{
Log.Information("Stress test completed");
}
this.hookStressTestRunning = false;
this.hookStressTestList.ForEach(hook =>
{
hook.Dispose();
this.hookStressTestList.Clear();
});
this.hookStressTestList.Clear();
});
}
}
ImGui.EndDisabled();
ImGui.Text("Status: " + (this.hookStressTestRunning ? "Running" : "Idle"));
ImGui.ProgressBar(this.hookStressTestCount / (float)this.hookStressTestMax, new System.Numerics.Vector2(0, 0), $"{this.hookStressTestCount}/{this.hookStressTestMax}");
}
@ -206,11 +207,6 @@ internal unsafe class HookWidget : IDataWindowWidget
this.addonFinalizeOriginal!(unitManager, atkUnitBase);
}
private void OnAddonUpdate(AtkUnitBase* thisPtr, float delta)
{
Log.Information("OnAddonUpdate");
}
private IDalamudHook HookMessageBoxW()
{
var hook = Hook<MessageBoxWDelegate>.FromSymbol(

View file

@ -8,6 +8,7 @@ using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Internal;
using Dalamud.Interface.Utility.Raii;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -62,6 +63,7 @@ public class IconBrowserWidget : IDataWindowWidget
// continue;
if (!texm.TryGetIconPath(new((uint)iconId), out var path))
continue;
result.Add((iconId, path));
}
@ -82,17 +84,17 @@ public class IconBrowserWidget : IDataWindowWidget
{
this.RecalculateIndexRange();
if (ImGui.BeginChild("ScrollableSection"u8, ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoMove))
using (var child = ImRaii.Child("ScrollableSection"u8, ImGui.GetContentRegionAvail(), false, ImGuiWindowFlags.NoMove))
{
var itemsPerRow = (int)MathF.Floor(
ImGui.GetContentRegionMax().X / (this.iconSize.X + ImGui.GetStyle().ItemSpacing.X));
var itemHeight = this.iconSize.Y + ImGui.GetStyle().ItemSpacing.Y;
if (child.Success)
{
var itemsPerRow = (int)MathF.Floor(ImGui.GetContentRegionMax().X / (this.iconSize.X + ImGui.GetStyle().ItemSpacing.X));
var itemHeight = this.iconSize.Y + ImGui.GetStyle().ItemSpacing.Y;
ImGuiClip.ClippedDraw(this.valueRange!, this.DrawIcon, itemsPerRow, itemHeight);
ImGuiClip.ClippedDraw(this.valueRange!, this.DrawIcon, itemsPerRow, itemHeight);
}
}
ImGui.EndChild();
this.ProcessMouseDragging();
}
}
@ -118,16 +120,16 @@ public class IconBrowserWidget : IDataWindowWidget
{
ImGui.Columns(2);
ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputInt("##StartRange"u8, ref this.startRange, 0, 0))
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputInt("##StartRange"u8, ref this.startRange))
{
this.startRange = Math.Clamp(this.startRange, 0, MaxIconId);
this.valueRange = null;
}
ImGui.NextColumn();
ImGui.PushItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputInt("##StopRange"u8, ref this.stopRange, 0, 0))
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputInt("##StopRange"u8, ref this.stopRange))
{
this.stopRange = Math.Clamp(this.stopRange, 0, MaxIconId);
this.valueRange = null;
@ -151,6 +153,10 @@ public class IconBrowserWidget : IDataWindowWidget
var texm = Service<TextureManager>.Get();
var cursor = ImGui.GetCursorScreenPos();
var white = ImGui.GetColorU32(ImGuiColors.DalamudWhite);
var red = ImGui.GetColorU32(ImGuiColors.DalamudRed);
var drawList = ImGui.GetWindowDrawList();
if (texm.Shared.GetFromGameIcon(iconId).TryGetWrap(out var texture, out var exc))
{
ImGui.Image(texture.Handle, this.iconSize);
@ -158,21 +164,17 @@ public class IconBrowserWidget : IDataWindowWidget
// If we have the option to show a tooltip image, draw the image, but make sure it's not too big.
if (ImGui.IsItemHovered() && this.showTooltipImage)
{
ImGui.BeginTooltip();
using var tooltip = ImRaii.Tooltip();
var scale = GetImageScaleFactor(texture);
var textSize = ImGui.CalcTextSize(iconId.ToString());
ImGui.SetCursorPosX(
texture.Size.X * scale / 2.0f - textSize.X / 2.0f + ImGui.GetStyle().FramePadding.X * 2.0f);
ImGui.SetCursorPosX((texture.Size.X * scale / 2.0f - (textSize.X / 2.0f)) + (ImGui.GetStyle().FramePadding.X * 2.0f));
ImGui.Text(iconId.ToString());
ImGui.Image(texture.Handle, texture.Size * scale);
ImGui.EndTooltip();
}
// else, just draw the iconId.
else if (ImGui.IsItemHovered())
else if (ImGui.IsItemHovered()) // else, just draw the iconId.
{
ImGui.SetTooltip(iconId.ToString());
}
@ -185,10 +187,7 @@ public class IconBrowserWidget : IDataWindowWidget
Task.FromResult(texture.CreateWrapSharingLowLevelResource()));
}
ImGui.GetWindowDrawList().AddRect(
cursor,
cursor + this.iconSize,
ImGui.GetColorU32(ImGuiColors.DalamudWhite));
drawList.AddRect(cursor, cursor + this.iconSize, white);
}
else if (exc is not null)
{
@ -197,19 +196,13 @@ public class IconBrowserWidget : IDataWindowWidget
{
var iconText = FontAwesomeIcon.Ban.ToIconString();
var textSize = ImGui.CalcTextSize(iconText);
ImGui.GetWindowDrawList().AddText(
cursor + ((this.iconSize - textSize) / 2),
ImGui.GetColorU32(ImGuiColors.DalamudRed),
iconText);
drawList.AddText(cursor + ((this.iconSize - textSize) / 2), red, iconText);
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip($"{iconId}\n{exc}");
ImGui.GetWindowDrawList().AddRect(
cursor,
cursor + this.iconSize,
ImGui.GetColorU32(ImGuiColors.DalamudRed));
drawList.AddRect(cursor, cursor + this.iconSize, red);
}
else
{
@ -218,18 +211,12 @@ public class IconBrowserWidget : IDataWindowWidget
ImGui.Dummy(this.iconSize);
var textSize = ImGui.CalcTextSize(text);
ImGui.GetWindowDrawList().AddText(
cursor + ((this.iconSize - textSize) / 2),
color,
text);
drawList.AddText(cursor + ((this.iconSize - textSize) / 2), color, text);
if (ImGui.IsItemHovered())
ImGui.SetTooltip(iconId.ToString());
ImGui.GetWindowDrawList().AddRect(
cursor,
cursor + this.iconSize,
color);
drawList.AddRect(cursor, cursor + this.iconSize, color);
}
}

View file

@ -54,8 +54,7 @@ internal class ImGuiWidget : IDataWindowWidget
ImGui.Separator();
ImGui.Text(
$"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
ImGui.Text($"WindowSystem.TimeSinceLastAnyFocus: {WindowSystem.TimeSinceLastAnyFocus.TotalMilliseconds:0}ms");
ImGui.Separator();
@ -79,45 +78,24 @@ internal class ImGuiWidget : IDataWindowWidget
switch (this.notificationTemplate.IconInt)
{
case 1 or 2:
ImGui.InputText(
"Icon Text##iconText"u8,
ref this.notificationTemplate.IconText,
255);
ImGui.InputText("Icon Text##iconText"u8, ref this.notificationTemplate.IconText, 255);
break;
case 5 or 6:
ImGui.Combo(
"Asset##iconAssetCombo",
ref this.notificationTemplate.IconAssetInt,
NotificationTemplate.AssetSources);
ImGui.Combo("Asset##iconAssetCombo", ref this.notificationTemplate.IconAssetInt, NotificationTemplate.AssetSources);
break;
case 3 or 7:
ImGui.InputText(
"Game Path##iconText"u8,
ref this.notificationTemplate.IconText,
255);
ImGui.InputText("Game Path##iconText"u8, ref this.notificationTemplate.IconText, 255);
break;
case 4 or 8:
ImGui.InputText(
"File Path##iconText"u8,
ref this.notificationTemplate.IconText,
255);
ImGui.InputText("File Path##iconText"u8, ref this.notificationTemplate.IconText, 255);
break;
}
ImGui.Combo(
"Initial Duration",
ref this.notificationTemplate.InitialDurationInt,
NotificationTemplate.InitialDurationTitles);
ImGui.Combo("Initial Duration", ref this.notificationTemplate.InitialDurationInt, NotificationTemplate.InitialDurationTitles);
ImGui.Combo(
"Extension Duration",
ref this.notificationTemplate.HoverExtendDurationInt,
NotificationTemplate.HoverExtendDurationTitles);
ImGui.Combo("Extension Duration", ref this.notificationTemplate.HoverExtendDurationInt, NotificationTemplate.HoverExtendDurationTitles);
ImGui.Combo(
"Progress",
ref this.notificationTemplate.ProgressMode,
NotificationTemplate.ProgressModeTitles);
ImGui.Combo("Progress", ref this.notificationTemplate.ProgressMode, NotificationTemplate.ProgressModeTitles);
ImGui.Checkbox("Respect UI Hidden"u8, ref this.notificationTemplate.RespectUiHidden);
@ -127,14 +105,11 @@ internal class ImGuiWidget : IDataWindowWidget
ImGui.Checkbox("User Dismissable"u8, ref this.notificationTemplate.UserDismissable);
ImGui.Checkbox(
"Action Bar (always on if not user dismissable for the example)"u8,
ref this.notificationTemplate.ActionBar);
ImGui.Checkbox("Action Bar (always on if not user dismissable for the example)"u8, ref this.notificationTemplate.ActionBar);
if (ImGui.Button("Add notification"u8))
{
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.";
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.";
NewRandom(out var title, out var type, out var progress);
if (this.notificationTemplate.ManualTitle)

View file

@ -22,6 +22,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class InventoryWidget : IDataWindowWidget
{
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders |
ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings;
private const ImGuiTableFlags InnerTableFlags = ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings;
private DataManager dataManager;
private TextureManager textureManager;
private GameInventoryType? selectedInventoryType = GameInventoryType.Inventory1;
@ -64,7 +69,7 @@ internal class InventoryWidget : IDataWindowWidget
private unsafe void DrawInventoryTypeList()
{
using var table = ImRaii.Table("InventoryTypeTable"u8, 2, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings, new Vector2(300, -1));
using var table = ImRaii.Table("InventoryTypeTable"u8, 2, TableFlags, new Vector2(300, -1));
if (!table) return;
ImGui.TableSetupColumn("Type"u8);
@ -107,7 +112,7 @@ internal class InventoryWidget : IDataWindowWidget
}
}
private unsafe void DrawInventoryType(GameInventoryType inventoryType)
private void DrawInventoryType(GameInventoryType inventoryType)
{
var items = GameInventoryItem.GetReadOnlySpanOfInventory(inventoryType);
if (items.IsEmpty)
@ -116,8 +121,9 @@ internal class InventoryWidget : IDataWindowWidget
return;
}
using var itemTable = ImRaii.Table("InventoryItemTable"u8, 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings);
using var itemTable = ImRaii.Table("InventoryItemTable"u8, 4, TableFlags);
if (!itemTable) return;
ImGui.TableSetupColumn("Slot"u8, ImGuiTableColumnFlags.WidthFixed, 40);
ImGui.TableSetupColumn("ItemId"u8, ImGuiTableColumnFlags.WidthFixed, 70);
ImGui.TableSetupColumn("Quantity"u8, ImGuiTableColumnFlags.WidthFixed, 70);
@ -129,7 +135,7 @@ internal class InventoryWidget : IDataWindowWidget
{
var item = items[slotIndex];
using var disableditem = ImRaii.Disabled(item.ItemId == 0);
using var disabledItem = ImRaii.Disabled(item.ItemId == 0);
ImGui.TableNextRow();
ImGui.TableNextColumn(); // Slot
@ -154,11 +160,11 @@ internal class InventoryWidget : IDataWindowWidget
if (ImGui.IsItemHovered())
{
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
ImGui.BeginTooltip();
using var tooltip = ImRaii.Tooltip();
ImGui.Text("Click to copy IconId"u8);
ImGui.Text($"ID: {iconId} Size: {texture.Width}x{texture.Height}");
ImGui.Image(texture.Handle, new(texture.Width, texture.Height));
ImGui.EndTooltip();
}
if (ImGui.IsItemClicked())
@ -169,7 +175,7 @@ internal class InventoryWidget : IDataWindowWidget
using var itemNameColor = ImRaii.PushColor(ImGuiCol.Text, this.GetItemRarityColor(item.ItemId));
using var node = ImRaii.TreeNode($"{itemName}###{inventoryType}_{slotIndex}", ImGuiTreeNodeFlags.SpanAvailWidth);
itemNameColor.Dispose();
itemNameColor.Pop();
using (var contextMenu = ImRaii.ContextPopupItem($"{inventoryType}_{slotIndex}_ContextMenu"))
{
@ -184,7 +190,7 @@ internal class InventoryWidget : IDataWindowWidget
if (!node) continue;
using var itemInfoTable = ImRaii.Table($"{inventoryType}_{slotIndex}_Table", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings);
using var itemInfoTable = ImRaii.Table($"{inventoryType}_{slotIndex}_Table", 2, InnerTableFlags);
if (!itemInfoTable) continue;
ImGui.TableSetupColumn("Name"u8, ImGuiTableColumnFlags.WidthFixed, 150);
@ -266,7 +272,7 @@ internal class InventoryWidget : IDataWindowWidget
ImGui.Text("Stains"u8);
ImGui.TableNextColumn();
using var stainTable = ImRaii.Table($"{inventoryType}_{slotIndex}_StainTable", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings);
using var stainTable = ImRaii.Table($"{inventoryType}_{slotIndex}_StainTable", 2, InnerTableFlags);
if (!stainTable) continue;
ImGui.TableSetupColumn("Stain Id"u8, ImGuiTableColumnFlags.WidthFixed, 80);
@ -287,7 +293,7 @@ internal class InventoryWidget : IDataWindowWidget
ImGui.Text("Materia"u8);
ImGui.TableNextColumn();
using var materiaTable = ImRaii.Table($"{inventoryType}_{slotIndex}_MateriaTable", 2, ImGuiTableFlags.BordersInner | ImGuiTableFlags.NoSavedSettings);
using var materiaTable = ImRaii.Table($"{inventoryType}_{slotIndex}_MateriaTable", 2, InnerTableFlags);
if (!materiaTable) continue;
ImGui.TableSetupColumn("Materia Id"u8, ImGuiTableColumnFlags.WidthFixed, 80);
@ -313,10 +319,12 @@ internal class InventoryWidget : IDataWindowWidget
private uint GetItemRarityColor(uint itemId, bool isEdgeColor = false)
{
if (ItemUtil.IsEventItem(itemId))
var normalized = ItemUtil.GetBaseId(itemId);
if (normalized.Kind == ItemKind.EventItem)
return isEdgeColor ? 0xFF000000 : 0xFFFFFFFF;
if (!this.dataManager.Excel.GetSheet<Item>().TryGetRow(ItemUtil.GetBaseId(itemId).ItemId, out var item))
if (!this.dataManager.Excel.GetSheet<Item>().TryGetRow(normalized.ItemId, out var item))
return isEdgeColor ? 0xFF000000 : 0xFFFFFFFF;
var rowId = ItemUtil.GetItemRarityColorType(item.RowId, isEdgeColor);
@ -327,18 +335,12 @@ internal class InventoryWidget : IDataWindowWidget
private uint GetItemIconId(uint itemId)
{
var normalized = ItemUtil.GetBaseId(itemId);
// EventItem
if (ItemUtil.IsEventItem(itemId))
if (normalized.Kind == ItemKind.EventItem)
return this.dataManager.Excel.GetSheet<EventItem>().TryGetRow(itemId, out var eventItem) ? eventItem.Icon : 0u;
// HighQuality
if (ItemUtil.IsHighQuality(itemId))
itemId -= 1_000_000;
// Collectible
if (ItemUtil.IsCollectible(itemId))
itemId -= 500_000;
return this.dataManager.Excel.GetSheet<Item>().TryGetRow(itemId, out var item) ? item.Icon : 0u;
return this.dataManager.Excel.GetSheet<Item>().TryGetRow(normalized.ItemId, out var item) ? item.Icon : 0u;
}
}

View file

@ -1,6 +1,7 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility.Raii;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -29,6 +30,7 @@ internal class KeyStateWidget : IDataWindowWidget
{
var keyState = Service<KeyState>.Get();
// TODO: Use table instead of columns
ImGui.Columns(4);
var i = 0;
@ -37,11 +39,10 @@ internal class KeyStateWidget : IDataWindowWidget
var code = (int)vkCode;
var value = keyState[code];
ImGui.PushStyleColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed);
ImGui.Text($"{vkCode} ({code})");
ImGui.PopStyleColor();
using (ImRaii.PushColor(ImGuiCol.Text, value ? ImGuiColors.HealerGreen : ImGuiColors.DPSRed))
{
ImGui.Text($"{vkCode} ({code})");
}
i++;
if (i % 24 == 0)

View file

@ -15,6 +15,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class MarketBoardWidget : IDataWindowWidget
{
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg;
private readonly ConcurrentQueue<(IMarketBoardHistory MarketBoardHistory, IMarketBoardHistoryListing Listing)> marketBoardHistoryQueue = new();
private readonly ConcurrentQueue<(IMarketBoardCurrentOfferings MarketBoardCurrentOfferings, IMarketBoardItemListing Listing)> marketBoardCurrentOfferingsQueue = new();
private readonly ConcurrentQueue<IMarketBoardPurchase> marketBoardPurchasesQueue = new();
@ -99,49 +101,47 @@ internal class MarketBoardWidget : IDataWindowWidget
this.marketBoardHistoryQueue.Clear();
}
using (var tabBar = ImRaii.TabBar("marketTabs"u8))
using var tabBar = ImRaii.TabBar("marketTabs"u8);
if (!tabBar.Success)
return;
using (var tabItem = ImRaii.TabItem("History"u8))
{
if (tabBar)
if (tabItem)
{
using (var tabItem = ImRaii.TabItem("History"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable(string.Empty, this.marketBoardHistoryQueue, this.DrawMarketBoardHistory, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity", "Is HQ?", "Sale Price", "Buyer Name", "Purchase Time");
}
}
ImGuiTable.DrawTable("history-table", this.marketBoardHistoryQueue, this.DrawMarketBoardHistory, TableFlags, "Item ID", "Quantity", "Is HQ?", "Sale Price", "Buyer Name", "Purchase Time");
}
}
using (var tabItem = ImRaii.TabItem("Offerings"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable(string.Empty, this.marketBoardCurrentOfferingsQueue, this.DrawMarketBoardCurrentOfferings, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity", "Is HQ?", "Price Per Unit", "Retainer Name");
}
}
using (var tabItem = ImRaii.TabItem("Offerings"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable("offerings-table", this.marketBoardCurrentOfferingsQueue, this.DrawMarketBoardCurrentOfferings, TableFlags, "Item ID", "Quantity", "Is HQ?", "Price Per Unit", "Retainer Name");
}
}
using (var tabItem = ImRaii.TabItem("Purchases"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable(string.Empty, this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity");
}
}
using (var tabItem = ImRaii.TabItem("Purchases"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable("purchases-table", this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, TableFlags, "Item ID", "Quantity");
}
}
using (var tabItem = ImRaii.TabItem("Purchase Requests"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable(string.Empty, this.marketBoardPurchaseRequestsQueue, this.DrawMarketBoardPurchaseRequests, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Is HQ?", "Quantity", "Price Per Unit", "Total Tax", "City ID", "Listing ID", "Retainer ID");
}
}
using (var tabItem = ImRaii.TabItem("Purchase Requests"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable("requests-table", this.marketBoardPurchaseRequestsQueue, this.DrawMarketBoardPurchaseRequests, TableFlags, "Item ID", "Is HQ?", "Quantity", "Price Per Unit", "Total Tax", "City ID", "Listing ID", "Retainer ID");
}
}
using (var tabItem = ImRaii.TabItem("Taxes"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable(string.Empty, this.marketTaxRatesQueue, this.DrawMarketTaxRates, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Uldah", "Limsa Lominsa", "Gridania", "Ishgard", "Kugane", "Crystarium", "Sharlayan", "Tuliyollal", "Valid Until");
}
}
using (var tabItem = ImRaii.TabItem("Taxes"u8))
{
if (tabItem)
{
ImGuiTable.DrawTable("taxes-table", this.marketTaxRatesQueue, this.DrawMarketTaxRates, TableFlags, "Uldah", "Limsa Lominsa", "Gridania", "Ishgard", "Kugane", "Crystarium", "Sharlayan", "Tuliyollal", "Valid Until");
}
}
}

View file

@ -1,44 +1,51 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Network;
using Dalamud.Interface.Utility;
using Dalamud.Game;
using Dalamud.Hooking;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Memory;
using ImGuiTable = Dalamud.Interface.Utility.ImGuiTable;
using FFXIVClientStructs.FFXIV.Application.Network;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.Network;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget to display the current packets.
/// </summary>
internal class NetworkMonitorWidget : IDataWindowWidget
internal unsafe class NetworkMonitorWidget : IDataWindowWidget
{
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookDown;
private Hook<ZoneClientSendPacketDelegate>? hookUp;
private bool trackNetwork;
private int trackedPackets;
private Regex? trackedOpCodes;
private int trackedPackets = 20;
private ulong nextPacketIndex;
private string filterString = string.Empty;
private Regex? untrackedOpCodes;
private string negativeFilterString = string.Empty;
private bool filterRecording = true;
private bool autoScroll = true;
private bool autoScrollPending;
/// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary>
~NetworkMonitorWidget()
{
if (this.trackNetwork)
{
this.trackNetwork = false;
var network = Service<GameNetwork>.GetNullable();
if (network != null)
{
network.NetworkMessage -= this.OnNetworkMessage;
}
}
this.hookDown?.Dispose();
this.hookUp?.Dispose();
}
private delegate byte ZoneClientSendPacketDelegate(ZoneClient* thisPtr, nint packet, uint a3, uint a4, byte a5);
private enum NetworkMessageDirection
{
ZoneDown,
ZoneUp,
}
/// <inheritdoc/>
@ -53,31 +60,36 @@ internal class NetworkMonitorWidget : IDataWindowWidget
/// <inheritdoc/>
public void Load()
{
this.trackNetwork = false;
this.trackedPackets = 20;
this.trackedOpCodes = null;
this.filterString = string.Empty;
this.packets.Clear();
this.hookDown = Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(
(nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket,
this.OnReceivePacketDetour);
// TODO: switch to ZoneClient.SendPacket from CS
if (Service<TargetSigScanner>.Get().TryScanText("E8 ?? ?? ?? ?? 4C 8B 44 24 ?? E9", out var address))
this.hookUp = Hook<ZoneClientSendPacketDelegate>.FromAddress(address, this.SendPacketDetour);
this.Ready = true;
}
/// <inheritdoc/>
public void Draw()
{
var network = Service<GameNetwork>.Get();
if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork))
{
if (this.trackNetwork)
{
network.NetworkMessage += this.OnNetworkMessage;
this.nextPacketIndex = 0;
this.hookDown?.Enable();
this.hookUp?.Enable();
}
else
{
network.NetworkMessage -= this.OnNetworkMessage;
this.hookDown?.Disable();
this.hookUp?.Disable();
}
}
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2);
ImGui.SetNextItemWidth(-1);
if (ImGui.DragInt("Stored Number of Packets"u8, ref this.trackedPackets, 0.1f, 1, 512))
{
this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512);
@ -88,131 +100,200 @@ internal class NetworkMonitorWidget : IDataWindowWidget
this.packets.Clear();
}
this.DrawFilterInput();
this.DrawNegativeFilterInput();
ImGui.SameLine();
ImGui.Checkbox("Auto-Scroll"u8, ref this.autoScroll);
ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "OpCode", "Hex", "Target", "Source", "Data");
}
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - (ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight()) * 2);
ImGui.InputTextWithHint("##Filter"u8, "Filter OpCodes..."u8, ref this.filterString, 1024, ImGuiInputTextFlags.AutoSelectAll);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.Checkbox("##FilterRecording"u8, ref this.filterRecording);
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Apply filter to incoming packets.\nUncheck to record all packets and filter the table instead."u8);
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGuiComponents.HelpMarker("Enter OpCodes in a comma-separated list.\nRanges are supported. Exclude OpCodes with exclamation mark.\nExample: -400,!50-100,650,700-980,!941");
private void DrawNetworkPacket(NetworkPacketData data)
{
ImGui.TableNextColumn();
ImGui.Text(data.Direction.ToString());
using var table = ImRaii.Table("NetworkMonitorTableV2"u8, 6, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings);
if (!table) return;
ImGui.TableNextColumn();
ImGui.Text(data.OpCode.ToString());
ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 50);
ImGui.TableSetupColumn("Time"u8, ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableSetupColumn("Direction"u8, ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableSetupColumn("OpCode"u8, ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableSetupColumn("OpCode (Hex)"u8, ImGuiTableColumnFlags.WidthFixed, 100);
ImGui.TableSetupColumn("Target EntityId"u8, ImGuiTableColumnFlags.WidthStretch);
ImGui.TableSetupScrollFreeze(0, 1);
ImGui.TableHeadersRow();
ImGui.TableNextColumn();
ImGui.Text($"0x{data.OpCode:X4}");
var autoScrollDisabled = false;
ImGui.TableNextColumn();
ImGui.Text(data.TargetActorId > 0 ? $"0x{data.TargetActorId:X}" : string.Empty);
ImGui.TableNextColumn();
ImGui.Text(data.SourceActorId > 0 ? $"0x{data.SourceActorId:X}" : string.Empty);
ImGui.TableNextColumn();
if (data.Data.Count > 0)
foreach (var packet in this.packets)
{
ImGui.Text(string.Join(" ", data.Data.Select(b => b.ToString("X2"))));
if (!this.filterRecording && !this.IsFiltered(packet.OpCode))
continue;
ImGui.TableNextColumn();
ImGui.Text(packet.Index.ToString());
ImGui.TableNextColumn();
ImGui.Text(packet.Time.ToLongTimeString());
ImGui.TableNextColumn();
ImGui.Text(packet.Direction.ToString());
ImGui.TableNextColumn();
using (ImRaii.PushId(packet.Index.ToString()))
{
if (ImGui.SmallButton("X"))
{
if (!string.IsNullOrEmpty(this.filterString))
this.filterString += ",";
this.filterString += $"!{packet.OpCode}";
}
}
if (ImGui.IsItemHovered())
ImGui.SetTooltip("Filter OpCode"u8);
autoScrollDisabled |= ImGui.IsItemHovered();
ImGui.SameLine();
WidgetUtil.DrawCopyableText(packet.OpCode.ToString());
autoScrollDisabled |= ImGui.IsItemHovered();
ImGui.TableNextColumn();
WidgetUtil.DrawCopyableText($"0x{packet.OpCode:X3}");
autoScrollDisabled |= ImGui.IsItemHovered();
ImGui.TableNextColumn();
if (packet.TargetEntityId > 0)
{
WidgetUtil.DrawCopyableText($"{packet.TargetEntityId:X}");
var name = !string.IsNullOrEmpty(packet.TargetName)
? packet.TargetName
: GetTargetName(packet.TargetEntityId);
if (!string.IsNullOrEmpty(name))
{
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.Text($"({name})");
}
}
}
else
if (this.autoScroll && this.autoScrollPending && !autoScrollDisabled)
{
ImGui.Dummy(ImGui.GetContentRegionAvail() with { Y = 0 });
ImGui.SetScrollHereY();
this.autoScrollPending = false;
}
}
private void DrawFilterInput()
private static string GetTargetName(uint targetId)
{
var invalidRegEx = this.filterString.Length > 0 && this.trackedOpCodes == null;
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx);
using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx);
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (!ImGui.InputTextWithHint("##Filter"u8, "Regex Filter OpCodes..."u8, ref this.filterString, 1024))
{
if (targetId == PlayerState.Instance()->EntityId)
return "Local Player";
var cachedName = NameCache.Instance()->GetNameByEntityId(targetId);
if (cachedName.HasValue)
return cachedName.ToString();
var obj = GameObjectManager.Instance()->Objects.GetObjectByEntityId(targetId);
if (obj != null)
return obj->NameString;
return string.Empty;
}
private void OnReceivePacketDetour(PacketDispatcher* thisPtr, uint targetId, nint packet)
{
var opCode = *(ushort*)(packet + 2);
var targetName = GetTargetName(targetId);
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneDown, targetId, targetName));
this.hookDown.OriginalDisposeSafe(thisPtr, targetId, packet);
}
private byte SendPacketDetour(ZoneClient* thisPtr, nint packet, uint a3, uint a4, byte a5)
{
var opCode = *(ushort*)packet;
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneUp, 0, string.Empty));
return this.hookUp.OriginalDisposeSafe(thisPtr, packet, a3, a4, a5);
}
private void RecordPacket(NetworkPacketData packet)
{
if (this.filterRecording && !this.IsFiltered(packet.OpCode))
return;
this.packets.Enqueue(packet);
while (this.packets.Count > this.trackedPackets)
{
this.packets.TryDequeue(out _);
}
if (this.filterString.Length == 0)
{
this.trackedOpCodes = null;
}
else
{
try
{
this.trackedOpCodes = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
}
catch
{
this.trackedOpCodes = null;
}
}
this.autoScrollPending = true;
}
private void DrawNegativeFilterInput()
private bool IsFiltered(ushort opcode)
{
var invalidRegEx = this.negativeFilterString.Length > 0 && this.untrackedOpCodes == null;
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx);
using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx);
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (!ImGui.InputTextWithHint("##NegativeFilter"u8, "Regex Filter Against OpCodes..."u8, ref this.negativeFilterString, 1024))
{
return;
}
var filterString = this.filterString.Replace(" ", string.Empty);
if (this.negativeFilterString.Length == 0)
if (filterString.Length == 0)
return true;
try
{
this.untrackedOpCodes = null;
var offset = 0;
var included = false;
var hasInclude = false;
while (filterString.Length - offset > 0)
{
var remaining = filterString[offset..];
// find the end of the current entry
var entryEnd = remaining.IndexOf(',');
if (entryEnd == -1)
entryEnd = remaining.Length;
var entry = filterString[offset..(offset + entryEnd)];
var dash = entry.IndexOf('-');
var isExcluded = entry.StartsWith('!');
var startOffset = isExcluded ? 1 : 0;
var entryMatch = dash == -1
? ushort.Parse(entry[startOffset..]) == opcode
: ((dash - startOffset == 0 || opcode >= ushort.Parse(entry[startOffset..dash]))
&& (entry[(dash + 1)..].Length == 0 || opcode <= ushort.Parse(entry[(dash + 1)..])));
if (isExcluded)
{
if (entryMatch)
return false;
}
else
{
hasInclude = true;
included |= entryMatch;
}
if (entryEnd == filterString.Length)
break;
offset += entryEnd + 1;
}
return !hasInclude || included;
}
else
catch (Exception ex)
{
try
{
this.untrackedOpCodes = new Regex(this.negativeFilterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
}
catch
{
this.untrackedOpCodes = null;
}
Serilog.Log.Error(ex, "Invalid filter string");
return false;
}
}
private void OnNetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
{
if ((this.trackedOpCodes == null || this.trackedOpCodes.IsMatch(this.OpCodeToString(opCode)))
&& (this.untrackedOpCodes == null || !this.untrackedOpCodes.IsMatch(this.OpCodeToString(opCode))))
{
this.packets.Enqueue(new NetworkPacketData(this, opCode, direction, sourceActorId, targetActorId, dataPtr));
while (this.packets.Count > this.trackedPackets)
{
this.packets.TryDequeue(out _);
}
}
}
private int GetSizeFromOpCode(ushort opCode)
=> 0;
/// <remarks> Add known packet-name -> packet struct size associations here to copy the byte data for such packets. </remarks>>
private int GetSizeFromName(string name)
=> name switch
{
_ => 0,
};
/// <remarks> The filter should find opCodes by number (decimal and hex) and name, if existing. </remarks>
private string OpCodeToString(ushort opCode)
=> $"{opCode}\0{opCode:X}";
#pragma warning disable SA1313
private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId)
private readonly record struct NetworkPacketData(ulong Index, DateTime Time, ushort OpCode, NetworkMessageDirection Direction, uint TargetEntityId, string TargetName);
#pragma warning restore SA1313
{
public readonly IReadOnlyList<byte> Data = Array.Empty<byte>();
public NetworkPacketData(NetworkMonitorWidget widget, ushort opCode, NetworkMessageDirection direction, uint sourceActorId, uint targetActorId, nint dataPtr)
: this(opCode, direction, sourceActorId, targetActorId)
=> this.Data = MemoryHelper.Read<byte>(dataPtr, widget.GetSizeFromOpCode(opCode), false);
}
}

View file

@ -55,8 +55,8 @@ internal class NounProcessorWidget : IDataWindowWidget
private ClientLanguage[] languages = [];
private string[] languageNames = [];
private int selectedSheetNameIndex = 0;
private int selectedLanguageIndex = 0;
private int selectedSheetNameIndex;
private int selectedLanguageIndex;
private int rowId = 1;
private int amount = 1;
@ -84,7 +84,6 @@ internal class NounProcessorWidget : IDataWindowWidget
{
var nounProcessor = Service<NounProcessor>.Get();
var dataManager = Service<DataManager>.Get();
var clientState = Service<ClientState>.Get();
var sheetType = NounSheets.ElementAt(this.selectedSheetNameIndex);
var language = this.languages[this.selectedLanguageIndex];

View file

@ -14,6 +14,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class ObjectTableWidget : IDataWindowWidget
{
private const ImGuiWindowFlags CharacterWindowFlags = ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.AlwaysAutoResize |
ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoMove |
ImGuiWindowFlags.NoMouseInputs | ImGuiWindowFlags.NoDocking |
ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav;
private bool resolveGameData;
private bool drawCharacters;
private float maxCharaDrawDistance = 20.0f;
@ -49,74 +54,66 @@ internal class ObjectTableWidget : IDataWindowWidget
if (objectTable.LocalPlayer == null)
{
ImGui.Text("LocalPlayer null."u8);
return;
}
else if (clientState.IsPvPExcludingDen)
if (clientState.IsPvPExcludingDen)
{
ImGui.Text("Cannot access object table while in PvP."u8);
return;
}
else
stateString += $"ObjectTableLen: {objectTable.Length}\n";
stateString += $"LocalPlayerName: {playerState.CharacterName}\n";
stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n";
stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n";
stateString += $"LocalCID: {playerState.ContentId:X}\n";
stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n";
stateString += $"TerritoryType: {clientState.TerritoryType}\n\n";
ImGui.Text(stateString);
ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters);
ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f);
for (var i = 0; i < objectTable.Length; i++)
{
stateString += $"ObjectTableLen: {objectTable.Length}\n";
stateString += $"LocalPlayerName: {playerState.CharacterName}\n";
stateString += $"CurrentWorldName: {(this.resolveGameData ? playerState.CurrentWorld.ValueNullable?.Name : playerState.CurrentWorld.RowId.ToString())}\n";
stateString += $"HomeWorldName: {(this.resolveGameData ? playerState.HomeWorld.ValueNullable?.Name : playerState.HomeWorld.RowId.ToString())}\n";
stateString += $"LocalCID: {playerState.ContentId:X}\n";
stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n";
stateString += $"TerritoryType: {clientState.TerritoryType}\n\n";
var obj = objectTable[i];
ImGui.Text(stateString);
if (obj == null)
continue;
ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters);
ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f);
Util.PrintGameObject(obj, i.ToString(), this.resolveGameData);
for (var i = 0; i < objectTable.Length; i++)
if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords))
{
var obj = objectTable[i];
// 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:X}:{obj.GameObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}";
if (obj == null)
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;
Util.PrintGameObject(obj, i.ToString(), this.resolveGameData);
if (obj.YalmDistanceX > this.maxCharaDrawDistance)
continue;
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.GameObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}";
ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y));
ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f));
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();
}
if (ImGui.Begin($"Actor{i}##ActorWindow{i}", CharacterWindowFlags))
ImGui.Text(objectText);
ImGui.End();
}
}
}

View file

@ -33,9 +33,9 @@ internal class PartyListWidget : IDataWindowWidget
ImGui.Checkbox("Resolve GameData"u8, 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($"GroupManager: {partyList.GroupManagerAddress:X}");
ImGui.Text($"GroupList: {partyList.GroupListAddress:X}");
ImGui.Text($"AllianceList: {partyList.AllianceListAddress:X}");
ImGui.Text($"{partyList.Length} Members");
@ -48,7 +48,7 @@ internal class PartyListWidget : IDataWindowWidget
continue;
}
ImGui.Text($"[{i}] {member.Address.ToInt64():X} - {member.Name} - {member.GameObject?.GameObjectId}");
ImGui.Text($"[{i}] {member.Address:X} - {member.Name} - {member.GameObject?.GameObjectId ?? 0}");
if (this.resolveGameData)
{
var actor = member.GameObject;

View file

@ -38,6 +38,11 @@ internal class SeStringCreatorWidget : IDataWindowWidget
{
private const LinkMacroPayloadType DalamudLinkType = (LinkMacroPayloadType)Payload.EmbeddedInfoType.DalamudLink - 1;
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg |
ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings;
private static readonly string[] TextEntryTypeOptions = ["String", "Macro", "Fixed"];
private readonly Dictionary<MacroCode, string[]> expressionNames = new()
{
{ MacroCode.SetResetTime, ["Hour", "WeekDay"] },
@ -191,7 +196,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
if (contentWidth != this.lastContentWidth)
{
var originalWidth = this.lastContentWidth != 0 ? this.lastContentWidth : contentWidth;
this.inputsWidth = this.inputsWidth / originalWidth * contentWidth;
this.inputsWidth = (this.inputsWidth / originalWidth) * contentWidth;
this.lastContentWidth = contentWidth;
}
@ -258,7 +263,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
using var tab = ImRaii.TabItem("Global Parameters"u8);
if (!tab) return;
using var table = ImRaii.Table("GlobalParametersTable"u8, 5, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY | ImGuiTableFlags.NoSavedSettings);
using var table = ImRaii.Table("GlobalParametersTable"u8, 5, TableFlags);
if (!table) return;
ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, 40);
@ -541,18 +546,16 @@ internal class SeStringCreatorWidget : IDataWindowWidget
ImGui.SameLine();
ImGui.SetNextItemWidth(90 * ImGuiHelpers.GlobalScale);
using (var dropdown = ImRaii.Combo("##Language"u8, this.language.ToString() ?? "Language..."))
using var dropdown = ImRaii.Combo("##Language"u8, this.language.ToString() ?? "Language...");
if (dropdown)
{
if (dropdown)
var values = Enum.GetValues<ClientLanguage>().OrderBy(lang => lang.ToString());
foreach (var value in values)
{
var values = Enum.GetValues<ClientLanguage>().OrderBy((ClientLanguage lang) => lang.ToString());
foreach (var value in values)
if (ImGui.Selectable(Enum.GetName(value), value == this.language))
{
if (ImGui.Selectable(Enum.GetName(value), value == this.language))
{
this.language = value;
this.UpdateInputString();
}
this.language = value;
this.UpdateInputString();
}
}
}
@ -572,7 +575,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
try
{
var headerFile = dataManager.GameData.GetFile<ExcelHeaderFile>($"exd/{sheetName}.exh");
if (headerFile.Header.Variant != ExcelVariant.Default)
if (headerFile == null || headerFile.Header.Variant != ExcelVariant.Default)
return false;
var sheet = dataManager.Excel.GetSheet<RawRow>(Language.English, sheetName);
@ -668,11 +671,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget
catch (Exception e)
{
ImGui.Text(e.Message);
return;
}
}
private unsafe void DrawInputs()
private void DrawInputs()
{
using var child = ImRaii.Child("Inputs"u8, new Vector2(this.inputsWidth, -1));
if (!child) return;
@ -688,8 +690,6 @@ internal class SeStringCreatorWidget : IDataWindowWidget
var arrowUpButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowUp);
var arrowDownButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowDown);
var trashButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Trash);
var terminalButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Terminal);
var entryToRemove = -1;
var entryToMoveUp = -1;
@ -706,7 +706,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
ImGui.TableNextColumn(); // Type
var type = (int)entry.Type;
ImGui.SetNextItemWidth(-1);
if (ImGui.Combo($"##Type{i}", ref type, ["String", "Macro", "Fixed"]))
if (ImGui.Combo($"##Type{i}", ref type, TextEntryTypeOptions))
{
entry.Type = (TextEntryType)type;
updateString |= true;
@ -798,7 +798,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
}
}
private unsafe void UpdateInputString(bool resetLocalParameters = true)
private void UpdateInputString(bool resetLocalParameters = true)
{
using var rssb = new RentedSeStringBuilder();
@ -1022,14 +1022,14 @@ internal class SeStringCreatorWidget : IDataWindowWidget
if (macroCode is MacroCode.JaNoun or MacroCode.EnNoun or MacroCode.DeNoun or MacroCode.FrNoun && exprIdx == 1)
{
var language = macroCode switch
var macroLanguage = macroCode switch
{
MacroCode.JaNoun => ClientLanguage.Japanese,
MacroCode.DeNoun => ClientLanguage.German,
MacroCode.FrNoun => ClientLanguage.French,
_ => ClientLanguage.English,
};
var articleTypeEnumType = language switch
var articleTypeEnumType = macroLanguage switch
{
ClientLanguage.Japanese => typeof(JapaneseArticleType),
ClientLanguage.German => typeof(GermanArticleType),
@ -1208,12 +1208,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget
if (expressionType == (int)ExpressionType.LocalNumber)
{
parameters[index] = new SeStringParameter(0);
return;
}
else if (expressionType == (int)ExpressionType.LocalString)
{
parameters[index] = new SeStringParameter(string.Empty);
return;
}
}
}

View file

@ -66,11 +66,10 @@ internal class ServicesWidget : IDataWindowWidget
var margin = ImGui.CalcTextSize("W\nW\nW"u8);
var rowHeight = cellPad.Y * 3;
var width = ImGui.GetContentRegionAvail().X;
if (ImGui.BeginChild(
"dependency-graph"u8,
new(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y),
false,
ImGuiWindowFlags.HorizontalScrollbar))
var childSize = new Vector2(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y);
using var child = ImRaii.Child("dependency-graph"u8, childSize, false, ImGuiWindowFlags.HorizontalScrollbar);
if (child.Success)
{
const uint rectBaseBorderColor = 0xFFFFFFFF;
const uint rectHoverFillColor = 0xFF404040;
@ -118,10 +117,8 @@ internal class ServicesWidget : IDataWindowWidget
hoveredNode = node;
if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{
if (this.selectedNodes.Contains(node.Type))
if (!this.selectedNodes.Add(node.Type))
this.selectedNodes.Remove(node.Type);
else
this.selectedNodes.Add(node.Type);
}
}
@ -195,13 +192,11 @@ internal class ServicesWidget : IDataWindowWidget
ImGui.SetTooltip(node.BlockingReason);
ImGui.SetCursorPos((new Vector2(rc.X, rc.Y) - pos) + ((cellSize - textSize) / 2));
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
using var pushedStyle = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
ImGui.Text(node.DisplayedName);
ImGui.SameLine();
ImGui.PushStyleColor(ImGuiCol.Text, node.TypeSuffixColor);
using var pushedColor = ImRaii.PushColor(ImGuiCol.Text, node.TypeSuffixColor);
ImGui.Text(node.TypeSuffix);
ImGui.PopStyleVar();
ImGui.PopStyleColor();
}
}
@ -233,7 +228,6 @@ internal class ServicesWidget : IDataWindowWidget
ImGui.SetCursorPos(default);
ImGui.Dummy(new(maxRowWidth, this.dependencyNodes.Count * rowHeight));
ImGui.EndChild();
}
}

View file

@ -3,11 +3,11 @@ using System.Numerics;
using System.Text;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Data;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.ImGuiNotification.Internal;
using Dalamud.Interface.ImGuiSeStringRenderer.Internal;
using Dalamud.Interface.Utility.Raii;
using Lumina.Excel.Sheets;

View file

@ -11,6 +11,7 @@ using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.StyleEditor;
using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility;
@ -461,7 +462,7 @@ public abstract class Window
ImGuiHelpers.ForceNextWindowMainViewport();
var wasFocused = this.IsFocused;
if (wasFocused)
if (wasFocused && this is not StyleEditorWindow)
{
var style = ImGui.GetStyle();
var focusedHeaderColor = style.Colors[(int)ImGuiCol.TitleBgActive];
@ -616,7 +617,7 @@ public abstract class Window
this.DrawTitleBarButtons();
}
if (wasFocused)
if (wasFocused && this is not StyleEditorWindow)
{
ImGui.PopStyleColor();
}

View file

@ -1,27 +0,0 @@
using Dalamud.Game.Network;
namespace Dalamud.Plugin.Services;
/// <summary>
/// This class handles interacting with game network events.
/// </summary>
[Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)]
public interface IGameNetwork : IDalamudService
{
// TODO(v9): we shouldn't be passing pointers to the actual data here
/// <summary>
/// The delegate type of a network message event.
/// </summary>
/// <param name="dataPtr">The pointer to the raw data.</param>
/// <param name="opCode">The operation ID code.</param>
/// <param name="sourceActorId">The source actor ID.</param>
/// <param name="targetActorId">The taret actor ID.</param>
/// <param name="direction">The direction of the packed.</param>
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
/// <summary>
/// Event that is called when a network message is sent/received.
/// </summary>
public event OnNetworkMessageDelegate NetworkMessage;
}

View file

@ -322,7 +322,7 @@ public interface ITextureProvider : IDalamudService
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <returns>Address of the new <see cref="FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture"/>.</returns>
/// <example>See <c>PrintTextureInfo</c> in <see cref="Interface.Internal.UiDebug.PrintSimpleNode"/> for an example
/// <example>See <c>PrintTextureInfo</c> in <see cref="Interface.Internal.UiDebug.Browsing.ImageNodeTre"/> for an example
/// of replacing the texture of an image node.</example>
/// <remarks>
/// <para>If the returned kernel texture is to be destroyed, call the fourth function in its vtable, by calling

View file

@ -205,6 +205,13 @@ public interface IUnlockState : IDalamudService
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
bool IsItemUnlocked(Item row);
/// <summary>
/// Determines whether the specified Leve is completed.
/// </summary>
/// <param name="row">The Leve row to check.</param>
/// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns>
bool IsLeveCompleted(Leve row);
/// <summary>
/// Determines whether the specified McGuffin is unlocked.
/// </summary>
@ -268,6 +275,13 @@ public interface IUnlockState : IDalamudService
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
bool IsPublicContentUnlocked(PublicContent row);
/// <summary>
/// Determines whether the specified Quest is completed.
/// </summary>
/// <param name="row">The Quest row to check.</param>
/// <returns><see langword="true"/> if completed; otherwise, <see langword="false"/>.</returns>
bool IsQuestCompleted(Quest row);
/// <summary>
/// Determines whether the specified Recipe is unlocked.
/// </summary>

View file

@ -41,8 +41,7 @@
<PackageVersion Include="HexaGen.Runtime" Version="1.1.20" />
<!-- Reloaded -->
<PackageVersion Include="goatcorp.Reloaded.Hooks" Version="4.2.0-goatcorp7" />
<PackageVersion Include="goatcorp.Reloaded.Assembler" Version="1.0.14-goatcorp5" />
<PackageVersion Include="goatcorp.Reloaded.Hooks" Version="4.2.0-goatcorp8" />
<PackageVersion Include="Reloaded.Memory" Version="7.0.0" />
<PackageVersion Include="Reloaded.Memory.Buffers" Version="2.0.0" />

@ -1 +1 @@
Subproject commit d83e0c13d3c802d4a483f373edcd129bc4802073
Subproject commit a02536a4bf6862036403c03945a02fcd6689e445