From d15526a0be4d5214fe9caaad3f7abbc7d7d798b8 Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 2 Apr 2026 01:14:56 -0500 Subject: [PATCH] Add initial PKH side struct for ZA Haven't bothered testing because maintenance; just disassembled the pa9=>pkh import function. There's a side(sizeof=25) assumedly allocated for Champions. Rare chance it's actually for Gen3; can't find the "create" method due to virtualization/indirection in the disassembly. --- PKHeX.Core/PKM/HOME/GameDataPA8.cs | 18 +-- PKHeX.Core/PKM/HOME/GameDataPA9.cs | 163 ++++++---------------- PKHeX.Core/PKM/HOME/GameDataPB7.cs | 24 ++-- PKHeX.Core/PKM/HOME/GameDataPB8.cs | 24 ++-- PKHeX.Core/PKM/HOME/GameDataPC9.cs | 85 +++++++++++ PKHeX.Core/PKM/HOME/GameDataPK8.cs | 23 ++- PKHeX.Core/PKM/HOME/GameDataPK9.cs | 18 +-- PKHeX.Core/PKM/HOME/HomeCrypto.cs | 14 +- PKHeX.Core/PKM/HOME/HomeGameDataFormat.cs | 3 +- PKHeX.Core/PKM/HOME/IGameDataSide.cs | 77 +++++++--- PKHeX.Core/PKM/HOME/PKH.cs | 19 +-- 11 files changed, 256 insertions(+), 212 deletions(-) create mode 100644 PKHeX.Core/PKM/HOME/GameDataPC9.cs diff --git a/PKHeX.Core/PKM/HOME/GameDataPA8.cs b/PKHeX.Core/PKM/HOME/GameDataPA8.cs index 48bd844b5..dacd89886 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPA8.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPA8.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility, IPokerusStatus, IAlpha +public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility, IPokerusStatus, IAlpha, IGameDataSidePP { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8; private const int SIZE = HomeCrypto.SIZE_2GAME_PA8; @@ -29,10 +29,10 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSize public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x09..]); set => WriteUInt16LittleEndian(Data[0x09..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x0B..]); set => WriteUInt16LittleEndian(Data[0x0B..], value); } - public int Move1_PP { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } - public int Move2_PP { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } - public int Move3_PP { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } - public int Move4_PP { get => Data[0x10]; set => Data[0x10] = (byte)value; } + public byte Move1_PP { get => Data[0x0D]; set => Data[0x0D] = value; } + public byte Move2_PP { get => Data[0x0E]; set => Data[0x0E] = value; } + public byte Move3_PP { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte Move4_PP { get => Data[0x10]; set => Data[0x10] = value; } public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); } public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x13..]); set => WriteUInt16LittleEndian(Data[0x13..], value); } public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x15..]); set => WriteUInt16LittleEndian(Data[0x15..], value); } @@ -67,10 +67,10 @@ public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSize // Not stored. public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.LA.GetFormEntry(species, form); - public int Move1_PPUps { get => 0; set { } } - public int Move2_PPUps { get => 0; set { } } - public int Move3_PPUps { get => 0; set { } } - public int Move4_PPUps { get => 0; set { } } + public byte Move1_PPUps { get => 0; set { } } + public byte Move2_PPUps { get => 0; set { } } + public byte Move3_PPUps { get => 0; set { } } + public byte Move4_PPUps { get => 0; set { } } #endregion diff --git a/PKHeX.Core/PKM/HOME/GameDataPA9.cs b/PKHeX.Core/PKM/HOME/GameDataPA9.cs index daf6603de..65126293e 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPA9.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPA9.cs @@ -3,8 +3,6 @@ namespace PKHeX.Core; -// TODO HOME ZA -- Simply copied from SV, needs to be updated to ZA when official support is added. - /// /// Side game data for data transferred into HOME. /// @@ -26,145 +24,75 @@ public sealed class GameDataPA9 : HomeOptional1, IGameDataSide, IScaledSize public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x03..]); set => WriteUInt16LittleEndian(Data[0x03..], value); } public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x05..]); set => WriteUInt16LittleEndian(Data[0x05..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x07..]); set => WriteUInt16LittleEndian(Data[0x07..], value); } + public bool IsAlpha { get => Data[0x9] != 0; set => Data[0x9] = value ? (byte)1 : (byte)0; } - public int Move1_PP { get => Data[0x09]; set => Data[0x09] = (byte)value; } - public int Move2_PP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; } - public int Move3_PP { get => Data[0x0B]; set => Data[0x0B] = (byte)value; } - public int Move4_PP { get => Data[0x0C]; set => Data[0x0C] = (byte)value; } - public int Move1_PPUps { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } - public int Move2_PPUps { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } - public int Move3_PPUps { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } - public int Move4_PPUps { get => Data[0x10]; set => Data[0x10] = (byte)value; } + // Second set of flags is stored in the second block of PA9, devs declared it matching block order, not sequential bitflag order. + private const int PlusStartB = 0xA; + internal const int PlusCountB = PA9.PlusCount1; + private const int PlusLengthB = PlusCountB / 8; // 12 + public Span PlusFlagsB => Data.Slice(PlusStartB, PlusLengthB); - public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); } - public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x13..]); set => WriteUInt16LittleEndian(Data[0x13..], value); } - public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x15..]); set => WriteUInt16LittleEndian(Data[0x15..], value); } - public ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], value); } - public bool IsAlpha { get => Data[0x19] != 0; set => Data[0x19] = value ? (byte)1 : (byte)0; } - // 0x1A Padding (???) TODO HOME ZA; this is adapted from SV's structure, replacing the 2 byte properties for Tera Type. - public byte Ball { get => Data[0x1B]; set => Data[0x1B] = value; } - public ushort EggLocation { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], value); } - public ushort MetLocation { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], value); } + // First set of flags is stored in the third block of PA9, devs declared it matching block order, not sequential bitflag order. + private const int PlusStartC = PlusStartB + PlusLengthB; + internal const int PlusCountC = PA9.PlusCount0; + private const int PlusLengthC = PlusCountC / 8; // 33 + public Span PlusFlagsC => Data.Slice(PlusStartC, PlusLengthC); - private const int RecordStartBase = 0x20; - internal const int COUNT_RECORD_BASE = PA9.COUNT_RECORD_BASE; // Up to 200 TM flags, but not all are used. - private const int RecordLengthBase = COUNT_RECORD_BASE / 8; // 0x19 bytes, 8 bits - public Span RecordFlagsBase => Data.Slice(RecordStartBase, RecordLengthBase); + public byte Ball { get => Data[0x37]; set => Data[0x37] = value; } + public ushort EggLocation { get => ReadUInt16LittleEndian(Data[0x38..]); set => WriteUInt16LittleEndian(Data[0x38..], value); } + public ushort MetLocation { get => ReadUInt16LittleEndian(Data[0x3A..]); set => WriteUInt16LittleEndian(Data[0x3A..], value); } + public byte Obedience_Level { get => Data[0x3C]; set => Data[0x3C] = value; } + public ushort Ability { get => ReadUInt16LittleEndian(Data[0x3D..]); set => WriteUInt16LittleEndian(Data[0x3D..], value); } + public byte AbilityNumber { get => Data[0x3F]; set => Data[0x3F] = value; } - // Rev2 Additions - public byte Obedience_Level { get => Data[0x39]; set => Data[0x39] = value; } - public ushort Ability { get => ReadUInt16LittleEndian(Data[0x3A..]); set => WriteUInt16LittleEndian(Data[0x3A..], value); } - public byte AbilityNumber { get => Data[0x3C]; set => Data[0x3C] = value; } + // not sure how best to handle the dropping of these + public ushort RelearnMove1 { get => 0; set { } } + public ushort RelearnMove2 { get => 0; set { } } + public ushort RelearnMove3 { get => 0; set { } } + public ushort RelearnMove4 { get => 0; set { } } - // Rev3 Additions - private const int RecordStartDLC = 0x3D; - internal const int COUNT_RECORD_DLC = PA9.COUNT_RECORD_DLC; // 13 additional bytes allocated for DLC1/2 TM Flags - private const int RecordLengthDLC = COUNT_RECORD_DLC / 8; - public Span RecordFlagsDLC => Data.Slice(RecordStartDLC, RecordLengthDLC); - - // Rev4 Additions (ZA) - private const int PlusStart0 = 0x4A; - internal const int PlusCount0 = PA9.PlusCount0; - private const int PlusLength0 = PlusCount0 / 8; - public Span PlusFlags0 => Data.Slice(PlusStart0, PlusLength0); - - private const int PlusStart1 = PlusStart0 + PlusLength0; // 0x6B - internal const int PlusCount1 = PA9.PlusCount1; - private const int PlusLength1 = PlusCount1 / 8; - public Span PlusFlags1 => Data.Slice(PlusStart1, PlusLength1); - - #endregion - - #region TM Flag Methods - public bool GetMoveRecordFlag(int index) - { - if ((uint)index >= COUNT_RECORD_BASE) - return GetMoveRecordFlagDLC(index - COUNT_RECORD_BASE); - int ofs = index >> 3; - return FlagUtil.GetFlag(Data, RecordStartBase + ofs, index & 7); - } - - private bool GetMoveRecordFlagDLC(int index) - { - if ((uint)index >= COUNT_RECORD_DLC) - throw new ArgumentOutOfRangeException(nameof(index)); - int ofs = index >> 3; - return FlagUtil.GetFlag(Data, RecordStartDLC + ofs, index & 7); - } - - public void SetMoveRecordFlag(int index, bool value = true) - { - if ((uint)index >= COUNT_RECORD_BASE) - { - SetMoveRecordFlagDLC(value, index - COUNT_RECORD_BASE); - return; - } - int ofs = index >> 3; - FlagUtil.SetFlag(Data, RecordStartBase + ofs, index & 7, value); - } - - private void SetMoveRecordFlagDLC(bool value, int index) - { - if ((uint)index >= COUNT_RECORD_DLC) - throw new ArgumentOutOfRangeException(nameof(index)); - int ofs = index >> 3; - FlagUtil.SetFlag(Data, RecordStartDLC + ofs, index & 7, value); - } - - public bool GetMoveRecordFlagAny() => GetMoveRecordFlagAnyBase() || GetMoveRecordFlagAnyDLC(); - private bool GetMoveRecordFlagAnyBase() => RecordFlagsBase.ContainsAnyExcept(0); - private bool GetMoveRecordFlagAnyDLC() => RecordFlagsDLC.ContainsAnyExcept(0); - - public void ClearMoveRecordFlags() - { - ClearMoveRecordFlagsBase(); - ClearMoveRecordFlagsDLC(); - } - - private void ClearMoveRecordFlagsBase() => RecordFlagsBase.Clear(); - private void ClearMoveRecordFlagsDLC() => RecordFlagsDLC.Clear(); #endregion #region Plus Moves public bool GetMovePlusFlag(int index) { - if ((uint)index >= PlusCount0) - return GetMovePlusFlag1(index - PlusCount0); + if ((uint)index >= PlusCountC) + return GetMovePlusFlag1(index - PlusCountC); int ofs = index >> 3; - return FlagUtil.GetFlag(Data, PlusStart0 + ofs, index & 7); + return FlagUtil.GetFlag(Data, PlusStartC + ofs, index & 7); } private bool GetMovePlusFlag1(int index) { - if ((uint)index >= PlusCount1) + if ((uint)index >= PlusCountB) throw new ArgumentOutOfRangeException(nameof(index)); int ofs = index >> 3; - return FlagUtil.GetFlag(Data, PlusStart1 + ofs, index & 7); + return FlagUtil.GetFlag(Data, PlusStartB + ofs, index & 7); } public void SetMovePlusFlag(int index, bool value = true) { - if ((uint)index >= PlusCount0) + if ((uint)index >= PlusCountC) { - SetMovePlusFlag1(value, index - PlusCount0); + SetMovePlusFlag1(value, index - PlusCountC); return; } int ofs = index >> 3; - FlagUtil.SetFlag(Data, PlusStart0 + ofs, index & 7, value); + FlagUtil.SetFlag(Data, PlusStartC + ofs, index & 7, value); } private void SetMovePlusFlag1(bool value, int index) { - if ((uint)index >= PlusCount1) + if ((uint)index >= PlusCountB) throw new ArgumentOutOfRangeException(nameof(index)); int ofs = index >> 3; - FlagUtil.SetFlag(Data, PlusStart1 + ofs, index & 7, value); + FlagUtil.SetFlag(Data, PlusStartB + ofs, index & 7, value); } public bool GetMovePlusFlagAny() => GetMovePlusFlagAny0() || GetMovePlusFlagAny1(); - private bool GetMovePlusFlagAny0() => PlusFlags0.ContainsAnyExcept(0); - private bool GetMovePlusFlagAny1() => PlusFlags1.ContainsAnyExcept(0); + private bool GetMovePlusFlagAny0() => PlusFlagsC.ContainsAnyExcept(0); + private bool GetMovePlusFlagAny1() => PlusFlagsB.ContainsAnyExcept(0); public void ClearMovePlusFlags() { @@ -172,8 +100,8 @@ public void ClearMovePlusFlags() ClearMovePlusFlags1(); } - private void ClearMovePlusFlags0() => PlusFlags0.Clear(); - private void ClearMovePlusFlags1() => PlusFlags1.Clear(); + private void ClearMovePlusFlags0() => PlusFlagsC.Clear(); + private void ClearMovePlusFlags1() => PlusFlagsB.Clear(); #endregion #region Conversion @@ -184,10 +112,8 @@ public void CopyTo(PA9 pk, PKH pkh) { this.CopyTo(pk); pk.Scale = Scale; - PlusFlags0.CopyTo(pk.PlusFlags0); - PlusFlags1.CopyTo(pk.PlusFlags1); - RecordFlagsBase.CopyTo(pk.RecordFlagsBase); - RecordFlagsDLC.CopyTo(pk.RecordFlagsDLC); + PlusFlagsC.CopyTo(pk.PlusFlags0); + PlusFlagsB.CopyTo(pk.PlusFlags1); pk.ObedienceLevel = Obedience_Level; pk.Ability = Ability; pk.AbilityNumber = AbilityNumber; @@ -197,10 +123,8 @@ public void CopyFrom(PA9 pk, PKH pkh) { this.CopyFrom(pk); pkh.HeightScalar = Scale = pk.Scale; // Overwrite Height - pk.PlusFlags0.CopyTo(PlusFlags0); - pk.PlusFlags1.CopyTo(PlusFlags1); - pk.RecordFlagsBase.CopyTo(RecordFlagsBase); - pk.RecordFlagsDLC.CopyTo(RecordFlagsDLC); + pk.PlusFlags0.CopyTo(PlusFlagsC); + pk.PlusFlags1.CopyTo(PlusFlagsB); Obedience_Level = pk.ObedienceLevel; Ability = (ushort)pk.Ability; AbilityNumber = (byte)pk.AbilityNumber; @@ -244,11 +168,8 @@ public PA9 ConvertToPKM(PKH pkh) return result; } - private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide - ?? pkh.DataPK8 as IGameDataSide - ?? pkh.DataPB8 as IGameDataSide - ?? pkh.DataPB7 as IGameDataSide - ?? pkh.DataPA8; + private static IGameDataSide? GetNearestNeighbor(PKH pkh) + => pkh.DataPK9 ?? pkh.DataPK8 ?? pkh.DataPB8 ?? pkh.DataPB7 ?? pkh.DataPA8 as IGameDataSide; public void InitializeFrom(IGameDataSide side, PKH pkh) { diff --git a/PKHeX.Core/PKM/HOME/GameDataPB7.cs b/PKHeX.Core/PKM/HOME/GameDataPB7.cs index 5b0d1de30..638681676 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPB7.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPB7.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IGameDataSplitAbility +public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IGameDataSplitAbility, IGameDataSidePP { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB7; private const int SIZE = HomeCrypto.SIZE_2GAME_PB7; @@ -32,14 +32,14 @@ public sealed class GameDataPB7 : HomeOptional1, IGameDataSide, IScaledSize public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x0B..]); set => WriteUInt16LittleEndian(Data[0x0B..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x0D..]); set => WriteUInt16LittleEndian(Data[0x0D..], value); } - public int Move1_PP { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } - public int Move2_PP { get => Data[0x10]; set => Data[0x10] = (byte)value; } - public int Move3_PP { get => Data[0x11]; set => Data[0x11] = (byte)value; } - public int Move4_PP { get => Data[0x12]; set => Data[0x12] = (byte)value; } - public int Move1_PPUps { get => Data[0x13]; set => Data[0x13] = (byte)value; } - public int Move2_PPUps { get => Data[0x14]; set => Data[0x14] = (byte)value; } - public int Move3_PPUps { get => Data[0x15]; set => Data[0x15] = (byte)value; } - public int Move4_PPUps { get => Data[0x16]; set => Data[0x16] = (byte)value; } + public byte Move1_PP { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte Move2_PP { get => Data[0x10]; set => Data[0x10] = value; } + public byte Move3_PP { get => Data[0x11]; set => Data[0x11] = value; } + public byte Move4_PP { get => Data[0x12]; set => Data[0x12] = value; } + public byte Move1_PPUps { get => Data[0x13]; set => Data[0x13] = value; } + public byte Move2_PPUps { get => Data[0x14]; set => Data[0x14] = value; } + public byte Move3_PPUps { get => Data[0x15]; set => Data[0x15] = value; } + public byte Move4_PPUps { get => Data[0x16]; set => Data[0x16] = value; } public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], value); } public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x19..]); set => WriteUInt16LittleEndian(Data[0x19..], value); } @@ -209,10 +209,8 @@ private void PopulateFromCore(PKH pkh) Ability = (ushort)pi.GetAbilityAtIndex(index); } - private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide - ?? pkh.DataPB8 as IGameDataSide - ?? pkh.DataPK8 as IGameDataSide - ?? pkh.DataPB7; + private static IGameDataSide? GetNearestNeighbor(PKH pkh) + => pkh.DataPA9 ?? pkh.DataPK9 ?? pkh.DataPB8 ?? pkh.DataPK8 ?? pkh.DataPB7 as IGameDataSide; public static T Create(GameDataPB7 data) where T : IGameDataSide, new() => new() { diff --git a/PKHeX.Core/PKM/HOME/GameDataPB8.cs b/PKHeX.Core/PKM/HOME/GameDataPB8.cs index 5bdbad4c2..34c3216c9 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPB8.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPB8.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPB8 : HomeOptional1, IGameDataSide, IGameDataSplitAbility, IPokerusStatus +public sealed class GameDataPB8 : HomeOptional1, IGameDataSide, IGameDataSplitAbility, IPokerusStatus, IGameDataSidePP { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PB8; private const int SIZE = HomeCrypto.SIZE_2GAME_PB8; @@ -24,14 +24,14 @@ public sealed class GameDataPB8 : HomeOptional1, IGameDataSide, IGameDataSp 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 byte Move1_PP { get => Data[0x08]; set => Data[0x08] = value; } + public byte Move2_PP { get => Data[0x09]; set => Data[0x09] = value; } + public byte Move3_PP { get => Data[0x0A]; set => Data[0x0A] = value; } + public byte Move4_PP { get => Data[0x0B]; set => Data[0x0B] = value; } + public byte Move1_PPUps { get => Data[0x0C]; set => Data[0x0C] = value; } + public byte Move2_PPUps { get => Data[0x0D]; set => Data[0x0D] = value; } + public byte Move3_PPUps { get => Data[0x0E]; set => Data[0x0E] = value; } + public byte Move4_PPUps { get => Data[0x0F]; set => Data[0x0F] = 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); } @@ -102,10 +102,8 @@ public PB8 ConvertToPKM(PKH pkh) return result; } - private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide - ?? pkh.DataPK8 as IGameDataSide - ?? pkh.DataPB7 as IGameDataSide - ?? pkh.DataPA8; + private static IGameDataSide? GetNearestNeighbor(PKH pkh) + => pkh.DataPA9 ?? pkh.DataPK9 ?? pkh.DataPK8 ?? pkh.DataPB7 ?? pkh.DataPA8 as IGameDataSide; public void InitializeFrom(IGameDataSide side, PKH pkh) { diff --git a/PKHeX.Core/PKM/HOME/GameDataPC9.cs b/PKHeX.Core/PKM/HOME/GameDataPC9.cs new file mode 100644 index 000000000..483a04ca1 --- /dev/null +++ b/PKHeX.Core/PKM/HOME/GameDataPC9.cs @@ -0,0 +1,85 @@ +using System; +using static System.Buffers.Binary.BinaryPrimitives; + +namespace PKHeX.Core; + +// TODO -- NOTHING HERE IS CORRECT + +/// +/// Side game data for Champions data linked to HOME (assumedly, not FR/LG). +/// +public sealed class GameDataPC9 : HomeOptional1, IScaledSize3, IGameDataSide +{ + private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PC9; + private const int SIZE = HomeCrypto.SIZE_4GAME_PC9; + protected override HomeGameDataFormat Format => ExpectFormat; + + public GameDataPC9() : base(SIZE) { } + public GameDataPC9(Memory data) : base(data) => EnsureSize(SIZE); + public GameDataPC9 Clone() => new(ToArray()); + public int WriteTo(Span result) => WriteWithHeader(result); + + #region Structure + public byte Scale { get => Data[0x00]; set => Data[0x00] = value; } + public ushort Move1 { get => ReadUInt16LittleEndian(Data[0x01..]); set => WriteUInt16LittleEndian(Data[0x01..], value); } + public ushort Move2 { get => ReadUInt16LittleEndian(Data[0x03..]); set => WriteUInt16LittleEndian(Data[0x03..], value); } + public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x05..]); set => WriteUInt16LittleEndian(Data[0x05..], value); } + public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x07..]); set => WriteUInt16LittleEndian(Data[0x07..], value); } + + public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); } + public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x13..]); set => WriteUInt16LittleEndian(Data[0x13..], value); } + public ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x15..]); set => WriteUInt16LittleEndian(Data[0x15..], value); } + public ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], value); } + public byte Ball { get => Data[0x1B]; set => Data[0x1B] = value; } + public ushort EggLocation { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], value); } + public ushort MetLocation { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], value); } + + + #endregion + + + #region Conversion + + public PersonalInfo GetPersonalInfo(ushort species, byte form) => PersonalTable.ZA.GetFormEntry(species, form); + + public PKM ConvertToPKM(PKH pkh) => throw new NotSupportedException("No conversion routine."); + + #endregion + + /// Reconstructive logic to best apply suggested values. + public static GameDataPC9? TryCreate(PKH pkh) + { + if (!PersonalTable.ZA.IsPresentInGame(pkh.Species, pkh.Form)) + return null; + + var result = CreateInternal(pkh); + if (result is null) + return null; + + result.PopulateFromCore(pkh); + return result; + } + + private static GameDataPC9? CreateInternal(PKH pkh) + { + var side = GetNearestNeighbor(pkh); + if (side is null) + return null; + + var result = new GameDataPC9(); + result.InitializeFrom(side, pkh); + return result; + } + + private static IGameDataSide? GetNearestNeighbor(PKH pkh) + => pkh.DataPA9 ?? pkh.DataPK9 ?? pkh.DataPK8 ?? pkh.DataPB8 ?? pkh.DataPB7 ?? pkh.DataPA8 as IGameDataSide; + + public void InitializeFrom(IGameDataSide side, PKH pkh) + { + PopulateFromCore(pkh); + } + + private void PopulateFromCore(PKH pkh) + { + } +} diff --git a/PKHeX.Core/PKM/HOME/GameDataPK8.cs b/PKHeX.Core/PKM/HOME/GameDataPK8.cs index bcc763cf3..792621acf 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPK8.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPK8.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDynamaxLevel, ISociability, IGameDataSplitAbility, IPokerusStatus +public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax, IDynamaxLevel, ISociability, IGameDataSplitAbility, IPokerusStatus, IGameDataSidePP { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PK8; private const int SIZE = HomeCrypto.SIZE_2GAME_PK8; @@ -27,14 +27,14 @@ public sealed class GameDataPK8 : HomeOptional1, IGameDataSide, IGigantamax public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x09..]); set => WriteUInt16LittleEndian(Data[0x09..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x0B..]); set => WriteUInt16LittleEndian(Data[0x0B..], value); } - public int Move1_PP { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } - public int Move2_PP { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } - public int Move3_PP { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } - public int Move4_PP { get => Data[0x10]; set => Data[0x10] = (byte)value; } - public int Move1_PPUps { get => Data[0x11]; set => Data[0x11] = (byte)value; } - public int Move2_PPUps { get => Data[0x12]; set => Data[0x12] = (byte)value; } - public int Move3_PPUps { get => Data[0x13]; set => Data[0x13] = (byte)value; } - public int Move4_PPUps { get => Data[0x14]; set => Data[0x14] = (byte)value; } + public byte Move1_PP { get => Data[0x0D]; set => Data[0x0D] = value; } + public byte Move2_PP { get => Data[0x0E]; set => Data[0x0E] = value; } + public byte Move3_PP { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte Move4_PP { get => Data[0x10]; set => Data[0x10] = value; } + public byte Move1_PPUps { get => Data[0x11]; set => Data[0x11] = value; } + public byte Move2_PPUps { get => Data[0x12]; set => Data[0x12] = value; } + public byte Move3_PPUps { get => Data[0x13]; set => Data[0x13] = value; } + public byte Move4_PPUps { get => Data[0x14]; set => Data[0x14] = value; } public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x15..]); set => WriteUInt16LittleEndian(Data[0x15..], value); } public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x17..]); set => WriteUInt16LittleEndian(Data[0x17..], value); } @@ -153,9 +153,8 @@ public PK8 ConvertToPKM(PKH pkh) } // Ignores LGP/E, already preferred if exists. - private static IGameDataSide? GetNearestNeighbor(PKH pkh) => pkh.DataPK9 as IGameDataSide - ?? pkh.DataPB8 as IGameDataSide - ?? pkh.DataPA8; + private static IGameDataSide? GetNearestNeighbor(PKH pkh) + => pkh.DataPA9 ?? pkh.DataPK9 ?? pkh.DataPB8 ?? pkh.DataPA8 as IGameDataSide; private static GameDataPK8 CreateViaPB7(PKH pkh, GameDataPB7 x) { diff --git a/PKHeX.Core/PKM/HOME/GameDataPK9.cs b/PKHeX.Core/PKM/HOME/GameDataPK9.cs index cee285682..50395ebd9 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPK9.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPK9.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPK9 : HomeOptional1, IGameDataSide, IScaledSize3, IGameDataSplitAbility +public sealed class GameDataPK9 : HomeOptional1, IGameDataSide, IScaledSize3, IGameDataSplitAbility, IGameDataSidePP { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PK9; private const int SIZE = HomeCrypto.SIZE_3GAME_PK9; @@ -25,14 +25,14 @@ public sealed class GameDataPK9 : HomeOptional1, IGameDataSide, IScaledSize public ushort Move3 { get => ReadUInt16LittleEndian(Data[0x05..]); set => WriteUInt16LittleEndian(Data[0x05..], value); } public ushort Move4 { get => ReadUInt16LittleEndian(Data[0x07..]); set => WriteUInt16LittleEndian(Data[0x07..], value); } - public int Move1_PP { get => Data[0x09]; set => Data[0x09] = (byte)value; } - public int Move2_PP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; } - public int Move3_PP { get => Data[0x0B]; set => Data[0x0B] = (byte)value; } - public int Move4_PP { get => Data[0x0C]; set => Data[0x0C] = (byte)value; } - public int Move1_PPUps { get => Data[0x0D]; set => Data[0x0D] = (byte)value; } - public int Move2_PPUps { get => Data[0x0E]; set => Data[0x0E] = (byte)value; } - public int Move3_PPUps { get => Data[0x0F]; set => Data[0x0F] = (byte)value; } - public int Move4_PPUps { get => Data[0x10]; set => Data[0x10] = (byte)value; } + public byte Move1_PP { get => Data[0x09]; set => Data[0x09] = value; } + public byte Move2_PP { get => Data[0x0A]; set => Data[0x0A] = value; } + public byte Move3_PP { get => Data[0x0B]; set => Data[0x0B] = value; } + public byte Move4_PP { get => Data[0x0C]; set => Data[0x0C] = value; } + public byte Move1_PPUps { get => Data[0x0D]; set => Data[0x0D] = value; } + public byte Move2_PPUps { get => Data[0x0E]; set => Data[0x0E] = value; } + public byte Move3_PPUps { get => Data[0x0F]; set => Data[0x0F] = value; } + public byte Move4_PPUps { get => Data[0x10]; set => Data[0x10] = value; } public ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x11..]); set => WriteUInt16LittleEndian(Data[0x11..], value); } public ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x13..]); set => WriteUInt16LittleEndian(Data[0x13..], value); } diff --git a/PKHeX.Core/PKM/HOME/HomeCrypto.cs b/PKHeX.Core/PKM/HOME/HomeCrypto.cs index e9ade3239..e3ceef310 100644 --- a/PKHeX.Core/PKM/HOME/HomeCrypto.cs +++ b/PKHeX.Core/PKM/HOME/HomeCrypto.cs @@ -13,6 +13,7 @@ public static class HomeCrypto public const int Version1 = 1; public const int Version2 = 2; public const int Version3 = 3; + public const int Version4 = 4; public const int SIZE_1HEADER = 0x10; // 16 @@ -34,17 +35,18 @@ public static class HomeCrypto public const int SIZE_3GAME_PK9 = 0x3D + 0xD; // 74 public const int SIZE_3STORED = 0x247; // 583 + public const int SIZE_4GAME_PA9 = 0x40; // 64 TODO HOME ZA + public const int SIZE_4GAME_PC9 = 0x19; // 25 + public const int SIZE_4STORED = 0x2A6; // 702 TODO HOME ZA + /// Latest maximum size of a Pokémon Home entity. - public const int SIZE_STORED = SIZE_3STORED; + public const int SIZE_STORED = SIZE_4STORED; /// Latest maximum size of a Pokémon Home entity's core data shared with all side-games. public const int SIZE_CORE = SIZE_2CORE; /// Latest Version identifier stored in the header. - public const int VersionLatest = Version3; + public const int VersionLatest = Version4; - public const int SIZE_4GAME_PA9 = 0x77; // TODO HOME ZA - public const int SIZE_4STORED = 0x2BE; // 702 - - public static bool IsKnownVersion(ushort version) => version is Version1 or Version2 or Version3; + public static bool IsKnownVersion(ushort version) => version is Version1 or Version2 or Version3 or Version4; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetEncryptionKey(Span key, ulong seed) diff --git a/PKHeX.Core/PKM/HOME/HomeGameDataFormat.cs b/PKHeX.Core/PKM/HOME/HomeGameDataFormat.cs index 6fc2a10b9..7b4847d5e 100644 --- a/PKHeX.Core/PKM/HOME/HomeGameDataFormat.cs +++ b/PKHeX.Core/PKM/HOME/HomeGameDataFormat.cs @@ -11,5 +11,6 @@ public enum HomeGameDataFormat : byte PA8 = 3, PB8 = 4, PK9 = 5, - PA9 = 6, + PC9 = 6, + PA9 = 7, } diff --git a/PKHeX.Core/PKM/HOME/IGameDataSide.cs b/PKHeX.Core/PKM/HOME/IGameDataSide.cs index 5549a3eb3..f787ebbbc 100644 --- a/PKHeX.Core/PKM/HOME/IGameDataSide.cs +++ b/PKHeX.Core/PKM/HOME/IGameDataSide.cs @@ -41,10 +41,10 @@ public interface IGameDataSide : IGameDataSide where T : PKM, new() /// public interface IGameDataSide { - ushort Move1 { get; set; } int Move1_PP { get; set; } int Move1_PPUps { get; set; } ushort RelearnMove1 { get; set; } - ushort Move2 { get; set; } int Move2_PP { get; set; } int Move2_PPUps { get; set; } ushort RelearnMove2 { get; set; } - ushort Move3 { get; set; } int Move3_PP { get; set; } int Move3_PPUps { get; set; } ushort RelearnMove3 { get; set; } - ushort Move4 { get; set; } int Move4_PP { get; set; } int Move4_PPUps { get; set; } ushort RelearnMove4 { get; set; } + ushort Move1 { get; set; } ushort RelearnMove1 { get; set; } + ushort Move2 { get; set; } ushort RelearnMove2 { get; set; } + ushort Move3 { get; set; } ushort RelearnMove3 { get; set; } + ushort Move4 { get; set; } ushort RelearnMove4 { get; set; } byte Ball { get; set; } ushort MetLocation { get; set; } ushort EggLocation { get; set; } @@ -55,6 +55,14 @@ public interface IGameDataSide PersonalInfo GetPersonalInfo(ushort species, byte form); } +public interface IGameDataSidePP +{ + byte Move1_PP { get; set; } byte Move1_PPUps { get; set; } + byte Move2_PP { get; set; } byte Move2_PPUps { get; set; } + byte Move3_PP { get; set; } byte Move3_PPUps { get; set; } + byte Move4_PP { get; set; } byte Move4_PPUps { get; set; } +} + public static class GameDataSideExtensions { extension(IGameDataSide data) @@ -65,13 +73,21 @@ public static class GameDataSideExtensions /// Destination entity public void CopyTo(PKM pk) { - pk.Move1 = data.Move1; pk.Move1_PP = data.Move1_PP; pk.Move1_PPUps = data.Move1_PPUps; pk.RelearnMove1 = data.RelearnMove1; - pk.Move2 = data.Move2; pk.Move2_PP = data.Move2_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2; - pk.Move3 = data.Move3; pk.Move3_PP = data.Move3_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3; - pk.Move4 = data.Move4; pk.Move4_PP = data.Move4_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4; + pk.Move1 = data.Move1; pk.RelearnMove1 = data.RelearnMove1; + pk.Move2 = data.Move2; pk.RelearnMove2 = data.RelearnMove2; + pk.Move3 = data.Move3; pk.RelearnMove3 = data.RelearnMove3; + pk.Move4 = data.Move4; pk.RelearnMove4 = data.RelearnMove4; pk.Ball = data.Ball; pk.MetLocation = data.MetLocation; pk.EggLocation = data.EggLocation; + + if (data is IGameDataSidePP pps) + { + pk.Move1_PP = pps.Move1_PP; pk.Move1_PPUps = pps.Move1_PPUps; + pk.Move2_PP = pps.Move2_PP; pk.Move2_PPUps = pps.Move2_PPUps; + pk.Move3_PP = pps.Move3_PP; pk.Move3_PPUps = pps.Move3_PPUps; + pk.Move4_PP = pps.Move4_PP; pk.Move4_PPUps = pps.Move4_PPUps; + } } /// @@ -80,13 +96,21 @@ public void CopyTo(PKM pk) /// Destination entity public void CopyTo(IGameDataSide pk) { - pk.Move1 = data.Move1; pk.Move1_PP = data.Move1_PP; pk.Move1_PPUps = data.Move1_PPUps; pk.RelearnMove1 = data.RelearnMove1; - pk.Move2 = data.Move2; pk.Move2_PP = data.Move2_PP; pk.Move2_PPUps = data.Move2_PPUps; pk.RelearnMove2 = data.RelearnMove2; - pk.Move3 = data.Move3; pk.Move3_PP = data.Move3_PP; pk.Move3_PPUps = data.Move3_PPUps; pk.RelearnMove3 = data.RelearnMove3; - pk.Move4 = data.Move4; pk.Move4_PP = data.Move4_PP; pk.Move4_PPUps = data.Move4_PPUps; pk.RelearnMove4 = data.RelearnMove4; + pk.Move1 = data.Move1; pk.RelearnMove1 = data.RelearnMove1; + pk.Move2 = data.Move2; pk.RelearnMove2 = data.RelearnMove2; + pk.Move3 = data.Move3; pk.RelearnMove3 = data.RelearnMove3; + pk.Move4 = data.Move4; pk.RelearnMove4 = data.RelearnMove4; pk.Ball = data.Ball; pk.MetLocation = data.MetLocation; pk.EggLocation = data.EggLocation; + + if (data is IGameDataSidePP pps && pk is IGameDataSidePP ppk) + { + ppk.Move1_PP = pps.Move1_PP; ppk.Move1_PPUps = pps.Move1_PPUps; + ppk.Move2_PP = pps.Move2_PP; ppk.Move2_PPUps = pps.Move2_PPUps; + ppk.Move3_PP = pps.Move3_PP; ppk.Move3_PPUps = pps.Move3_PPUps; + ppk.Move4_PP = pps.Move4_PP; ppk.Move4_PPUps = pps.Move4_PPUps; + } } /// @@ -95,13 +119,21 @@ public void CopyTo(IGameDataSide pk) /// Destination entity public void CopyFrom(PKM pk) { - data.Move1 = pk.Move1; data.Move1_PP = pk.Move1_PP; data.Move1_PPUps = pk.Move1_PPUps; data.RelearnMove1 = pk.RelearnMove1; - data.Move2 = pk.Move2; data.Move2_PP = pk.Move2_PP; data.Move2_PPUps = pk.Move2_PPUps; data.RelearnMove2 = pk.RelearnMove2; - data.Move3 = pk.Move3; data.Move3_PP = pk.Move3_PP; data.Move3_PPUps = pk.Move3_PPUps; data.RelearnMove3 = pk.RelearnMove3; - data.Move4 = pk.Move4; data.Move4_PP = pk.Move4_PP; data.Move4_PPUps = pk.Move4_PPUps; data.RelearnMove4 = pk.RelearnMove4; + data.Move1 = pk.Move1; data.RelearnMove1 = pk.RelearnMove1; + data.Move2 = pk.Move2; data.RelearnMove2 = pk.RelearnMove2; + data.Move3 = pk.Move3; data.RelearnMove3 = pk.RelearnMove3; + data.Move4 = pk.Move4; data.RelearnMove4 = pk.RelearnMove4; data.Ball = pk.Ball; data.MetLocation = pk.MetLocation; data.EggLocation = pk.EggLocation; + + if (pk is IGameDataSidePP ppk && data is IGameDataSidePP pps) + { + pps.Move1_PP = ppk.Move1_PP; pps.Move1_PPUps = ppk.Move1_PPUps; + pps.Move2_PP = ppk.Move2_PP; pps.Move2_PPUps = ppk.Move2_PPUps; + pps.Move3_PP = ppk.Move3_PP; pps.Move3_PPUps = ppk.Move3_PPUps; + pps.Move4_PP = ppk.Move4_PP; pps.Move4_PPUps = ppk.Move4_PPUps; + } } /// @@ -116,10 +148,15 @@ public void ResetMoves(ushort species, byte form, byte level, ILearnSource sourc data.Move2 = moves[1]; data.Move3 = moves[2]; data.Move4 = moves[3]; - data.Move1_PP = MoveInfo.GetPP(context, moves[0]); - data.Move2_PP = MoveInfo.GetPP(context, moves[1]); - data.Move3_PP = MoveInfo.GetPP(context, moves[2]); - data.Move4_PP = MoveInfo.GetPP(context, moves[3]); + + if (data is IGameDataSidePP pps) + { + pps.Move1_PPUps = 0; pps.Move2_PPUps = 0; pps.Move3_PPUps = 0; pps.Move4_PPUps = 0; + pps.Move1_PP = MoveInfo.GetPP(context, moves[0]); + pps.Move2_PP = MoveInfo.GetPP(context, moves[1]); + pps.Move3_PP = MoveInfo.GetPP(context, moves[2]); + pps.Move4_PP = MoveInfo.GetPP(context, moves[3]); + } } } } diff --git a/PKHeX.Core/PKM/HOME/PKH.cs b/PKHeX.Core/PKM/HOME/PKH.cs index 86e5172e8..01ccf229b 100644 --- a/PKHeX.Core/PKM/HOME/PKH.cs +++ b/PKHeX.Core/PKM/HOME/PKH.cs @@ -14,6 +14,7 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat public GameDataPA8? DataPA8 { get; private set; } public GameDataPB8? DataPB8 { get; private set; } public GameDataPK9? DataPK9 { get; private set; } + public GameDataPC9? DataPC9 { get; private set; } public GameDataPA9? DataPA9 { get; private set; } public override EntityContext Context => EntityContext.None; @@ -63,6 +64,7 @@ private void ReadGameData1(Memory data) HomeGameDataFormat.PA8 => DataPA8 = new GameDataPA8(chunk), HomeGameDataFormat.PB8 => DataPB8 = new GameDataPB8(chunk), HomeGameDataFormat.PK9 => DataPK9 = new GameDataPK9(chunk), + HomeGameDataFormat.PC9 => DataPC9 = new GameDataPC9(chunk), HomeGameDataFormat.PA9 => DataPA9 = new GameDataPA9(chunk), _ => throw new ArgumentException($"Unknown {nameof(HomeGameDataFormat)} {format}"), }; @@ -213,14 +215,14 @@ private static Memory DecryptHome(Memory data) public override ushort Move2 { get => LatestGameData.Move2 ; set => LatestGameData.Move2 = value; } public override ushort Move3 { get => LatestGameData.Move3 ; set => LatestGameData.Move3 = value; } public override ushort Move4 { get => LatestGameData.Move4 ; set => LatestGameData.Move4 = value; } - public override int Move1_PP { get => LatestGameData.Move1_PP ; set => LatestGameData.Move1_PP = value; } - public override int Move2_PP { get => LatestGameData.Move2_PP ; set => LatestGameData.Move2_PP = value; } - public override int Move3_PP { get => LatestGameData.Move3_PP ; set => LatestGameData.Move3_PP = value; } - public override int Move4_PP { get => LatestGameData.Move4_PP ; set => LatestGameData.Move4_PP = value; } - public override int Move1_PPUps { get => LatestGameData.Move1_PPUps; set => LatestGameData.Move1_PPUps = value; } - public override int Move2_PPUps { get => LatestGameData.Move2_PPUps; set => LatestGameData.Move2_PPUps = value; } - public override int Move3_PPUps { get => LatestGameData.Move3_PPUps; set => LatestGameData.Move3_PPUps = value; } - public override int Move4_PPUps { get => LatestGameData.Move4_PPUps; set => LatestGameData.Move4_PPUps = value; } + public override int Move1_PP { get => (LatestGameData as IGameDataSidePP)?.Move1_PP ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move1_PP = (byte)value; } + public override int Move2_PP { get => (LatestGameData as IGameDataSidePP)?.Move2_PP ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move2_PP = (byte)value; } + public override int Move3_PP { get => (LatestGameData as IGameDataSidePP)?.Move3_PP ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move3_PP = (byte)value; } + public override int Move4_PP { get => (LatestGameData as IGameDataSidePP)?.Move4_PP ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move4_PP = (byte)value; } + public override int Move1_PPUps { get => (LatestGameData as IGameDataSidePP)?.Move1_PPUps ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move1_PPUps = (byte)value; } + public override int Move2_PPUps { get => (LatestGameData as IGameDataSidePP)?.Move2_PPUps ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move2_PPUps = (byte)value; } + public override int Move3_PPUps { get => (LatestGameData as IGameDataSidePP)?.Move3_PPUps ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move3_PPUps = (byte)value; } + public override int Move4_PPUps { get => (LatestGameData as IGameDataSidePP)?.Move4_PPUps ?? 0; set => (LatestGameData as IGameDataSidePP)?.Move4_PPUps = (byte)value; } public override byte Ball { get => LatestGameData.Ball; set => LatestGameData.Ball = value; } public override ushort MetLocation { get => LatestGameData.MetLocation; set => LatestGameData.MetLocation = value; } @@ -309,6 +311,7 @@ private void WriteTo(Span span, int innerLength) if (DataPA8 is { } pa8) ctr += pa8.WriteTo(span[ctr..]); if (DataPB8 is { } pb8) ctr += pb8.WriteTo(span[ctr..]); if (DataPK9 is { } pk9) ctr += pk9.WriteTo(span[ctr..]); + if (DataPC9 is { } pc9) ctr += pc9.WriteTo(span[ctr..]); if (DataPA9 is { } pa9) ctr += pa9.WriteTo(span[ctr..]); WriteUInt16LittleEndian(gameDataLengthSpan, GameDataSize = (ushort)(ctr - gameDataStart));