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

View file

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

View file

@ -81,7 +81,7 @@ public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable<Agent
/// Focuses the AtkUnitBase. /// Focuses the AtkUnitBase.
/// </summary> /// </summary>
/// <returns> <c>true</c> when the addon was focused, <c>false</c> otherwise. </returns> /// <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> /// <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> /// <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; private bool disposing;
[ServiceManager.ServiceConstructor] [ServiceManager.ServiceConstructor]
private NetworkHandlers( private NetworkHandlers(TargetSigScanner sigScanner, HappyHttpClient happyHttpClient)
GameNetwork gameNetwork,
TargetSigScanner sigScanner,
HappyHttpClient happyHttpClient)
{ {
this.uploader = new UniversalisMarketBoardUploader(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/> /// <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/> /// <inheritdoc/>
@ -328,6 +331,15 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return this.IsUnlockLinkUnlocked(row.UnlockLink); return this.IsUnlockLinkUnlocked(row.UnlockLink);
} }
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row)
{
if (!this.IsLoaded)
return false;
return PlayerState.Instance()->IsMcGuffinUnlocked(row.RowId);
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsMountUnlocked(Mount row) public bool IsMountUnlocked(Mount row)
{ {
@ -376,9 +388,21 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
return UIState.IsPublicContentUnlocked(row.RowId); return UIState.IsPublicContentUnlocked(row.RowId);
} }
/// <inheritdoc/>
public bool IsQuestCompleted(Quest row)
{
if (!this.IsLoaded)
return false;
return QuestManager.IsQuestComplete(row.RowId);
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsRecipeUnlocked(Recipe row) public bool IsRecipeUnlocked(Recipe row)
{ {
if (!this.IsLoaded)
return false;
return this.recipeData.IsRecipeUnlocked(row); return this.recipeData.IsRecipeUnlocked(row);
} }
@ -509,6 +533,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (rowRef.TryGetValue<Item>(out var itemRow)) if (rowRef.TryGetValue<Item>(out var itemRow))
return this.IsItemUnlocked(itemRow); return this.IsItemUnlocked(itemRow);
if (rowRef.TryGetValue<Leve>(out var leveRow))
return this.IsLeveCompleted(leveRow);
if (rowRef.TryGetValue<MJILandmark>(out var mjiLandmarkRow)) if (rowRef.TryGetValue<MJILandmark>(out var mjiLandmarkRow))
return this.IsMJILandmarkUnlocked(mjiLandmarkRow); return this.IsMJILandmarkUnlocked(mjiLandmarkRow);
@ -536,6 +563,9 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (rowRef.TryGetValue<PublicContentSheet>(out var publicContentRow)) if (rowRef.TryGetValue<PublicContentSheet>(out var publicContentRow))
return this.IsPublicContentUnlocked(publicContentRow); return this.IsPublicContentUnlocked(publicContentRow);
if (rowRef.TryGetValue<Quest>(out var questRow))
return this.IsQuestCompleted(questRow);
if (rowRef.TryGetValue<Recipe>(out var recipeRow)) if (rowRef.TryGetValue<Recipe>(out var recipeRow))
return this.IsRecipeUnlocked(recipeRow); return this.IsRecipeUnlocked(recipeRow);
@ -596,6 +626,8 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
if (!this.IsLoaded) if (!this.IsLoaded)
return; return;
Log.Verbose("Checking for new unlocks...");
this.UpdateUnlocksForSheet<ActionSheet>(); this.UpdateUnlocksForSheet<ActionSheet>();
this.UpdateUnlocksForSheet<AetherCurrent>(); this.UpdateUnlocksForSheet<AetherCurrent>();
this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>(); this.UpdateUnlocksForSheet<AetherCurrentCompFlgSet>();
@ -629,6 +661,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
this.UpdateUnlocksForSheet<Ornament>(); this.UpdateUnlocksForSheet<Ornament>();
this.UpdateUnlocksForSheet<Perform>(); this.UpdateUnlocksForSheet<Perform>();
this.UpdateUnlocksForSheet<PublicContentSheet>(); this.UpdateUnlocksForSheet<PublicContentSheet>();
this.UpdateUnlocksForSheet<Quest>();
this.UpdateUnlocksForSheet<Recipe>(); this.UpdateUnlocksForSheet<Recipe>();
this.UpdateUnlocksForSheet<SecretRecipeBook>(); this.UpdateUnlocksForSheet<SecretRecipeBook>();
this.UpdateUnlocksForSheet<Trait>(); this.UpdateUnlocksForSheet<Trait>();
@ -637,6 +670,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
// Not implemented: // Not implemented:
// - DescriptionPage: quite complex // - DescriptionPage: quite complex
// - QuestAcceptAdditionCondition: ignored // - QuestAcceptAdditionCondition: ignored
// - Leve: AgentUpdateFlag.UnlocksUpdate is not set and the completed status can be unset again!
// For some other day: // For some other day:
// - FishingSpot // - FishingSpot
@ -676,7 +710,7 @@ internal unsafe class UnlockState : IInternalDisposableService, IUnlockState
unlockedRowIds.Add(row.RowId); 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)) 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); public bool IsItemUnlocked(Item row) => this.unlockStateService.IsItemUnlocked(row);
/// <inheritdoc/> /// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row); public bool IsLeveCompleted(Leve row) => this.unlockStateService.IsLeveCompleted(row);
/// <inheritdoc/> /// <inheritdoc/>
public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row); public bool IsMJILandmarkUnlocked(MJILandmark row) => this.unlockStateService.IsMJILandmarkUnlocked(row);
@ -804,6 +838,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
/// <inheritdoc/> /// <inheritdoc/>
public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row); public bool IsMKDLoreUnlocked(MKDLore row) => this.unlockStateService.IsMKDLoreUnlocked(row);
/// <inheritdoc/>
public bool IsMcGuffinUnlocked(McGuffin row) => this.unlockStateService.IsMcGuffinUnlocked(row);
/// <inheritdoc/> /// <inheritdoc/>
public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row); public bool IsMountUnlocked(Mount row) => this.unlockStateService.IsMountUnlocked(row);
@ -822,6 +859,9 @@ internal class UnlockStatePluginScoped : IInternalDisposableService, IUnlockStat
/// <inheritdoc/> /// <inheritdoc/>
public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row); public bool IsPublicContentUnlocked(PublicContentSheet row) => this.unlockStateService.IsPublicContentUnlocked(row);
/// <inheritdoc/>
public bool IsQuestCompleted(Quest row) => this.unlockStateService.IsQuestCompleted(row);
/// <inheritdoc/> /// <inheritdoc/>
public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row); public bool IsRecipeUnlocked(Recipe row) => this.unlockStateService.IsRecipeUnlocked(row);

