diff --git a/PKHeX.Core/PKM/Util/PKX.cs b/PKHeX.Core/PKM/Util/PKX.cs index bd1a3c053..e0c9e9c48 100644 --- a/PKHeX.Core/PKM/Util/PKX.cs +++ b/PKHeX.Core/PKM/Util/PKX.cs @@ -492,7 +492,7 @@ private static void CryptPKM45(byte[] data, uint pv, uint chk, int blockSize) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CryptArray(byte[] data, uint seed, int start, int end) + public static void CryptArray(byte[] data, uint seed, int start, int end) { for (int i = start; i < end; i += 2) Crypt(data, ref seed, i); diff --git a/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForest.cs b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForest.cs new file mode 100644 index 000000000..558d63ee2 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForest.cs @@ -0,0 +1,119 @@ +using System; + +namespace PKHeX.Core +{ + public class EntreeForest + { + /// + /// Areas 1 thru 8 have 20 slots. + /// + private const byte Count18 = 20; + + /// + /// 9th Area has only 10 slots. + /// + private const byte Count9 = 10; + + private const int TotalSlots = Count18 + (3 * 8 * Count18) + (3 * Count9); + + /// + /// Areas 3 thru 8 can be unlocked (set a value 0 to 6). + /// + private const byte MaxUnlock38Areas = 6; + + + private readonly byte[] Data; + + public EntreeForest(byte[] data) + { + Data = data; + PKX.CryptArray(data, EncryptionSeed, 0, data.Length); + } + public byte[] Write() + { + byte[] data = (byte[])Data.Clone(); + PKX.CryptArray(data, EncryptionSeed, 0, data.Length); + return data; + } + + /// + /// Gets all Entree Slot data. + /// + public EntreeSlot[] Slots + { + get + { + var slots = new EntreeSlot[TotalSlots]; + for (int i = 0; i < slots.Length; i++) + slots[i] = new EntreeSlot(Data, i * 4) { Area = GetSlotArea(i) }; + return slots; + } + } + + /// + /// Determines if the 9th Area is available to enter. + /// + public bool Unlock9thArea + { + get => Data[0x848] == 1; + set => Data[0x848] = (byte)(value ? 1 : 0); + } + + /// + /// Determines how many extra areas are available to enter. Areas 1 & 2 are already available by default. + /// + public int Unlock38Areas + { + get => Data[0x849]; + set => Data[0x849] = (byte)Math.Max(MaxUnlock38Areas, value); + } + + public uint EncryptionSeed + { + get => BitConverter.ToUInt32(Data, 0x84C); + private set => BitConverter.GetBytes(value).CopyTo(Data, 0x84C); + } + public void UnlockAllAreas() + { + Unlock38Areas = MaxUnlock38Areas; + Unlock9thArea = true; + } + + public void DeleteAll() + { + foreach (var e in Slots) + e.Delete(); + } + + private static EntreeForestArea GetSlotArea(int index) + { + if (index < Count18) + return EntreeForestArea.Deepest; + index -= Count18; + + const int slots9 = 3 * Count9; + if (index < slots9) + return EntreeForestArea.Ninth | GetSlotPosition(index / Count9); + index -= slots9; + + const int slots18 = 3 * Count18; + int area = index / slots18; + if (area >= 8) + throw new ArgumentOutOfRangeException(nameof(index)); + index %= slots18; + + return (EntreeForestArea)((int)EntreeForestArea.First << area) | GetSlotPosition(index / Count18); + } + + private static EntreeForestArea GetSlotPosition(int index) + { + switch (index) + { + case 0: return EntreeForestArea.Center; + case 1: return EntreeForestArea.Left; + case 2: return EntreeForestArea.Right; + default: throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForestArea.cs b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForestArea.cs new file mode 100644 index 000000000..5349ebe90 --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeForestArea.cs @@ -0,0 +1,23 @@ +using System; + +namespace PKHeX.Core +{ + [Flags] + public enum EntreeForestArea + { + Deepest = 1 << 0, + First = 1 << 1, + Second = 1 << 2, + Third = 1 << 3, + Fourth = 1 << 4, + Fifth = 1 << 5, + Sixth = 1 << 6, + Seventh = 1 << 7, + Eighth = 1 << 8, + Ninth = 1 << 9, + + Left = 1 << 10, + Right = 1 << 11, + Center = 1 << 12, + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeSlot.cs b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeSlot.cs new file mode 100644 index 000000000..3aa56711a --- /dev/null +++ b/PKHeX.Core/Saves/Substructures/EntreeForest/EntreeSlot.cs @@ -0,0 +1,57 @@ +using System; + +namespace PKHeX.Core +{ + public class EntreeSlot + { + public int Species // bits 0-10 + { + get => (int)(RawValue & 0x7FF) >> 0; + set => RawValue = (RawValue & 0xFFFF_F800) | ((uint)value << 0); + } + public int Move // bits 11-20 + { + get => (int)(RawValue & 0x1F_F800) >> 10; + set => RawValue = (RawValue & 0xFFF0_02FF) | ((uint)value << 10); + } + public int Gender // bits 21-22 + { + get => (int)(RawValue & 60_0000) >> 21; + set => RawValue = (RawValue & 0xFF9F_FFFF) | ((uint)value << 21); + } + public int Form // bits 23-27 + { + get => (int)(RawValue & 0x0F80_0000) >> 23; + set => RawValue = (RawValue & 0xF07F_FFFF) | ((uint)value << 23); + } + public bool Invisible // bit 28 + { + get => ((RawValue >> 28) & 1) == 1; + set => RawValue = (RawValue & 0xEFFFFFFF) | (value ? 0 : 1u << 28); + } + public int Animation // bits 29-31 + { + get => (int)(RawValue >> 29); + set => RawValue = (RawValue << 3) >> 3 | (uint)(value << 29); + } + + private readonly byte[] Data; + private readonly int Offset; + + public uint RawValue + { + get => BitConverter.ToUInt32(Data, Offset); + set => BitConverter.GetBytes(value).CopyTo(Data, Offset); + } + + public void Delete() => RawValue = 0; + + public EntreeForestArea Area { get; internal set; } + + public EntreeSlot(byte[] data, int ofs) + { + Data = data; + Offset = ofs; + } + } +} \ No newline at end of file