diff --git a/Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs b/Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs index 4bbbec57a..d4ebf4d3b 100644 --- a/Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs +++ b/Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs @@ -1,7 +1,9 @@ +using System.Collections.Generic; using System.Numerics; using System.Runtime.InteropServices; using FFXIVClientStructs.FFXIV.Component.GUI; +using FFXIVClientStructs.Interop; namespace Dalamud.Game.NativeWrapper; @@ -24,7 +26,7 @@ public readonly unsafe struct AtkUnitBasePtr(nint address) : IEquatable this.Address == 0; /// - /// Gets a value indicating whether the OnSetup function of the AtkUnitBase has been called. + /// Gets a value indicating whether the OnSetup function has been called. /// public readonly bool IsReady => !this.IsNull && this.Struct->IsReady; @@ -34,75 +36,103 @@ public readonly unsafe struct AtkUnitBasePtr(nint address) : IEquatable !this.IsNull && this.Struct->IsVisible; /// - /// Gets the name of the AtkUnitBase. + /// Gets the name. /// public readonly string Name => this.IsNull ? string.Empty : this.Struct->NameString; /// - /// Gets the id of the AtkUnitBase. + /// Gets the id. /// public readonly ushort Id => this.IsNull ? (ushort)0 : this.Struct->Id; /// - /// Gets the parent id of the AtkUnitBase. + /// Gets the parent id. /// public readonly ushort ParentId => this.IsNull ? (ushort)0 : this.Struct->ParentId; /// - /// Gets the host id of the AtkUnitBase. + /// Gets the host id. /// public readonly ushort HostId => this.IsNull ? (ushort)0 : this.Struct->HostId; /// - /// Gets the scale of the AtkUnitBase. + /// Gets the scale. /// public readonly float Scale => this.IsNull ? 0f : this.Struct->Scale; /// - /// Gets the x-position of the AtkUnitBase. + /// Gets the x-position. /// public readonly short X => this.IsNull ? (short)0 : this.Struct->X; /// - /// Gets the y-position of the AtkUnitBase. + /// Gets the y-position. /// public readonly short Y => this.IsNull ? (short)0 : this.Struct->Y; /// - /// Gets the width of the AtkUnitBase. + /// Gets the width. /// public readonly float Width => this.IsNull ? 0f : this.Struct->GetScaledWidth(false); /// - /// Gets the height of the AtkUnitBase. + /// Gets the height. /// public readonly float Height => this.IsNull ? 0f : this.Struct->GetScaledHeight(false); /// - /// Gets the scaled width of the AtkUnitBase. + /// Gets the scaled width. /// public readonly float ScaledWidth => this.IsNull ? 0f : this.Struct->GetScaledWidth(true); /// - /// Gets the scaled height of the AtkUnitBase. + /// Gets the scaled height. /// public readonly float ScaledHeight => this.IsNull ? 0f : this.Struct->GetScaledHeight(true); /// - /// Gets the position of the AtkUnitBase. + /// Gets the position. /// public readonly Vector2 Position => new(this.X, this.Y); /// - /// Gets the size of the AtkUnitBase. + /// Gets the size. /// public readonly Vector2 Size => new(this.Width, this.Height); /// - /// Gets the scaled size of the AtkUnitBase. + /// Gets the scaled size. /// public readonly Vector2 ScaledSize => new(this.ScaledWidth, this.ScaledHeight); + /// + /// Gets the number of entries. + /// + public readonly int AtkValuesCount => this.Struct->AtkValuesCount; + + /// + /// Gets an enumerable collection of of the addons current AtkValues. + /// + /// + /// An of corresponding to the addons AtkValues. + /// + public IEnumerable AtkValues + { + get + { + for (var i = 0; i < this.AtkValuesCount; i++) + { + AtkValuePtr ptr; + unsafe + { + ptr = new AtkValuePtr((nint)this.Struct->AtkValuesSpan.GetPointer(i)); + } + + yield return ptr; + } + } + } + /// /// Gets the AtkUnitBase*. /// diff --git a/Dalamud/Game/NativeWrapper/AtkValuePtr.cs b/Dalamud/Game/NativeWrapper/AtkValuePtr.cs new file mode 100644 index 000000000..005906e20 --- /dev/null +++ b/Dalamud/Game/NativeWrapper/AtkValuePtr.cs @@ -0,0 +1,113 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +using Dalamud.Utility; + +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace Dalamud.Game.NativeWrapper; + +/// +/// A readonly wrapper for AtkValue. +/// +/// The address to the AtkValue. +[StructLayout(LayoutKind.Explicit, Size = 0x08)] +public readonly unsafe struct AtkValuePtr(nint address) : IEquatable +{ + /// + /// The address to the AtkValue. + /// + [FieldOffset(0x00)] + public readonly nint Address = address; + + /// + /// Gets a value indicating whether the underlying pointer is a nullptr. + /// + public readonly bool IsNull => this.Address == 0; + + /// + /// Gets the value type. + /// + public readonly AtkValueType ValueType => (AtkValueType)this.Struct->Type; + + /// + /// Gets the AtkValue*. + /// + /// Internal use only. + internal readonly AtkValue* Struct => (AtkValue*)this.Address; + + public static implicit operator nint(AtkValuePtr wrapper) => wrapper.Address; + + public static implicit operator AtkValuePtr(nint address) => new(address); + + public static implicit operator AtkValuePtr(void* ptr) => new((nint)ptr); + + public static bool operator ==(AtkValuePtr left, AtkValuePtr right) => left.Address == right.Address; + + public static bool operator !=(AtkValuePtr left, AtkValuePtr right) => left.Address != right.Address; + + /// + /// Gets the value of the underlying as a boxed object, based on its . + /// + /// + /// The boxed value represented by this , or null if the value is null or undefined. + /// + /// + /// Thrown if the value type is not currently handled by this implementation. + /// + public unsafe object? GetValue() + { + if (this.Struct == null) + return null; + + return this.ValueType switch + { + AtkValueType.Undefined or AtkValueType.Null => null, + AtkValueType.Bool => this.Struct->Bool, + AtkValueType.Int => this.Struct->Int, + AtkValueType.Int64 => this.Struct->Int64, + AtkValueType.UInt => this.Struct->UInt, + AtkValueType.UInt64 => this.Struct->UInt64, + AtkValueType.Float => this.Struct->Float, + AtkValueType.String or AtkValueType.String8 or AtkValueType.ManagedString => this.Struct->String == null ? default : this.Struct->String.AsReadOnlySeString(), + AtkValueType.WideString => this.Struct->WideString == null ? string.Empty : new string(this.Struct->WideString), + AtkValueType.Pointer => (nint)this.Struct->Pointer, + _ => throw new NotImplementedException($"AtkValueType {this.ValueType} is currently not supported"), + }; + } + + /// + /// Attempts to retrieve the value as a strongly-typed object if the underlying type matches. + /// + /// The expected value type to extract. + /// + /// When this method returns true, contains the extracted value of type . + /// Otherwise, contains the default value of type . + /// + /// + /// true if the value was successfully extracted and matched ; otherwise, false. + /// + public unsafe bool TryGet([NotNullWhen(true)] out T? result) where T : struct + { + object? value = this.GetValue(); + if (value is T typed) + { + result = typed; + return true; + } + + result = default; + return false; + } + + /// Determines whether the specified AtkValuePtr is equal to the current AtkValuePtr. + /// The AtkValuePtr to compare with the current AtkValuePtr. + /// true if the specified AtkValuePtr is equal to the current AtkValuePtr; otherwise, false. + public readonly bool Equals(AtkValuePtr other) => this.Address == other.Address || this.Struct->EqualTo(other.Struct); + + /// + public override readonly bool Equals(object obj) => obj is AtkValuePtr wrapper && this.Equals(wrapper); + + /// + public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode(); +} diff --git a/Dalamud/Game/NativeWrapper/AtkValueType.cs b/Dalamud/Game/NativeWrapper/AtkValueType.cs new file mode 100644 index 000000000..ef169e102 --- /dev/null +++ b/Dalamud/Game/NativeWrapper/AtkValueType.cs @@ -0,0 +1,87 @@ +namespace Dalamud.Game.NativeWrapper; + +/// +/// Represents the data type of the AtkValue. +/// +public enum AtkValueType +{ + /// + /// The value is undefined or invalid. + /// + Undefined = 0, + + /// + /// The value is null. + /// + Null = 0x1, + + /// + /// The value is a boolean. + /// + Bool = 0x2, + + /// + /// The value is a 32-bit signed integer. + /// + Int = 0x3, + + /// + /// The value is a 64-bit signed integer. + /// + Int64 = 0x4, + + /// + /// The value is a 32-bit unsigned integer. + /// + UInt = 0x5, + + /// + /// The value is a 64-bit unsigned integer. + /// + UInt64 = 0x6, + + /// + /// The value is a 32-bit floating-point number. + /// + Float = 0x7, + + /// + /// The value points to a null-terminated 8-bit character string (ASCII or UTF-8). + /// + String = 0x8, + + /// + /// The value points to a null-terminated 16-bit character string (UTF-16 / wide string). + /// + WideString = 0x9, + + /// + /// The value points to a constant null-terminated 8-bit character string (const char*). + /// + String8 = 0xA, + + /// + /// The value is a vector. + /// + Vector = 0xB, + + /// + /// The value is a pointer. + /// + Pointer = 0xC, + + /// + /// The value is pointing to an array of AtkValue entries. + /// + AtkValues = 0xD, + + /// + /// The value is a managed string. See . + /// + ManagedString = 0x28, + + /// + /// The value is a managed vector. See . + /// + ManagedVector = 0x2B, +}