View file

@ -1,8 +1,13 @@
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Logging.Internal; using Dalamud.Logging.Internal;
using InteropGenerator.Runtime;
namespace Dalamud.Hooking.Internal.Verification; namespace Dalamud.Hooking.Internal.Verification;
/// <summary> /// <summary>
@ -19,11 +24,13 @@ internal static class HookVerifier
new( new(
"ActorControlSelf", "ActorControlSelf",
"E8 ?? ?? ?? ?? 0F B7 0B 83 E9 64", "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) "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> /// <summary>
/// Initializes a new instance of the <see cref="HookVerifier"/> class. /// Initializes a new instance of the <see cref="HookVerifier"/> class.
@ -71,7 +78,7 @@ internal static class HookVerifier
var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!; var enforcedInvoke = entry.TargetDelegateType.GetMethod("Invoke")!;
// Compare Return Type // Compare Return Type
var mismatch = passedInvoke.ReturnType != enforcedInvoke.ReturnType; var mismatch = !CheckParam(passedInvoke.ReturnType, enforcedInvoke.ReturnType);
// Compare Parameter Count // Compare Parameter Count
var passedParams = passedInvoke.GetParameters(); var passedParams = passedInvoke.GetParameters();
@ -86,7 +93,7 @@ internal static class HookVerifier
// Compare Parameter Types // Compare Parameter Types
for (var i = 0; i < passedParams.Length; i++) 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; mismatch = true;
break; 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) private record VerificationEntry(string Name, string Signature, Type TargetDelegateType, string Message)
{ {
public nint Address { get; set; } 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 System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility; using Dalamud.Utility;
@ -9,7 +9,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType; using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="AddonTree"/> /// <inheritdoc cref="AddonTree"/>
public unsafe partial class AddonTree public unsafe partial class AddonTree

View file

@ -7,9 +7,9 @@ using FFXIVClientStructs.Attributes;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using static System.Reflection.BindingFlags; 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"/> /// <inheritdoc cref="AddonTree"/>
public unsafe partial class AddonTree public unsafe partial class AddonTree

View file

@ -8,12 +8,12 @@ using Dalamud.Interface.Components;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Interface.FontAwesomeIcon; using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.ElementSelector; using static Dalamud.Interface.Internal.UiDebug.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; using static Dalamud.Interface.Internal.UiDebug.UiDebug;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util; using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A class representing an <see cref="AtkUnitBase"/>, allowing it to be browsed within an ImGui window. /// 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.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// Class that prints the events table for a node, where applicable. /// 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; using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkClippingMaskNode"/> that can be printed and browsed via ImGui. /// 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; using static Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkCollisionNode"/> that can be printed and browsed via ImGui. /// 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 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 Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.ComponentType; using static FFXIVClientStructs.FFXIV.Component.GUI.ComponentType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkComponentNode"/> that can be printed and browsed via ImGui. /// 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 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 Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkCounterNode"/> that can be printed and browsed via ImGui. /// 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.Bindings.ImGui;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
@ -16,10 +16,10 @@ using static Dalamud.Bindings.ImGui.ImGuiTableColumnFlags;
using static Dalamud.Bindings.ImGui.ImGuiTableFlags; using static Dalamud.Bindings.ImGui.ImGuiTableFlags;
using static Dalamud.Interface.ColorHelpers; using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.FontAwesomeIcon; 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; using static Dalamud.Interface.Utility.ImGuiHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="ResNodeTree"/> /// <inheritdoc cref="ResNodeTree"/>
internal unsafe partial class 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.ImGuiTableFlags;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers; 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 Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.TextureType; using static FFXIVClientStructs.FFXIV.Component.GUI.TextureType;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkImageNode"/> that can be printed and browsed via ImGui. /// 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.Bindings.ImGui;
using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Internal.UiDebug.Utility;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
@ -9,7 +9,7 @@ using static Dalamud.Utility.Util;
using Vector2 = System.Numerics.Vector2; using Vector2 = System.Numerics.Vector2;
using Vector4 = System.Numerics.Vector4; using Vector4 = System.Numerics.Vector4;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkNineGridNode"/> that can be printed and browsed via ImGui. /// 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.Bindings.ImGui;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
@ -13,14 +13,14 @@ using static Dalamud.Bindings.ImGui.ImGuiCol;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers; using static Dalamud.Interface.ColorHelpers;
using static Dalamud.Interface.FontAwesomeIcon; using static Dalamud.Interface.FontAwesomeIcon;
using static Dalamud.Interface.Internal.UiDebug2.Browsing.Events; using static Dalamud.Interface.Internal.UiDebug.Browsing.Events;
using static Dalamud.Interface.Internal.UiDebug2.ElementSelector; using static Dalamud.Interface.Internal.UiDebug.ElementSelector;
using static Dalamud.Interface.Internal.UiDebug2.UiDebug2; using static Dalamud.Interface.Internal.UiDebug.UiDebug;
using static Dalamud.Interface.Internal.UiDebug2.Utility.Gui; using static Dalamud.Interface.Internal.UiDebug.Utility.Gui;
using static Dalamud.Utility.Util; using static Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkResNode"/> that can be printed and browsed via ImGui. /// 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 Lumina.Text.ReadOnly;
using static Dalamud.Interface.ColorHelpers; 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 Dalamud.Utility.Util;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A tree for an <see cref="AtkTextNode"/> that can be printed and browsed via ImGui. /// 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; using Dalamud.Bindings.ImGui;
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <inheritdoc cref="TimelineTree"/> /// <inheritdoc cref="TimelineTree"/>
public readonly partial struct 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.ImGuiTableFlags;
using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags; using static Dalamud.Bindings.ImGui.ImGuiTreeNodeFlags;
using static Dalamud.Interface.ColorHelpers; 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 Dalamud.Utility.Util;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeType;
// ReSharper disable SuggestBaseTypeForParameter // ReSharper disable SuggestBaseTypeForParameter
namespace Dalamud.Interface.Internal.UiDebug2.Browsing; namespace Dalamud.Interface.Internal.UiDebug.Browsing;
/// <summary> /// <summary>
/// A struct allowing a node's animation timeline to be printed and browsed. /// 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.Bindings.ImGui;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.UiDebug2.Browsing; using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Internal.UiDebug2.Utility; using Dalamud.Interface.Internal.UiDebug.Utility;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI; 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.ImGuiCol;
using static Dalamud.Bindings.ImGui.ImGuiWindowFlags; using static Dalamud.Bindings.ImGui.ImGuiWindowFlags;
using static Dalamud.Interface.FontAwesomeIcon; 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.UiBuilder;
using static Dalamud.Interface.Utility.ImGuiHelpers; using static Dalamud.Interface.Utility.ImGuiHelpers;
using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags; using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
@ -25,7 +25,7 @@ using static FFXIVClientStructs.FFXIV.Component.GUI.NodeFlags;
#pragma warning disable CS0659 #pragma warning disable CS0659
namespace Dalamud.Interface.Internal.UiDebug2; namespace Dalamud.Interface.Internal.UiDebug;
/// <summary> /// <summary>
/// A tool that enables the user to select UI elements within the inspector by mousing over them onscreen. /// 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 const int UnitListCount = 18;
private readonly UiDebug2 uiDebug2; private readonly UiDebug uiDebug;
private string addressSearchInput = string.Empty; private string addressSearchInput = string.Empty;
@ -43,10 +43,10 @@ internal unsafe class ElementSelector : IDisposable
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ElementSelector"/> class. /// Initializes a new instance of the <see cref="ElementSelector"/> class.
/// </summary> /// </summary>
/// <param name="uiDebug2">The instance of <see cref="UiDebug2"/> this Element Selector belongs to.</param> /// <param name="uiDebug">The instance of <see cref="UiDebug"/> this Element Selector belongs to.</param>
internal ElementSelector(UiDebug2 uiDebug2) internal ElementSelector(UiDebug uiDebug)
{ {
this.uiDebug2 = uiDebug2; this.uiDebug = uiDebug;
} }
/// <summary> /// <summary>
@ -181,7 +181,7 @@ internal unsafe class ElementSelector : IDisposable
{ {
this.Active = false; this.Active = false;
this.uiDebug2.SelectedAddonName = a.Addon->NameString; this.uiDebug.SelectedAddonName = a.Addon->NameString;
var ptrList = new List<nint> { (nint)n.Node }; var ptrList = new List<nint> { (nint)n.Node };
@ -420,7 +420,7 @@ internal unsafe class ElementSelector : IDisposable
var addon = unitManager->Entries[j].Value; var addon = unitManager->Entries[j].Value;
if ((nint)addon == address || FindByAddress(addon, address)) if ((nint)addon == address || FindByAddress(addon, address))
{ {
this.uiDebug2.SelectedAddonName = addon->NameString; this.uiDebug.SelectedAddonName = addon->NameString;
return; return;
} }
} }

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ using System.Collections.Generic;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using Dalamud.Interface.Internal.UiDebug2.Browsing; using Dalamud.Interface.Internal.UiDebug.Browsing;
using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal; using Dalamud.Logging.Internal;
@ -12,7 +12,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using static Dalamud.Bindings.ImGui.ImGuiWindowFlags; 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 // 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 // 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> /// <summary>
/// A tool for browsing the contents and structure of UI elements. /// A tool for browsing the contents and structure of UI elements.
/// </summary> /// </summary>
internal partial class UiDebug2 : IDisposable internal partial class UiDebug : IDisposable
{ {
/// <inheritdoc cref="ModuleLog"/> /// <inheritdoc cref="ModuleLog"/>
internal static readonly ModuleLog Log = ModuleLog.Create<UiDebug2>(); internal static readonly ModuleLog Log = ModuleLog.Create<UiDebug>();
private readonly ElementSelector elementSelector; private readonly ElementSelector elementSelector;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UiDebug2"/> class. /// Initializes a new instance of the <see cref="UiDebug"/> class.
/// </summary> /// </summary>
internal UiDebug2() internal UiDebug()
{ {
this.elementSelector = new(this); this.elementSelector = new(this);
} }

View file

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

View file

@ -11,7 +11,7 @@ using static System.MathF;
using static Dalamud.Interface.ColorHelpers; using static Dalamud.Interface.ColorHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Utility; namespace Dalamud.Interface.Internal.UiDebug.Utility;
/// <summary> /// <summary>
/// A struct representing the perimeter of an <see cref="AtkResNode"/>, accounting for all transformations. /// 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 = private readonly IDataWindowWidget[] modules =
[ [
new AddonInspectorWidget(), new AddonInspectorWidget(),
new AddonInspectorWidget2(),
new AddonLifecycleWidget(), new AddonLifecycleWidget(),
new AddonWidget(), new AddonWidget(),
new AddressesWidget(), new AddressesWidget(),

View file

@ -5,8 +5,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary> /// </summary>
internal class AddonInspectorWidget : IDataWindowWidget internal class AddonInspectorWidget : IDataWindowWidget
{ {
private UiDebug? addonInspector; private UiDebug.UiDebug? addonInspector;
/// <inheritdoc/> /// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["ai", "addoninspector"]; public string[]? CommandShortcuts { get; init; } = ["ai", "addoninspector"];
@ -19,7 +19,7 @@ internal class AddonInspectorWidget : IDataWindowWidget
/// <inheritdoc/> /// <inheritdoc/>
public void Load() public void Load()
{ {
this.addonInspector = new UiDebug(); this.addonInspector = new UiDebug.UiDebug();
if (this.addonInspector is not null) 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> /// <summary>
/// Widget for displaying Addon Data. /// Widget for displaying Addon Data.
/// </summary> /// </summary>
internal unsafe class AddonWidget : IDataWindowWidget internal class AddonWidget : IDataWindowWidget
{ {
private string inputAddonName = string.Empty; private string inputAddonName = string.Empty;
private int inputAddonIndex; private int inputAddonIndex;

View file

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

View file

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

View file

@ -25,10 +25,10 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
private int selectedExtendArray; private int selectedExtendArray;
private string searchTerm = string.Empty; private string searchTerm = string.Empty;
private bool hideUnsetStringArrayEntries = false; private bool hideUnsetStringArrayEntries;
private bool hideUnsetExtendArrayEntries = false; private bool hideUnsetExtendArrayEntries;
private bool showTextAddress = false; private bool showTextAddress;
private bool showMacroString = false; private bool showMacroString;
/// <inheritdoc/> /// <inheritdoc/>
public bool Ready { get; set; } public bool Ready { get; set; }
@ -155,17 +155,14 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
using var tooltip = ImRaii.Tooltip(); 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++) ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString);
{
if (array->SubscribedAddons[j] == 0)
continue;
ImGui.Text(raptureAtkUnitManager->GetAddonById(array->SubscribedAddons[j])->NameString);
}
} }
} }
} }
@ -244,9 +241,9 @@ internal unsafe class AtkArrayDataBrowserWidget : IDataWindowWidget
var atkArrayDataHolder = RaptureAtkModule.Instance()->AtkArrayDataHolder; 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.SetNextItemWidth(-1);
ImGui.InputTextWithHint("##TextSearch"u8, "Search..."u8, ref this.searchTerm, 256, ImGuiInputTextFlags.AutoSelectAll); 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(); var buddyList = Service<BuddyList>.Get();
ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData);
var companionBuddy = buddyList.CompanionBuddy;
if (companionBuddy == null)
{ {
var member = buddyList.CompanionBuddy; ImGui.Text("[Companion] null"u8);
if (member == null) }
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) if (this.resolveGameData)
{ {
var gameObject = member.GameObject; var gameObject = member?.GameObject;
if (gameObject == null) if (gameObject == null)
{ {
ImGui.Text("GameObject was null"u8); 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> /// </summary>
internal class CommandWidget : IDataWindowWidget internal class CommandWidget : IDataWindowWidget
{ {
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders |
ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Sortable |
ImGuiTableFlags.SortTristate;
/// <inheritdoc/> /// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["command"]; public string[]? CommandShortcuts { get; init; } = ["command"];
@ -31,9 +35,7 @@ internal class CommandWidget : IDataWindowWidget
{ {
var commandManager = Service<CommandManager>.Get(); var commandManager = Service<CommandManager>.Get();
var tableFlags = ImGuiTableFlags.ScrollY | ImGuiTableFlags.Borders | ImGuiTableFlags.SizingStretchProp | using var table = ImRaii.Table("CommandList"u8, 4, TableFlags);
ImGuiTableFlags.Sortable | ImGuiTableFlags.SortTristate;
using var table = ImRaii.Table("CommandList"u8, 4, tableFlags);
if (table) if (table)
{ {
ImGui.TableSetupScrollFreeze(0, 1); ImGui.TableSetupScrollFreeze(0, 1);

View file

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

View file

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

View file

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

View file

@ -3,6 +3,7 @@ using System.Numerics;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Game.Gui.FlyText; using Dalamud.Game.Gui.FlyText;
using Dalamud.Interface.Utility.Raii;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets; namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -39,18 +40,18 @@ internal class FlyTextWidget : IDataWindowWidget
/// <inheritdoc/> /// <inheritdoc/>
public void Draw() 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(); if (combo.Success)
foreach (var value in values)
{ {
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); 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;
using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility; using Dalamud.Utility;
using Serilog; using Serilog;
@ -248,7 +249,8 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{ {
ImGui.Text($"{gfs.SizePt}pt"); ImGui.Text($"{gfs.SizePt}pt");
ImGui.SameLine(offsetX); ImGui.SameLine(offsetX);
ImGui.PushTextWrapPos(this.useWordWrap ? 0f : -1f);
using var pushedWrap = ImRaii.TextWrapPos(this.useWordWrap ? 0f : -1f);
try try
{ {
if (handle.Value.LoadException is { } exc) if (handle.Value.LoadException is { } exc)
@ -263,6 +265,7 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{ {
if (!this.atlasScaleMode) if (!this.atlasScaleMode)
ImGui.SetWindowFontScale(1 / ImGuiHelpers.GlobalScale); ImGui.SetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
if (counter++ % 2 == 0) if (counter++ % 2 == 0)
{ {
using var pushPop = handle.Value.Push(); using var pushPop = handle.Value.Push();
@ -279,7 +282,6 @@ internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
finally finally
{ {
ImGui.SetWindowFontScale(1); ImGui.SetWindowFontScale(1);
ImGui.PopTextWrapPos();
} }
} }
} }

View file

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

View file

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

View file

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Interface.Utility.Raii;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using Serilog; using Serilog;
@ -26,12 +26,12 @@ internal unsafe class HookWidget : IDataWindowWidget
private Hook<MessageBoxWDelegate>? messageBoxMinHook; private Hook<MessageBoxWDelegate>? messageBoxMinHook;
private bool hookUseMinHook; private bool hookUseMinHook;
private int hookStressTestCount = 0; private int hookStressTestCount;
private int hookStressTestMax = 1000; private int hookStressTestMax = 1000;
private int hookStressTestWait = 100; private int hookStressTestWait = 100;
private int hookStressTestMaxDegreeOfParallelism = 10; private int hookStressTestMaxDegreeOfParallelism = 10;
private StressTestHookTarget hookStressTestHookTarget = StressTestHookTarget.Random; private StressTestHookTarget hookStressTestHookTarget = StressTestHookTarget.Random;
private bool hookStressTestRunning = false; private bool hookStressTestRunning;
private MessageBoxWDelegate? messageBoxWOriginal; private MessageBoxWDelegate? messageBoxWOriginal;
private AddonFinalizeDelegate? addonFinalizeOriginal; private AddonFinalizeDelegate? addonFinalizeOriginal;
@ -50,7 +50,7 @@ internal unsafe class HookWidget : IDataWindowWidget
{ {
MessageBoxW, MessageBoxW,
AddonFinalize, AddonFinalize,
Random, Random
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -106,67 +106,68 @@ internal unsafe class HookWidget : IDataWindowWidget
ImGui.Separator(); ImGui.Separator();
ImGui.BeginDisabled(this.hookStressTestRunning); using (ImRaii.Disabled(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)))
{ {
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)) if (combo.Success)
this.hookStressTestHookTarget = target; {
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))
}
if (ImGui.Button("Stress Test"u8))
{
Task.Run(() =>
{ {
this.hookStressTestRunning = true; Task.Run(() =>
this.hookStressTestCount = 0; {
Parallel.For( this.hookStressTestRunning = true;
0, this.hookStressTestCount = 0;
this.hookStressTestMax, Parallel.For(
new ParallelOptions 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)); Log.Information("Stress test completed");
this.hookStressTestCount++; }
Thread.Sleep(this.hookStressTestWait);
this.hookStressTestRunning = false;
this.hookStressTestList.ForEach(hook =>
{
hook.Dispose();
}); });
}).ContinueWith(t => this.hookStressTestList.Clear();
{
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(); }
});
} }
ImGui.EndDisabled();
ImGui.Text("Status: " + (this.hookStressTestRunning ? "Running" : "Idle")); ImGui.Text("Status: " + (this.hookStressTestRunning ? "Running" : "Idle"));
ImGui.ProgressBar(this.hookStressTestCount / (float)this.hookStressTestMax, new System.Numerics.Vector2(0, 0), $"{this.hookStressTestCount}/{this.hookStressTestMax}"); 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); this.addonFinalizeOriginal!(unitManager, atkUnitBase);
} }
private void OnAddonUpdate(AtkUnitBase* thisPtr, float delta)
{
Log.Information("OnAddonUpdate");
}
private IDalamudHook HookMessageBoxW() private IDalamudHook HookMessageBoxW()
{ {
var hook = Hook<MessageBoxWDelegate>.FromSymbol( var hook = Hook<MessageBoxWDelegate>.FromSymbol(

View file

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

View file

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

View file

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

View file

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

View file

@ -15,6 +15,8 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary> /// </summary>
internal class MarketBoardWidget : IDataWindowWidget internal class MarketBoardWidget : IDataWindowWidget
{ {
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg;
private readonly ConcurrentQueue<(IMarketBoardHistory MarketBoardHistory, IMarketBoardHistoryListing Listing)> marketBoardHistoryQueue = new(); private readonly ConcurrentQueue<(IMarketBoardHistory MarketBoardHistory, IMarketBoardHistoryListing Listing)> marketBoardHistoryQueue = new();
private readonly ConcurrentQueue<(IMarketBoardCurrentOfferings MarketBoardCurrentOfferings, IMarketBoardItemListing Listing)> marketBoardCurrentOfferingsQueue = new(); private readonly ConcurrentQueue<(IMarketBoardCurrentOfferings MarketBoardCurrentOfferings, IMarketBoardItemListing Listing)> marketBoardCurrentOfferingsQueue = new();
private readonly ConcurrentQueue<IMarketBoardPurchase> marketBoardPurchasesQueue = new(); private readonly ConcurrentQueue<IMarketBoardPurchase> marketBoardPurchasesQueue = new();
@ -99,49 +101,47 @@ internal class MarketBoardWidget : IDataWindowWidget
this.marketBoardHistoryQueue.Clear(); 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)) ImGuiTable.DrawTable("history-table", this.marketBoardHistoryQueue, this.DrawMarketBoardHistory, TableFlags, "Item ID", "Quantity", "Is HQ?", "Sale Price", "Buyer Name", "Purchase Time");
{ }
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");
}
}
using (var tabItem = ImRaii.TabItem("Offerings"u8)) using (var tabItem = ImRaii.TabItem("Offerings"u8))
{ {
if (tabItem) if (tabItem)
{ {
ImGuiTable.DrawTable(string.Empty, this.marketBoardCurrentOfferingsQueue, this.DrawMarketBoardCurrentOfferings, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity", "Is HQ?", "Price Per Unit", "Retainer Name"); 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)) using (var tabItem = ImRaii.TabItem("Purchases"u8))
{ {
if (tabItem) if (tabItem)
{ {
ImGuiTable.DrawTable(string.Empty, this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Item ID", "Quantity"); ImGuiTable.DrawTable("purchases-table", this.marketBoardPurchasesQueue, this.DrawMarketBoardPurchases, TableFlags, "Item ID", "Quantity");
} }
} }
using (var tabItem = ImRaii.TabItem("Purchase Requests"u8)) using (var tabItem = ImRaii.TabItem("Purchase Requests"u8))
{ {
if (tabItem) 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"); 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)) using (var tabItem = ImRaii.TabItem("Taxes"u8))
{ {
if (tabItem) 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"); 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.Concurrent;
using System.Collections.Generic; using System.Threading;
using System.Linq;
using System.Text.RegularExpressions;
using Dalamud.Bindings.ImGui; using Dalamud.Bindings.ImGui;
using Dalamud.Game.Network; using Dalamud.Game;
using Dalamud.Interface.Utility; using Dalamud.Hooking;
using Dalamud.Interface.Components;
using Dalamud.Interface.Utility.Raii; 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; namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary> /// <summary>
/// Widget to display the current packets. /// Widget to display the current packets.
/// </summary> /// </summary>
internal class NetworkMonitorWidget : IDataWindowWidget internal unsafe class NetworkMonitorWidget : IDataWindowWidget
{ {
private readonly ConcurrentQueue<NetworkPacketData> packets = new(); private readonly ConcurrentQueue<NetworkPacketData> packets = new();
private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookDown;
private Hook<ZoneClientSendPacketDelegate>? hookUp;
private bool trackNetwork; private bool trackNetwork;
private int trackedPackets; private int trackedPackets = 20;
private Regex? trackedOpCodes; private ulong nextPacketIndex;
private string filterString = string.Empty; private string filterString = string.Empty;
private Regex? untrackedOpCodes; private bool filterRecording = true;
private string negativeFilterString = string.Empty; private bool autoScroll = true;
private bool autoScrollPending;
/// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary> /// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary>
~NetworkMonitorWidget() ~NetworkMonitorWidget()
{ {
if (this.trackNetwork) this.hookDown?.Dispose();
{ this.hookUp?.Dispose();
this.trackNetwork = false; }
var network = Service<GameNetwork>.GetNullable();
if (network != null) private delegate byte ZoneClientSendPacketDelegate(ZoneClient* thisPtr, nint packet, uint a3, uint a4, byte a5);
{
network.NetworkMessage -= this.OnNetworkMessage; private enum NetworkMessageDirection
} {
} ZoneDown,
ZoneUp,
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -53,31 +60,36 @@ internal class NetworkMonitorWidget : IDataWindowWidget
/// <inheritdoc/> /// <inheritdoc/>
public void Load() public void Load()
{ {
this.trackNetwork = false; this.hookDown = Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(
this.trackedPackets = 20; (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket,
this.trackedOpCodes = null; this.OnReceivePacketDetour);
this.filterString = string.Empty;
this.packets.Clear(); // 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; this.Ready = true;
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Draw() public void Draw()
{ {
var network = Service<GameNetwork>.Get();
if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork)) if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork))
{ {
if (this.trackNetwork) if (this.trackNetwork)
{ {
network.NetworkMessage += this.OnNetworkMessage; this.nextPacketIndex = 0;
this.hookDown?.Enable();
this.hookUp?.Enable();
} }
else 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)) if (ImGui.DragInt("Stored Number of Packets"u8, ref this.trackedPackets, 0.1f, 1, 512))
{ {
this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512); this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512);
@ -88,131 +100,200 @@ internal class NetworkMonitorWidget : IDataWindowWidget
this.packets.Clear(); this.packets.Clear();
} }
this.DrawFilterInput(); ImGui.SameLine();
this.DrawNegativeFilterInput(); 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) using var table = ImRaii.Table("NetworkMonitorTableV2"u8, 6, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings);
{ if (!table) return;
ImGui.TableNextColumn();
ImGui.Text(data.Direction.ToString());
ImGui.TableNextColumn(); ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 50);
ImGui.Text(data.OpCode.ToString()); 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(); var autoScrollDisabled = false;
ImGui.Text($"0x{data.OpCode:X4}");
ImGui.TableNextColumn(); foreach (var packet in this.packets)
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)
{ {
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; if (targetId == PlayerState.Instance()->EntityId)
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx); return "Local Player";
using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx);
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); var cachedName = NameCache.Instance()->GetNameByEntityId(targetId);
if (!ImGui.InputTextWithHint("##Filter"u8, "Regex Filter OpCodes..."u8, ref this.filterString, 1024)) 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; return;
this.packets.Enqueue(packet);
while (this.packets.Count > this.trackedPackets)
{
this.packets.TryDequeue(out _);
} }
if (this.filterString.Length == 0) this.autoScrollPending = true;
{
this.trackedOpCodes = null;
}
else
{
try
{
this.trackedOpCodes = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
}
catch
{
this.trackedOpCodes = null;
}
}
} }
private void DrawNegativeFilterInput() private bool IsFiltered(ushort opcode)
{ {
var invalidRegEx = this.negativeFilterString.Length > 0 && this.untrackedOpCodes == null; var filterString = this.filterString.Replace(" ", string.Empty);
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;
}
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 Serilog.Log.Error(ex, "Invalid filter string");
{ return false;
this.untrackedOpCodes = new Regex(this.negativeFilterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
}
catch
{
this.untrackedOpCodes = null;
}
} }
} }
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 #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 #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 ClientLanguage[] languages = [];
private string[] languageNames = []; private string[] languageNames = [];
private int selectedSheetNameIndex = 0; private int selectedSheetNameIndex;
private int selectedLanguageIndex = 0; private int selectedLanguageIndex;
private int rowId = 1; private int rowId = 1;
private int amount = 1; private int amount = 1;
@ -84,7 +84,6 @@ internal class NounProcessorWidget : IDataWindowWidget
{ {
var nounProcessor = Service<NounProcessor>.Get(); var nounProcessor = Service<NounProcessor>.Get();
var dataManager = Service<DataManager>.Get(); var dataManager = Service<DataManager>.Get();
var clientState = Service<ClientState>.Get();
var sheetType = NounSheets.ElementAt(this.selectedSheetNameIndex); var sheetType = NounSheets.ElementAt(this.selectedSheetNameIndex);
var language = this.languages[this.selectedLanguageIndex]; var language = this.languages[this.selectedLanguageIndex];

View file

@ -14,6 +14,11 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary> /// </summary>
internal class ObjectTableWidget : IDataWindowWidget 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 resolveGameData;
private bool drawCharacters; private bool drawCharacters;
private float maxCharaDrawDistance = 20.0f; private float maxCharaDrawDistance = 20.0f;
@ -49,74 +54,66 @@ internal class ObjectTableWidget : IDataWindowWidget
if (objectTable.LocalPlayer == null) if (objectTable.LocalPlayer == null)
{ {
ImGui.Text("LocalPlayer null."u8); ImGui.Text("LocalPlayer null."u8);
return;
} }
else if (clientState.IsPvPExcludingDen)
if (clientState.IsPvPExcludingDen)
{ {
ImGui.Text("Cannot access object table while in PvP."u8); 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"; var obj = objectTable[i];
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); if (obj == null)
continue;
ImGui.Checkbox("Draw characters on screen"u8, ref this.drawCharacters); Util.PrintGameObject(obj, i.ToString(), this.resolveGameData);
ImGui.SliderFloat("Draw Distance"u8, ref this.maxCharaDrawDistance, 2f, 40f);
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; continue;
Util.PrintGameObject(obj, i.ToString(), this.resolveGameData); if (obj.YalmDistanceX > this.maxCharaDrawDistance)
continue;
if (this.drawCharacters && gameGui.WorldToScreen(obj.Position, out var screenCoords)) ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y));
{ ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f));
// 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}";
var screenPos = ImGui.GetMainViewport().Pos; if (ImGui.Begin($"Actor{i}##ActorWindow{i}", CharacterWindowFlags))
var screenSize = ImGui.GetMainViewport().Size; ImGui.Text(objectText);
ImGui.End();
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();
}
} }
} }
} }

View file

@ -33,9 +33,9 @@ internal class PartyListWidget : IDataWindowWidget
ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData); ImGui.Checkbox("Resolve GameData"u8, ref this.resolveGameData);
ImGui.Text($"GroupManager: {partyList.GroupManagerAddress.ToInt64():X}"); ImGui.Text($"GroupManager: {partyList.GroupManagerAddress:X}");
ImGui.Text($"GroupList: {partyList.GroupListAddress.ToInt64():X}"); ImGui.Text($"GroupList: {partyList.GroupListAddress:X}");
ImGui.Text($"AllianceList: {partyList.AllianceListAddress.ToInt64():X}"); ImGui.Text($"AllianceList: {partyList.AllianceListAddress:X}");
ImGui.Text($"{partyList.Length} Members"); ImGui.Text($"{partyList.Length} Members");
@ -48,7 +48,7 @@ internal class PartyListWidget : IDataWindowWidget
continue; 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) if (this.resolveGameData)
{ {
var actor = member.GameObject; 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 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() private readonly Dictionary<MacroCode, string[]> expressionNames = new()
{ {
{ MacroCode.SetResetTime, ["Hour", "WeekDay"] }, { MacroCode.SetResetTime, ["Hour", "WeekDay"] },
@ -191,7 +196,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
if (contentWidth != this.lastContentWidth) if (contentWidth != this.lastContentWidth)
{ {
var originalWidth = this.lastContentWidth != 0 ? this.lastContentWidth : contentWidth; var originalWidth = this.lastContentWidth != 0 ? this.lastContentWidth : contentWidth;
this.inputsWidth = this.inputsWidth / originalWidth * contentWidth; this.inputsWidth = (this.inputsWidth / originalWidth) * contentWidth;
this.lastContentWidth = contentWidth; this.lastContentWidth = contentWidth;
} }
@ -258,7 +263,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
using var tab = ImRaii.TabItem("Global Parameters"u8); using var tab = ImRaii.TabItem("Global Parameters"u8);
if (!tab) return; 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; if (!table) return;
ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, 40); ImGui.TableSetupColumn("Id"u8, ImGuiTableColumnFlags.WidthFixed, 40);
@ -541,18 +546,16 @@ internal class SeStringCreatorWidget : IDataWindowWidget
ImGui.SameLine(); ImGui.SameLine();
ImGui.SetNextItemWidth(90 * ImGuiHelpers.GlobalScale); 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()); if (ImGui.Selectable(Enum.GetName(value), value == this.language))
foreach (var value in values)
{ {
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 try
{ {
var headerFile = dataManager.GameData.GetFile<ExcelHeaderFile>($"exd/{sheetName}.exh"); 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; return false;
var sheet = dataManager.Excel.GetSheet<RawRow>(Language.English, sheetName); var sheet = dataManager.Excel.GetSheet<RawRow>(Language.English, sheetName);
@ -668,11 +671,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget
catch (Exception e) catch (Exception e)
{ {
ImGui.Text(e.Message); ImGui.Text(e.Message);
return;
} }
} }
private unsafe void DrawInputs() private void DrawInputs()
{ {
using var child = ImRaii.Child("Inputs"u8, new Vector2(this.inputsWidth, -1)); using var child = ImRaii.Child("Inputs"u8, new Vector2(this.inputsWidth, -1));
if (!child) return; if (!child) return;
@ -688,8 +690,6 @@ internal class SeStringCreatorWidget : IDataWindowWidget
var arrowUpButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowUp); var arrowUpButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowUp);
var arrowDownButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowDown); var arrowDownButtonSize = this.GetIconButtonSize(FontAwesomeIcon.ArrowDown);
var trashButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Trash);
var terminalButtonSize = this.GetIconButtonSize(FontAwesomeIcon.Terminal);
var entryToRemove = -1; var entryToRemove = -1;
var entryToMoveUp = -1; var entryToMoveUp = -1;
@ -706,7 +706,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
ImGui.TableNextColumn(); // Type ImGui.TableNextColumn(); // Type
var type = (int)entry.Type; var type = (int)entry.Type;
ImGui.SetNextItemWidth(-1); 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; entry.Type = (TextEntryType)type;
updateString |= true; 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(); 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) 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.JaNoun => ClientLanguage.Japanese,
MacroCode.DeNoun => ClientLanguage.German, MacroCode.DeNoun => ClientLanguage.German,
MacroCode.FrNoun => ClientLanguage.French, MacroCode.FrNoun => ClientLanguage.French,
_ => ClientLanguage.English, _ => ClientLanguage.English,
}; };
var articleTypeEnumType = language switch var articleTypeEnumType = macroLanguage switch
{ {
ClientLanguage.Japanese => typeof(JapaneseArticleType), ClientLanguage.Japanese => typeof(JapaneseArticleType),
ClientLanguage.German => typeof(GermanArticleType), ClientLanguage.German => typeof(GermanArticleType),
@ -1208,12 +1208,10 @@ internal class SeStringCreatorWidget : IDataWindowWidget
if (expressionType == (int)ExpressionType.LocalNumber) if (expressionType == (int)ExpressionType.LocalNumber)
{ {
parameters[index] = new SeStringParameter(0); parameters[index] = new SeStringParameter(0);
return;
} }
else if (expressionType == (int)ExpressionType.LocalString) else if (expressionType == (int)ExpressionType.LocalString)
{ {
parameters[index] = new SeStringParameter(string.Empty); 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 margin = ImGui.CalcTextSize("W\nW\nW"u8);
var rowHeight = cellPad.Y * 3; var rowHeight = cellPad.Y * 3;
var width = ImGui.GetContentRegionAvail().X; var width = ImGui.GetContentRegionAvail().X;
if (ImGui.BeginChild( var childSize = new Vector2(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y);
"dependency-graph"u8,
new(width, (this.dependencyNodes.Count * (rowHeight + margin.Y)) + cellPad.Y), using var child = ImRaii.Child("dependency-graph"u8, childSize, false, ImGuiWindowFlags.HorizontalScrollbar);
false, if (child.Success)
ImGuiWindowFlags.HorizontalScrollbar))
{ {
const uint rectBaseBorderColor = 0xFFFFFFFF; const uint rectBaseBorderColor = 0xFFFFFFFF;
const uint rectHoverFillColor = 0xFF404040; const uint rectHoverFillColor = 0xFF404040;
@ -118,10 +117,8 @@ internal class ServicesWidget : IDataWindowWidget
hoveredNode = node; hoveredNode = node;
if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)) if (ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{ {
if (this.selectedNodes.Contains(node.Type)) if (!this.selectedNodes.Add(node.Type))
this.selectedNodes.Remove(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.SetTooltip(node.BlockingReason);
ImGui.SetCursorPos((new Vector2(rc.X, rc.Y) - pos) + ((cellSize - textSize) / 2)); 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.Text(node.DisplayedName);
ImGui.SameLine(); ImGui.SameLine();
ImGui.PushStyleColor(ImGuiCol.Text, node.TypeSuffixColor); using var pushedColor = ImRaii.PushColor(ImGuiCol.Text, node.TypeSuffixColor);
ImGui.Text(node.TypeSuffix); ImGui.Text(node.TypeSuffix);
ImGui.PopStyleVar();
ImGui.PopStyleColor();
} }
} }
@ -233,7 +228,6 @@ internal class ServicesWidget : IDataWindowWidget
ImGui.SetCursorPos(default); ImGui.SetCursorPos(default);
ImGui.Dummy(new(maxRowWidth, this.dependencyNodes.Count * rowHeight)); ImGui.Dummy(new(maxRowWidth, this.dependencyNodes.Count * rowHeight));
ImGui.EndChild();
} }
} }

View file

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

View file

@ -11,6 +11,7 @@ using Dalamud.Game.ClientState.Keys;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Components; using Dalamud.Interface.Components;
using Dalamud.Interface.Internal; using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.StyleEditor;
using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.Internal;
using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Textures.TextureWraps;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
@ -461,7 +462,7 @@ public abstract class Window
ImGuiHelpers.ForceNextWindowMainViewport(); ImGuiHelpers.ForceNextWindowMainViewport();
var wasFocused = this.IsFocused; var wasFocused = this.IsFocused;
if (wasFocused) if (wasFocused && this is not StyleEditorWindow)
{ {
var style = ImGui.GetStyle(); var style = ImGui.GetStyle();
var focusedHeaderColor = style.Colors[(int)ImGuiCol.TitleBgActive]; var focusedHeaderColor = style.Colors[(int)ImGuiCol.TitleBgActive];
@ -616,7 +617,7 @@ public abstract class Window
this.DrawTitleBarButtons(); this.DrawTitleBarButtons();
} }
if (wasFocused) if (wasFocused && this is not StyleEditorWindow)
{ {
ImGui.PopStyleColor(); 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 /// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param> /// <see cref="Task{TResult}"/> completes.</param>
/// <returns>Address of the new <see cref="FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture"/>.</returns> /// <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> /// of replacing the texture of an image node.</example>
/// <remarks> /// <remarks>
/// <para>If the returned kernel texture is to be destroyed, call the fourth function in its vtable, by calling /// <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> /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
bool IsItemUnlocked(Item row); 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> /// <summary>
/// Determines whether the specified McGuffin is unlocked. /// Determines whether the specified McGuffin is unlocked.
/// </summary> /// </summary>
@ -268,6 +275,13 @@ public interface IUnlockState : IDalamudService
/// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns> /// <returns><see langword="true"/> if unlocked; otherwise, <see langword="false"/>.</returns>
bool IsPublicContentUnlocked(PublicContent row); 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> /// <summary>
/// Determines whether the specified Recipe is unlocked. /// Determines whether the specified Recipe is unlocked.
/// </summary> /// </summary>

View file

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

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