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.HasValue ? this.Struct->String.AsReadOnlySeString() : default, 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 { var 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(); }