using System; using static System.Buffers.Binary.BinaryPrimitives; namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// public sealed class GameDataPB8 : HomeOptional1, IGameDataSide, IGameDataSplitAbility, IPokerusStatus { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB8; private const int SIZE = HomeCrypto.SIZE_2GAME_PB8; protected override HomeGameDataFormat Format => ExpectFormat; public GameDataPB8() : base(SIZE) { } public GameDataPB8(Memory data) : base(data) => EnsureSize(SIZE); public GameDataPB8 Clone() => new(ToArray()); public int WriteTo(Span result) => WriteWithHeader(result); #region Structure public ushort Move1 { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); } public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x02..]); set => WriteUInt16LittleEndian(Data[0x02..], value); } public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); } public int Move1_PP { get => Data[0x08]; set => Data[0x08] = (byte)value; } public int Move2_PP { get => Data[0x09]; set => Data[0x09] = (byte)value; } public int Move3_PP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; } public int Move4_PP { get => Data[0x0B]; set => Data[0x0B] = (byte)value; } public int Move1_PPUps { get => Data[0x0C]; set => Data[0x0C] = (byte)value; } public int Move2_PPUps { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } public int Move3_PPUps { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } public int Move4_PPUps { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); } public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x12..]); set => WriteUInt16LittleEndian(Data[0x12..], value); } public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], value); } public ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x16..]); set => WriteUInt16LittleEndian(Data[0x16..], value); } private Span RecordFlag => Data.Slice(0x18, 14); public bool GetMoveRecordFlag(int index) => FlagUtil.GetFlag(RecordFlag, index >> 3, index & 7); public void SetMoveRecordFlag(int index, bool value) => FlagUtil.SetFlag(RecordFlag, index >> 3, index & 7, value); public bool GetMoveRecordFlagAny() => RecordFlag.IndexOfAnyExcept(0) >= 0; public void ClearMoveRecordFlags() => RecordFlag.Clear(); public int Ball { get => Data[0x26]; set => Data[0x26] = (byte)value; } public int Egg_Location { get => ReadUInt16LittleEndian(Data[0x27..]); set => WriteUInt16LittleEndian(Data[0x27..], (ushort)value); } public int Met_Location { get => ReadUInt16LittleEndian(Data[0x29..]); set => WriteUInt16LittleEndian(Data[0x29..], (ushort)value); } // Rev2 Additions public byte PKRS { get => Data[0x2B]; set => Data[0x2B] = value; } public ushort Ability { get => ReadUInt16LittleEndian(Data[0x2C..]); set => WriteUInt16LittleEndian(Data[0x2C..], value); } public byte AbilityNumber { get => Data[0x2E]; set => Data[0x2E] = value; } #endregion #region Conversion public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.BDSP.GetFormEntry(species, form); public void CopyTo(PB8 pk, PKH pkh) { this.CopyTo(pk); // Move Records are not settable in PB8; do not copy even if nonzero (illegal). pk.PKRS = PKRS; pk.AbilityNumber = AbilityNumber; pk.Ability = Ability; } public void CopyFrom(PB8 pk, PKH pkh) { this.CopyFrom(pk); // Move Records are not settable in PB8; do not copy even if nonzero (illegal). PKRS = pk.PKRS; AbilityNumber = (byte)pk.AbilityNumber; Ability = (ushort)pk.Ability; } public PB8 ConvertToPKM(PKH pkh) { var pk = new PB8(); pkh.CopyTo(pk); CopyTo(pk, pkh); pk.ResetPartyStats(); pk.RefreshChecksum(); return pk; } #endregion /// Reconstructive logic to best apply suggested values. public static GameDataPB8? TryCreate(PKH pkh) { var side = GetNearestNeighbor(pkh); if (side == null) return null; var result = new GameDataPB8(); result.InitializeFrom(side, pkh); return result; } private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide ?? pkh.DataPK8 as IGameDataSide ?? pkh.DataPB7 as IGameDataSide ?? pkh.DataPA8; public void InitializeFrom(IGameDataSide side, PKH pkh) { Ball = side.Ball; Met_Location = side.Met_Location == 0 ? Locations.Default8bNone : side.Met_Location; Egg_Location = side.Egg_Location == 0 ? Locations.Default8bNone : side.Egg_Location; if (side is IPokerusStatus p) PKRS = p.PKRS; if (side is IGameDataSplitAbility a) AbilityNumber = a.AbilityNumber; else AbilityNumber = 1; PopulateFromCore(pkh); this.ResetMoves(pkh.Species, pkh.Form, pkh.CurrentLevel, LearnSource8BDSP.Instance, EntityContext.Gen8b); } private void PopulateFromCore(PKH pkh) { var pi = PersonalTable.BDSP.GetFormEntry(pkh.Species, pkh.Form); Ability = (ushort)pi.GetAbilityAtIndex(AbilityNumber >> 1); } }