diff --git a/PKHeX.Core/Saves/SAV3E.cs b/PKHeX.Core/Saves/SAV3E.cs index ff134160d..42d1efcdb 100644 --- a/PKHeX.Core/Saves/SAV3E.cs +++ b/PKHeX.Core/Saves/SAV3E.cs @@ -150,18 +150,16 @@ public PokeBlock3Case PokeBlocks protected override int SeenOffset2 => 0x988; - public DecorationInventory3 Decorations - { - get => Large.Slice(0x2734, DecorationInventory3.SIZE).ToStructure(); - set => SetData(Large, value.ToBytes(), 0x2734); - } + public DecorationInventory3 Decorations => new(Large.AsSpan(0x2734, DecorationInventory3.SIZE)); public Swarm3 Swarm { - get => Large.Slice(0x2B90, Swarm3.SIZE).ToClass(); - set => SetData(Large, value.ToBytesClass(), 0x2B90); + get => new(Large.Slice(0x2B90, Swarm3.SIZE)); + set => SetData(Large, value.Data, 0x2B90); } + private void ClearSwarm() => Large.AsSpan(0x2B90, Swarm3.SIZE).Clear(); + public IReadOnlyList DefaultSwarms => Swarm3Details.Swarms_E; public int SwarmIndex @@ -170,7 +168,10 @@ public int SwarmIndex set { var arr = DefaultSwarms; - Swarm = (uint)value >= arr.Count ? new Swarm3() : arr[value]; + if ((uint)value >= arr.Count) + ClearSwarm(); + else + Swarm = arr[value]; } } diff --git a/PKHeX.Core/Saves/SAV3RS.cs b/PKHeX.Core/Saves/SAV3RS.cs index f185f2bbe..4f268a4dd 100644 --- a/PKHeX.Core/Saves/SAV3RS.cs +++ b/PKHeX.Core/Saves/SAV3RS.cs @@ -113,18 +113,16 @@ public PokeBlock3Case PokeBlocks protected override int SeenOffset2 => 0x938; - public DecorationInventory3 Decorations - { - get => Large.Slice(0x26A0, DecorationInventory3.SIZE).ToStructure(); - set => SetData(Large, value.ToBytes(), 0x26A0); - } + public DecorationInventory3 Decorations => new(Large.AsSpan(0x26A0, DecorationInventory3.SIZE)); public Swarm3 Swarm { - get => Large.Slice(0x2AFC, Swarm3.SIZE).ToClass(); - set => SetData(Large, value.ToBytesClass(), 0x2AFC); + get => new(Large.Slice(0x2AFC, Swarm3.SIZE)); + set => SetData(Large, value.Data, 0x2AFC); } + private void ClearSwarm() => Large.AsSpan(0x2AFC, Swarm3.SIZE).Clear(); + public IReadOnlyList DefaultSwarms => Swarm3Details.Swarms_RS; public int SwarmIndex @@ -133,7 +131,10 @@ public int SwarmIndex set { var arr = DefaultSwarms; - Swarm = (uint)value >= arr.Count ? new Swarm3() : arr[value]; + if ((uint)value >= arr.Count) + ClearSwarm(); + else + Swarm = arr[value]; } } diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index a89ff2f3e..496144cf6 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core @@ -177,16 +178,15 @@ public int Badges16 public PokegearNumber[] GetPokeGearRoloDex() { - var arr = new PokegearNumber[GearMaxCallers]; - for (int i = 0; i < arr.Length; i++) - arr[i] = GetCallerAtIndex(i); - return arr; + var arr = General.AsSpan(OFS_GearRolodex, GearMaxCallers); + return MemoryMarshal.Cast(arr).ToArray(); } - public void SetPokeGearRoloDex(IReadOnlyList value) + public void SetPokeGearRoloDex(ReadOnlySpan value) { - for (int i = 0; i < value.Count; i++) - SetCallerAtIndex(i, value[i]); + if (value.Length > GearMaxCallers) + throw new ArgumentOutOfRangeException(nameof(value)); + MemoryMarshal.Cast(value).CopyTo(General.AsSpan(OFS_GearRolodex, GearMaxCallers)); } public void PokeGearUnlockAllCallers() diff --git a/PKHeX.Core/Saves/Substructures/Gen3/DecorationInventory3.cs b/PKHeX.Core/Saves/Substructures/Gen3/DecorationInventory3.cs index 6328aff8c..3932095f0 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/DecorationInventory3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/DecorationInventory3.cs @@ -1,27 +1,23 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace PKHeX.Core { [StructLayout(LayoutKind.Sequential)] - public readonly struct DecorationInventory3 + public readonly ref struct DecorationInventory3 { public const int SIZE = 150; + private readonly Span Data; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly Decoration3[] Desk; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly Decoration3[] Chair; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly Decoration3[] Plant; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public readonly Decoration3[] Ornament; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public readonly Decoration3[] Mat; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly Decoration3[] Poster; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 40)] - public readonly Decoration3[] Doll; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly Decoration3[] Cushion; + public DecorationInventory3(Span data) => Data = data; + + public Span Desk => MemoryMarshal.Cast(Data[..10]); + public Span Chair => MemoryMarshal.Cast(Data.Slice(10, 10)); + public Span Plant => MemoryMarshal.Cast(Data.Slice(20, 10)); + public Span Ornament => MemoryMarshal.Cast(Data.Slice(30, 30)); + public Span Mat => MemoryMarshal.Cast(Data.Slice(60, 30)); + public Span Poster => MemoryMarshal.Cast(Data.Slice(90, 10)); + public Span Doll => MemoryMarshal.Cast(Data.Slice(100, 40)); + public Span Cushion => MemoryMarshal.Cast(Data.Slice(140, 10)); } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs b/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs index 103242742..e67b7b3e4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs @@ -10,7 +10,7 @@ public interface IGen3Hoenn RTC3 ClockInitial { get; set; } RTC3 ClockElapsed { get; set; } PokeBlock3Case PokeBlocks { get; set; } - DecorationInventory3 Decorations { get; set; } + DecorationInventory3 Decorations { get; } Swarm3 Swarm { get; set; } IReadOnlyList DefaultSwarms { get; } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs b/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs deleted file mode 100644 index 26f216ceb..000000000 --- a/PKHeX.Core/Saves/Substructures/Gen3/StructConverter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace PKHeX.Core -{ - public static class StructConverter - { - public static T ToStructure(this byte[] bytes) where T : struct - { - var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - try { return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); } - finally { handle.Free(); } - } - - public static T ToClass(this byte[] bytes) where T : class - { - var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - try { return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); } - finally { handle.Free(); } - } - - public static byte[] ToBytesClass(this T obj) where T : class - { - int size = Marshal.SizeOf(obj); - byte[] arr = new byte[size]; - - IntPtr ptr = Marshal.AllocHGlobal(size); - Marshal.StructureToPtr(obj, ptr, true); - Marshal.Copy(ptr, arr, 0, size); - Marshal.FreeHGlobal(ptr); - return arr; - } - - public static byte[] ToBytes(this T obj) where T : struct - { - int size = Marshal.SizeOf(obj); - byte[] arr = new byte[size]; - - IntPtr ptr = Marshal.AllocHGlobal(size); - Marshal.StructureToPtr(obj, ptr, true); - Marshal.Copy(ptr, arr, 0, size); - Marshal.FreeHGlobal(ptr); - return arr; - } - } -} diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs index cfaa4bfa8..7bb3c2e0c 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Swarm3.cs @@ -1,6 +1,8 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; using static PKHeX.Core.Move; using static PKHeX.Core.Species; +using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core { @@ -8,24 +10,27 @@ namespace PKHeX.Core public sealed class Swarm3 { public const int SIZE = 0x14; + public readonly byte[] Data; - public ushort Gen3Species { get; set; } - public byte MapNum { get; set; } - public byte MapGroup { get; set; } - public byte Level { get; set; } - public byte Unused1 { get; set; } - public ushort Unused2 { get; set; } - public ushort Move1 { get; set; } - public ushort Move2 { get; set; } - public ushort Move3 { get; set; } - public ushort Move4 { get; set; } - public byte Unused3 { get; set; } - public byte EncounterProbability { get; set; } - public ushort DaysLeft { get; set; } + private Span Raw => Data.AsSpan(); - public Swarm3() {} + public ushort Gen3Species { get => ReadUInt16LittleEndian(Raw); set => WriteUInt16LittleEndian(Raw, value); } + public byte MapNum { get => Raw[2]; set => Raw[2] = value; } + public byte MapGroup { get => Raw[3]; set => Raw[3] = value; } + public byte Level { get => Raw[4]; set => Raw[4] = value; } + public byte Unused1 { get => Raw[5]; set => Raw[5] = value; } + public ushort Unused2 { get => ReadUInt16LittleEndian(Raw[0x6..]); set => WriteUInt16LittleEndian(Raw[0x6..], value); } + public ushort Move1 { get => ReadUInt16LittleEndian(Raw[0x8..]); set => WriteUInt16LittleEndian(Raw[0x8..], value); } + public ushort Move2 { get => ReadUInt16LittleEndian(Raw[0xA..]); set => WriteUInt16LittleEndian(Raw[0xA..], value); } + public ushort Move3 { get => ReadUInt16LittleEndian(Raw[0xC..]); set => WriteUInt16LittleEndian(Raw[0xC..], value); } + public ushort Move4 { get => ReadUInt16LittleEndian(Raw[0xE..]); set => WriteUInt16LittleEndian(Raw[0xE..], value); } + public byte Unused3 { get => Raw[0x10]; set => Raw[0x10] = value; } + public byte EncounterProbability { get => Raw[0x11]; set => Raw[0x11] = value; } + public ushort DaysLeft { get => ReadUInt16LittleEndian(Raw[0x12..]); set => WriteUInt16LittleEndian(Raw[0x12..], value); } - public Swarm3(Species species, byte level, byte map, Move m1, Move m2 = 0, Move m3 = 0, Move m4 = 0) + public Swarm3(byte[] data) => Data = data; + + public Swarm3(Species species, byte level, byte map, Move m1, Move m2 = 0, Move m3 = 0, Move m4 = 0) : this(new byte[SIZE]) { Gen3Species = (ushort)SpeciesConverter.GetG3Species((int)species); Level = level; diff --git a/Tests/PKHeX.Core.Tests/General/MarshalTests.cs b/Tests/PKHeX.Core.Tests/General/MarshalTests.cs index 44892486d..a164fce17 100644 --- a/Tests/PKHeX.Core.Tests/General/MarshalTests.cs +++ b/Tests/PKHeX.Core.Tests/General/MarshalTests.cs @@ -3,27 +3,14 @@ using PKHeX.Core; using Xunit; -namespace PKHeX.Tests.General +namespace PKHeX.Tests.General; + +public class MarshalTests { - public class MarshalTests + [Fact] + public void MarshalSize() { - [Fact] - public void MarshalStructure() - { - new DecorationInventory3().ToBytes().Length.Should().Be(DecorationInventory3.SIZE); - } - - [Fact] - public void MarshalClass() - { - new Swarm3().ToBytesClass().Length.Should().Be(Swarm3.SIZE); - } - - [Fact] - public void MarshalSize() - { - Marshal.SizeOf(typeof(NPCLock)).Should().Be(8); - Marshal.SizeOf(typeof(PIDIV)).Should().Be(8); - } + Marshal.SizeOf(typeof(NPCLock)).Should().Be(8); + Marshal.SizeOf(typeof(PIDIV)).Should().Be(8); } }