Implements Form foreign interface

This commit is contained in:
Deukhoofd 2022-09-21 19:40:04 +02:00
parent 8bb62aa260
commit c8be93f83f
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
10 changed files with 254 additions and 19 deletions

View File

@ -0,0 +1,57 @@
using System;
using System.Runtime.InteropServices;
using PkmnLibSharp.StaticData;
namespace PkmnLibSharp.FFI.StaticData
{
internal static class Form
{
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_new(IntPtr name, float height, float weight, uint baseExperience,
IntPtr types, ulong typesLength, IntPtr baseStats, IntPtr abilities, ulong abilitiesLength,
IntPtr hiddenAbilities, ulong hiddenAbilitiesLength, IntPtr learnableMoves, IntPtr flags,
ulong flagsLength);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern void form_drop(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_name(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern float form_height(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern float form_weight(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern uint form_base_experience(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong form_types_length(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern TypeIdentifier form_types_get(IntPtr ptr, ulong index);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_base_stats(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong form_abilities_length(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_abilities_get(IntPtr ptr, ulong index);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern ulong form_hidden_abilities_length(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_hidden_abilities_get(IntPtr ptr, ulong index);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr form_moves(IntPtr ptr);
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl)]
internal static extern byte form_has_flag(IntPtr ptr, IntPtr flag);
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>PkmnLibSharp</RootNamespace>
<LangVersion>8</LangVersion>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -0,0 +1,70 @@
using System.Collections.Generic;
using System.Linq;
using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.StaticData.Form;
namespace PkmnLibSharp.StaticData
{
public class Form : ExternPointer<Form.CacheData>
{
public class CacheData
{
public string? Name { get; internal set; }
public float? Height { get; internal set; }
public float? Weight { get; internal set; }
public uint? BaseExperience { get; internal set; }
public ulong? TypesLength { get; internal set; }
public CachedExternValueArray<TypeIdentifier>? Types { get; internal set; }
public StaticStatisticSet<ushort>? BaseStats { get; internal set; }
public CachedExternArray<string>? Abilities { get; internal set; }
public CachedExternArray<string>? HiddenAbilities { get; internal set; }
public LearnableMoves? LearnableMoves { get; internal set; }
}
public Form(string name, float height, float weight, uint baseExperience, TypeIdentifier[] types,
StaticStatisticSet<short> baseStats, IReadOnlyCollection<string> abilities,
IReadOnlyCollection<string> hiddenAbilities, LearnableMoves learnableMoves,
IReadOnlyCollection<string> flags)
{
var typesArr = types.ArrayPtr();
var abilitiesPtrArray = abilities.Select(x => x.ToPtr()).ToArray();
var hiddenAbilitiesPtrArray = hiddenAbilities.Select(x => x.ToPtr()).ToArray();
var flagsPtrArray = flags.Select(x => x.ToPtr()).ToArray();
var ptr = Interface.form_new(name.ToPtr(), height, weight, baseExperience, typesArr, (ulong)types.Length,
baseStats.TakeOwnershipAndInvalidate(), abilitiesPtrArray.ArrayPtr(), (ulong)abilities.Count,
hiddenAbilitiesPtrArray.ArrayPtr(), (ulong)hiddenAbilities.Count,
learnableMoves.TakeOwnershipAndInvalidate(), flagsPtrArray.ArrayPtr(), (ulong)flags.Count);
InitializePointer(ptr, true);
}
public string Name => Cache.Name ??= Interface.form_name(Ptr).PtrString()!;
public float Height => Cache.Height ??= Interface.form_height(Ptr);
public float Weight => Cache.Weight ??= Interface.form_weight(Ptr);
public uint BaseExperience => Cache.BaseExperience ??= Interface.form_base_experience(Ptr);
public IReadOnlyList<TypeIdentifier> Types =>
Cache.Types ??= new CachedExternValueArray<TypeIdentifier>(Interface.form_types_length(Ptr),
arg => Interface.form_types_get(Ptr, arg));
public StaticStatisticSet<ushort> BaseStats =>
Cache.BaseStats ??= new StaticStatisticSet<ushort>(Interface.form_base_stats(Ptr), false);
public IReadOnlyList<string> Abilities =>
Cache.Abilities ??= new CachedExternArray<string>(Interface.form_abilities_length(Ptr),
arg => Interface.form_abilities_get(Ptr, arg).PtrString()!);
public IReadOnlyList<string> HiddenAbilities =>
Cache.HiddenAbilities ??= new CachedExternArray<string>(Interface.form_hidden_abilities_length(Ptr),
arg => Interface.form_hidden_abilities_get(Ptr, arg).PtrString()!);
public LearnableMoves LearnableMoves =>
Cache.LearnableMoves ??= new LearnableMoves(Interface.form_moves(Ptr), false);
protected override CacheData CreateCache() => new CacheData();
protected override void Destructor() => Interface.form_drop(Ptr);
}
}

View File

@ -1,3 +1,4 @@
using System;
using PkmnLibSharp.Utils;
using Interface = PkmnLibSharp.FFI.StaticData.LearnableMoves;
using LevelInt = System.Byte;
@ -6,6 +7,10 @@ namespace PkmnLibSharp.StaticData
{
public class LearnableMoves : ExternPointer<object>
{
internal LearnableMoves(IntPtr ptr, bool isOwner) : base(ptr, isOwner)
{
}
public LearnableMoves() : base(Interface.learnable_moves_new(), true)
{
}

View File

@ -12,8 +12,7 @@ namespace PkmnLibSharp.StaticData
{
public float? Chance { get; internal set; }
public string? Name { get; internal set; }
public ulong? ParameterLength { get; internal set; }
public EffectParameter?[]? Parameters { get; internal set; }
public CachedExternArray<EffectParameter>? Parameters { get; internal set; }
}
internal SecondaryEffect(IntPtr ptr, bool isOwner) : base(ptr, isOwner)
@ -32,20 +31,10 @@ namespace PkmnLibSharp.StaticData
public float Chance => Cache.Chance ??= Interface.secondary_effect_chance(Ptr);
public string Name => Cache.Name ?? (Cache.Name = Interface.secondary_effect_effect_name(Ptr).PtrString()!);
public ulong ParameterLength =>
Cache.ParameterLength ?? (Cache.ParameterLength = Interface.secondary_effect_parameter_length(Ptr)).Value;
public EffectParameter GetParameter(int index)
{
Cache.Parameters ??= new EffectParameter[ParameterLength];
if (Cache.Parameters[index] == null)
{
var ptr = Interface.secondary_effect_parameter_get(Ptr, (ulong)index);
Cache.Parameters[index] = new EffectParameter(ptr, false);
}
return Cache.Parameters[index]!;
}
public IReadOnlyList<EffectParameter> Parameters =>
Cache.Parameters ??= new CachedExternArray<EffectParameter>(
Interface.secondary_effect_parameter_length(Ptr),
arg => new EffectParameter(Interface.secondary_effect_parameter_get(Ptr, arg), false));
protected override CacheData CreateCache()
{

View File

@ -17,6 +17,10 @@ namespace PkmnLibSharp.StaticData
public T? Speed { get; internal set; }
}
internal StaticStatisticSet(IntPtr ptr, bool isOwner) : base(ptr, isOwner)
{
}
public StaticStatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed)
{
var p = typeof(T) switch

View File

@ -8,7 +8,7 @@ namespace PkmnLibSharp.StaticData
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
[FieldOffset(0)] private readonly byte _identifier;
internal TypeIdentifier(byte b)
public TypeIdentifier(byte b)
{
_identifier = b;
}
@ -37,5 +37,10 @@ namespace PkmnLibSharp.StaticData
{
return !left.Equals(right);
}
public override string ToString()
{
return $"Type({_identifier})";
}
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace PkmnLibSharp.Utils
{
public class CachedExternArray<T> : IReadOnlyList<T>
where T: class
{
private readonly T?[] _array;
private readonly Func<ulong, T> _getItem;
public CachedExternArray(ulong size, Func<ulong, T> getItem)
{
_array = new T?[(int)size];
_getItem = getItem;
}
public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => _array.Length;
public T this[int index]
{
get
{
if (index >= _array.Length)
throw new ArgumentOutOfRangeException(
$"Index {index} was outside of the bounds of the external array with length {Count}");
return _array[index] ??= _getItem((ulong)index);
}
}
}
public class CachedExternValueArray<T> : IReadOnlyList<T>
where T: struct
{
private readonly T?[] _array;
private readonly Func<ulong, T> _getItem;
public CachedExternValueArray(ulong size, Func<ulong, T> getItem)
{
_array = new T?[(int)size];
_getItem = getItem;
}
public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < Count; i++)
{
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => _array.Length;
public T this[int index] => _array[index] ??= _getItem((ulong)index);
}
}

View File

@ -25,7 +25,7 @@ namespace PkmnLibSharp.Utils
{
return Marshal.UnsafeAddrOfPinnedArrayElement(a, 0);
}
internal static IntPtr ArrayPtr<T>(this T[] a) where T : struct, IConvertible
internal static IntPtr ArrayPtr<T>(this T[] a) where T : struct
{
return Marshal.UnsafeAddrOfPinnedArrayElement(a, 0);
}

View File

@ -0,0 +1,28 @@
using System;
using NUnit.Framework;
using PkmnLibSharp.StaticData;
namespace PkmnLibRSharpTests.StaticData
{
public class FormTests
{
[Test]
public void BasicTests()
{
using var form = new Form("foobar", 0.2f, 5.8f, 300, new TypeIdentifier[] { new(1), new(2) },
new StaticStatisticSet<short>(5, 10, 30, 20, 2, 0), new[] { "foo", "bar" }, new[] { "set" },
new LearnableMoves(), Array.Empty<string>());
Assert.AreEqual("foobar", form.Name);
Assert.AreEqual(0.2f, form.Height, 0.00001f);
Assert.AreEqual(5.8f, form.Weight, 0.00001f);
Assert.AreEqual(300, form.BaseExperience);
Assert.AreEqual(new TypeIdentifier(1), form.Types[0]);
Assert.AreEqual(new TypeIdentifier(2), form.Types[1]);
Assert.AreEqual(10, form.BaseStats.Attack);
Assert.AreEqual("foo", form.Abilities[0]);
Assert.AreEqual("bar", form.Abilities[1]);
Assert.AreEqual("set", form.HiddenAbilities[0]);
}
}
}