From c0b5d3eef7bc9031d70a1a5388bef24d6c056a6d Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 19 Aug 2018 15:50:15 -0700 Subject: [PATCH] Abstract away pk1/pk2 properties to shared class some inefficiencies with pk1 altform/iskorean but is much easier to manage --- PKHeX.Core/PKM/PK1.cs | 326 +++++++--------------------------- PKHeX.Core/PKM/PK2.cs | 279 +---------------------------- PKHeX.Core/PKM/Shared/_K12.cs | 287 ++++++++++++++++++++++++++++++ 3 files changed, 358 insertions(+), 534 deletions(-) create mode 100644 PKHeX.Core/PKM/Shared/_K12.cs diff --git a/PKHeX.Core/PKM/PK1.cs b/PKHeX.Core/PKM/PK1.cs index 9042bc2cf..76a659054 100644 --- a/PKHeX.Core/PKM/PK1.cs +++ b/PKHeX.Core/PKM/PK1.cs @@ -1,181 +1,34 @@ using System; -using System.Collections.Generic; using System.Linq; namespace PKHeX.Core { /// Generation 1 format. - public sealed class PK1 : PKM + public sealed class PK1 : _K12 { - internal byte[] otname; - internal byte[] nick; public override PersonalInfo PersonalInfo => PersonalTable.Y[Species]; public override bool Valid => Species <= 151 && (Data[0] == 0 || Species != 0); public override int SIZE_PARTY => PKX.SIZE_1PARTY; public override int SIZE_STORED => PKX.SIZE_1STORED; - internal const int STRLEN_J = 6; - internal const int STRLEN_U = 11; - private int StringLength => Japanese ? STRLEN_J : STRLEN_U; - - private string GetString(int Offset, int Count) => StringConverter.GetString1(Data, Offset, Count, Japanese); - private byte[] SetString(string value, int maxLength) => StringConverter.SetString1(value, maxLength - 1, Japanese); - - // Trash Bytes - public override byte[] Nickname_Trash { get => nick; set { if (value?.Length == nick.Length) nick = value; } } - public override byte[] OT_Trash { get => otname; set { if (value?.Length == otname.Length) otname = value; } } + public override bool Korean => false; public override int Format => 1; - public override bool Japanese => otname.Length == STRLEN_J; - public override bool Korean => false; - - public override string FileName - { - get - { - string star = IsShiny ? " ★" : ""; - return $"{Species:000}{star} - {Nickname} - {SaveUtil.CRC16_CCITT(Encrypt()):X4}.{Extension}"; - } - } - - public PK1(byte[] decryptedData = null, string ident = null, bool jp = false) - { - Data = decryptedData ?? new byte[SIZE_PARTY]; - Identifier = ident; - if (Data.Length != SIZE_PARTY) - Array.Resize(ref Data, SIZE_PARTY); - int strLen = jp ? STRLEN_J : STRLEN_U; - otname = Enumerable.Repeat((byte) 0x50, strLen).ToArray(); - nick = Enumerable.Repeat((byte) 0x50, strLen).ToArray(); - } + public PK1(byte[] decryptedData = null, string ident = null, bool jp = false) : base(decryptedData, ident, jp) { } public override PKM Clone() => new PK1((byte[])Data.Clone(), Identifier, Japanese) { otname = (byte[])otname.Clone(), nick = (byte[])nick.Clone(), }; - public override string Nickname - { - get => StringConverter.GetString1(nick, 0, nick.Length, Japanese); - set - { - if (!IsNicknamed && Nickname == value) - return; - - byte[] strdata = SetString(value, StringLength); - if (nick.Any(b => b == 0) && nick[StringLength - 1] == 0x50 && Array.FindIndex(nick, b => b == 0) == strdata.Length - 1) // Handle JP Mew event with grace - { - int firstInd = Array.FindIndex(nick, b => b == 0); - for (int i = firstInd; i < StringLength - 1; i++) - if (nick[i] != 0) - break; - strdata = strdata.Take(strdata.Length - 1).ToArray(); - } - strdata.CopyTo(nick, 0); - } - } - - public override string OT_Name - { - get => StringConverter.GetString1(otname, 0, otname.Length, Japanese); - set - { - byte[] strdata = SetString(value, StringLength); - if (otname.Any(b => b == 0) && otname[StringLength - 1] == 0x50 && Array.FindIndex(otname, b => b == 0) == strdata.Length - 1) // Handle JP Mew event with grace - { - int firstInd = Array.FindIndex(otname, b => b == 0); - for (int i = firstInd; i < StringLength - 1; i++) - if (otname[i] != 0) - break; - strdata = strdata.Take(strdata.Length - 1).ToArray(); - } - strdata.CopyTo(otname, 0); - } - } protected override byte[] Encrypt() => new PokeList1(this).Write(); - public override byte[] EncryptedPartyData => Encrypt(); - public override byte[] EncryptedBoxData => Encrypt(); - public override byte[] DecryptedBoxData => Encrypt(); - public override byte[] DecryptedPartyData => Encrypt(); - - private bool? _isnicknamed; - public override bool IsNicknamed - { - get => (bool)(_isnicknamed ?? (_isnicknamed = !nick.SequenceEqual(GetNonNickname()))); - set - { - _isnicknamed = value; - if (_isnicknamed == false) - SetNotNicknamed(); - } - } - public void SetNotNicknamed() => nick = GetNonNickname().ToArray(); - private IEnumerable GetNonNickname() - { - var name = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); - var bytes = SetString(name, StringLength); - return bytes.Concat(Enumerable.Repeat((byte)0x50, nick.Length - bytes.Length)) - .Select(b => (byte)(b == 0xF2 ? 0xE8 : b)); // Decimal point<->period fix - } - public bool IsNicknamedBank - { - get - { - var spName = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); - return Nickname != spName; - } - } - public override int Language - { - get - { - if (Japanese) - return (int)LanguageID.Japanese; - if (StringConverter.IsG12German(otname)) - return (int)LanguageID.German; - int lang = PKX.GetSpeciesNameLanguage(Species, Nickname, Format); - if (lang > 0) - return lang; - return 0; - } - set { } - } - private int GuessedLanguage(int fallback = (int)LanguageID.English) - { - int lang = Language; - if (lang > 0) - return lang; - if (fallback == (int)LanguageID.French || fallback == (int)LanguageID.German) // only other permitted besides English - return fallback; - return (int)LanguageID.English; - } #region Stored Attributes public int SpeciesID1 { get => Data[0]; set => Data[0] = (byte)value; } // raw access - public override int Species - { - get => SpeciesConverter.GetG1Species(SpeciesID1); - set - { - SpeciesID1 = (byte)SpeciesConverter.SetG1Species(value); - Type_A = PersonalInfo.Type1; - Type_B = PersonalInfo.Type2; - - // Before updating catch rate, check if non-standard - if (TradebackStatus != TradebackType.WasTradeback && !Legal.IsCatchRateHeldItem(Catch_Rate) && !(value == 25 && Catch_Rate == 0xA3)) // Light Ball Pikachu - { - int baseSpecies = Legal.GetBaseSpecies(this); - int Rate = Catch_Rate; - int count = value - baseSpecies + 1; - if (Enumerable.Range(baseSpecies, count).All(z => Rate != PersonalTable.RB[z].CatchRate && Rate != PersonalTable.Y[z].CatchRate)) - Catch_Rate = PersonalTable.RB[value].CatchRate; - } - } - } - + public override int Species { get => SpeciesConverter.GetG1Species(SpeciesID1); set => SetSpeciesValues(value); } public override int Stat_HPCurrent { get => BigEndian.ToUInt16(Data, 0x1); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x1); } public int Stat_LevelBox { get => Data[3];set => Data[3] = (byte)value; } public int Status_Condition { get => Data[4]; set => Data[4] = (byte)value; } @@ -187,11 +40,7 @@ public override int Species public override int Move3 { get => Data[10]; set => Data[10] = (byte)value; } public override int Move4 { get => Data[11]; set => Data[11] = (byte)value; } public override int TID { get => BigEndian.ToUInt16(Data, 0xC); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xC); } - public override uint EXP - { - get => (BigEndian.ToUInt32(Data, 0xE) >> 8) & 0x00FFFFFF; - set => Array.Copy(BigEndian.GetBytes((value << 8) & 0xFFFFFF00), 0, Data, 0xE, 3); - } + public override uint EXP { get => BigEndian.ToUInt32(Data, 0xE) >> 8; set => Array.Copy(BigEndian.GetBytes(value << 8), 0, Data, 0xE, 3); } public override int EV_HP { get => BigEndian.ToUInt16(Data, 0x11); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x11); } public override int EV_ATK { get => BigEndian.ToUInt16(Data, 0x13); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x13); } public override int EV_DEF { get => BigEndian.ToUInt16(Data, 0x15); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x15); } @@ -199,14 +48,7 @@ public override uint EXP public int EV_SPC { get => BigEndian.ToUInt16(Data, 0x19); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x19); } public override int EV_SPA { get => EV_SPC; set => EV_SPC = value; } public override int EV_SPD { get => EV_SPC; set { } } - public ushort DV16 { get => BigEndian.ToUInt16(Data, 0x1B); set => BigEndian.GetBytes(value).CopyTo(Data, 0x1B); } - public override int IV_HP { get => ((IV_ATK & 1) << 3) | ((IV_DEF & 1) << 2) | ((IV_SPE & 1) << 1) | ((IV_SPC & 1) << 0); set { } } - public override int IV_ATK { get => (DV16 >> 12) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 12)) | (ushort)((value > 0xF ? 0xF : value) << 12)); } - public override int IV_DEF { get => (DV16 >> 8) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 8)) | (ushort)((value > 0xF ? 0xF : value) << 8)); } - public override int IV_SPE { get => (DV16 >> 4) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 4)) | (ushort)((value > 0xF ? 0xF : value) << 4)); } - public int IV_SPC { get => (DV16 >> 0) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 0)) | (ushort)((value > 0xF ? 0xF : value) << 0)); } - public override int IV_SPA { get => IV_SPC; set => IV_SPC = value; } - public override int IV_SPD { get => IV_SPC; set { } } + public override ushort DV16 { get => BigEndian.ToUInt16(Data, 0x1B); set => BigEndian.GetBytes(value).CopyTo(Data, 0x1B); } public override int Move1_PP { get => Data[0x1D] & 0x3F; set => Data[0x1D] = (byte)((Data[0x1D] & 0xC0) | Math.Min(63, value)); } public override int Move2_PP { get => Data[0x1E] & 0x3F; set => Data[0x1E] = (byte)((Data[0x1E] & 0xC0) | Math.Min(63, value)); } public override int Move3_PP { get => Data[0x1F] & 0x3F; set => Data[0x1F] = (byte)((Data[0x1F] & 0xC0) | Math.Min(63, value)); } @@ -218,11 +60,7 @@ public override uint EXP #endregion #region Party Attributes - public override int Stat_Level - { - get => Data[0x21]; - set => Stat_LevelBox = Data[0x21] = (byte)value; - } + public override int Stat_Level { get => Data[0x21]; set => Stat_LevelBox = Data[0x21] = (byte)value; } public override int Stat_HPMax { get => BigEndian.ToUInt16(Data, 0x22); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x22); } public override int Stat_ATK { get => BigEndian.ToUInt16(Data, 0x24); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x24); } public override int Stat_DEF { get => BigEndian.ToUInt16(Data, 0x26); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x26); } @@ -233,119 +71,48 @@ public override int Stat_Level public override int Stat_SPD { get => Stat_SPC; set { } } #endregion - public override int GetMovePP(int move, int ppup) => Math.Min(61, base.GetMovePP(move, ppup)); - public override ushort[] GetStats(PersonalInfo p) + private void SetSpeciesValues(int value) { - var lv = Stat_Level; - ushort[] Stats = + var updated = (byte)SpeciesConverter.SetG1Species(value); + if (SpeciesID1 == updated) + return; + + Type_A = PersonalInfo.Type1; + Type_B = PersonalInfo.Type2; + + // Before updating catch rate, check if non-standard + if (TradebackStatus != TradebackType.WasTradeback && !Legal.IsCatchRateHeldItem(Catch_Rate) && !(value == 25 && Catch_Rate == 0xA3)) // Light Ball Pikachu { - GetStat(p.HP , IV_HP , EV_HP , lv), - GetStat(p.ATK, IV_ATK, EV_ATK, lv), - GetStat(p.DEF, IV_DEF, EV_DEF, lv), - GetStat(p.SPE, IV_SPE, EV_SPE, lv), - GetStat(p.SPA, IV_SPA, EV_SPA, lv), - GetStat(p.SPD, IV_SPD, EV_SPD, lv), - }; - Stats[0] += (ushort)(5 + lv); // HP - return Stats; + int baseSpecies = Legal.GetBaseSpecies(this); + int Rate = Catch_Rate; + for (int z = baseSpecies; z <= value; z++) + { + if (Rate == PersonalTable.RB[z].CatchRate && Rate == PersonalTable.Y[z].CatchRate) + return; + } + Catch_Rate = PersonalTable.RB[value].CatchRate; + } } - private static ushort GetStat(int BV, int IV, int EV, int LV) - { - EV = (ushort)Math.Sqrt(EV) >> 2; - return (ushort)((((2 * (BV + IV)) + EV) * LV / 100) + 5); - } - - #region Future, Unused Attributes - public override bool IsGenderValid() => true; // not a separate property, derived via IVs - public override uint EncryptionConstant { get => 0; set { } } - public override uint PID { get => 0; set { } } - public override int Met_Level { get => 0; set { } } - public override int Nature { get => 0; set { } } - public override int AltForm { get => 0; set { } } - public override bool IsEgg { get => false; set { } } - public override int HeldItem { get => 0; set { } } - public override bool CanHoldItem(ushort[] ValidArray) => false; - public override bool IsShiny => IV_DEF == 10 && IV_SPE == 10 && IV_SPC == 10 && (IV_ATK & 2) == 2; - public override ushort Sanity { get => 0; set { } } - public override bool ChecksumValid => true; - public override ushort Checksum { get => 0; set { } } - public override bool FatefulEncounter { get => false; set { } } - public override int TSV => 0x0000; - public override int PSV => 0xFFFF; - public override int Characteristic => -1; - public override int MarkValue { get => 0; protected set { } } - public override int CurrentFriendship { get => 0; set { } } - public override int Ability { get => -1; set { } } - public override int CurrentHandler { get => 0; set { } } - public override int Met_Location { get => 0; set { } } - public override int Egg_Location { get => 0; set { } } - public override int OT_Friendship { get => 0; set { } } - public override int OT_Gender { get => 0; set { } } - public override int Ball { get => 0; set { } } public override int Version { get => (int)GameVersion.RBY; set { } } - public override int SID { get => 0; set { } } public override int PKRS_Strain { get => 0; set { } } public override int PKRS_Days { get => 0; set { } } - #endregion - - public override int Gender - { - get - { - int gv = PersonalInfo.Gender; - if (gv == 255) - return 2; - if (gv == 254) - return 1; - if (gv == 0) - return 0; - return IV_ATK > gv >> 4 ? 0 : 1; - } - set { } - } // Maximums public override int MaxMoveID => Legal.MaxMoveID_1; public override int MaxSpeciesID => Legal.MaxSpeciesID_1; public override int MaxAbilityID => Legal.MaxAbilityID_1; public override int MaxItemID => Legal.MaxItemID_1; - public override int MaxBallID => -1; - public override int MaxGameID => -1; - public override int MaxIV => 15; - public override int MaxEV => ushort.MaxValue; - public override int OTLength => Japanese ? 5 : 7; - public override int NickLength => Japanese ? 5 : 10; public PK2 ConvertToPK2() { PK2 pk2 = new PK2(null, Identifier, Japanese) {Species = Species}; Array.Copy(Data, 0x7, pk2.Data, 0x1, 0x1A); - // https://github.com/pret/pokecrystal/blob/master/engine/link.asm#L1132 - if (!Legal.HeldItems_GSC.Contains((ushort)pk2.HeldItem)) - switch (pk2.HeldItem) - { - case 0x19: - pk2.HeldItem = 0x92; // Leftovers - break; - case 0x2D: - pk2.HeldItem = 0x53; // Bitter Berry - break; - case 0x32: - pk2.HeldItem = 0xAE; // Leftovers - break; - case 0x5A: - case 0x64: - case 0x78: - case 0x87: - case 0xBE: - case 0xC3: - case 0xDC: - case 0xFA: - case 0xFF: - pk2.HeldItem = 0xAD; // Berry - break; - } + + var held = pk2.HeldItem; + if (!Legal.HeldItems_GSC.Contains((ushort)held)) + pk2.HeldItem = GetConvertedHeldItem(held); + pk2.CurrentFriendship = pk2.PersonalInfo.BaseFriendship; // Pokerus = 0 // Caught Data = 0 @@ -356,6 +123,35 @@ public PK2 ConvertToPK2() return pk2; } + /// + /// Returns a Gen2 Item ID from the input Gen1 (Teru-sama) Item ID + /// + /// Gen1 Item ID + /// Gen2 Item ID + /// https://github.com/pret/pokecrystal/blob/edb624c20ceb50eef9d73a5df0ac041cc156dd32/engine/link/link.asm#L1093-L1115 + private static int GetConvertedHeldItem(int terusama) + { + switch (terusama) + { + case 0x19: return 0x92; // Leftovers + case 0x2D: return 0x53; // Bitter Berry + case 0x32: return 0xAE; // Leftovers + + case 0x5A: + case 0x64: + case 0x78: + case 0x87: + case 0xBE: + case 0xC3: + case 0xDC: + case 0xFA: + case 0xFF: + return 0xAD; // Berry + + default: return terusama; + } + } + public PK7 ConvertToPK7() { var pk7 = new PK7 @@ -420,7 +216,9 @@ public PK7 ConvertToPK7() pk7.RefreshAbility(abil); // 0/1/2 (not 1/2/4) if (Species == 151) // Mew gets special treatment. + { pk7.FatefulEncounter = true; + } else if (IsNicknamedBank) { pk7.IsNicknamed = true; diff --git a/PKHeX.Core/PKM/PK2.cs b/PKHeX.Core/PKM/PK2.cs index dad6312b7..010b9c44c 100644 --- a/PKHeX.Core/PKM/PK2.cs +++ b/PKHeX.Core/PKM/PK2.cs @@ -1,66 +1,21 @@ using System; -using System.Collections.Generic; -using System.Linq; namespace PKHeX.Core { /// Generation 2 format. - public sealed class PK2 : PKM + public sealed class PK2 : _K12 { - internal byte[] otname; - internal byte[] nick; public override PersonalInfo PersonalInfo => PersonalTable.C[Species]; public override bool Valid => Species <= 252; public override int SIZE_PARTY => PKX.SIZE_2PARTY; public override int SIZE_STORED => PKX.SIZE_2STORED; - internal const int STRLEN_J = 6; - internal const int STRLEN_U = 11; - private int StringLength => Japanese ? STRLEN_J : STRLEN_U; public override bool Korean => !Japanese && otname[0] <= 0xB; - private string GetString(int Offset, int Count) - { - if (Korean) - return StringConverter.GetString2KOR(Data, Offset, Count); - return StringConverter.GetString1(Data, Offset, Count, Japanese); - } - - private byte[] SetString(string value, int maxLength) - { - if (Korean) - return StringConverter.SetString2KOR(value, maxLength - 1); - return StringConverter.SetString1(value, maxLength - 1, Japanese); - } - - // Trash Bytes - public override byte[] Nickname_Trash { get => nick; set { if (value?.Length == nick.Length) nick = value; } } - public override byte[] OT_Trash { get => otname; set { if (value?.Length == otname.Length) otname = value; } } - public override int Format => 2; - public override bool Japanese => otname.Length == STRLEN_J; - public override string FileName - { - get - { - string form = AltForm > 0 ? $"-{AltForm:00}" : ""; - string star = IsShiny ? " ★" : ""; - return $"{Species:000}{form}{star} - {Nickname} - {SaveUtil.CRC16_CCITT(Encrypt()):X4}.{Extension}"; - } - } - - public PK2(byte[] decryptedData = null, string ident = null, bool jp = false) - { - Data = decryptedData ?? new byte[SIZE_PARTY]; - Identifier = ident; - if (Data.Length != SIZE_PARTY) - Array.Resize(ref Data, SIZE_PARTY); - int strLen = jp ? STRLEN_J : STRLEN_U; - otname = Enumerable.Repeat((byte) 0x50, strLen).ToArray(); - nick = Enumerable.Repeat((byte) 0x50, strLen).ToArray(); - } + public PK2(byte[] decryptedData = null, string ident = null, bool jp = false) : base(decryptedData, ident, jp) { } public override PKM Clone() => new PK2((byte[])Data.Clone(), Identifier, Japanese) { @@ -68,123 +23,11 @@ public override PKM Clone() => new PK2((byte[])Data.Clone(), Identifier, Japanes nick = (byte[])nick.Clone(), IsEgg = IsEgg, }; - public override string Nickname - { - get - { - if (Korean) - return StringConverter.GetString2KOR(nick, 0, nick.Length); - return StringConverter.GetString1(nick, 0, nick.Length, Japanese); - } - set - { - if (!IsNicknamed && Nickname == value) - return; - - byte[] strdata = SetString(value, StringLength); - if (nick.Any(b => b == 0) && nick[StringLength - 1] == 0x50 && Array.FindIndex(nick, b => b == 0) == strdata.Length - 1) // Handle JP Mew event with grace - { - int firstInd = Array.FindIndex(nick, b => b == 0); - for (int i = firstInd; i < StringLength - 1; i++) - if (nick[i] != 0) - break; - strdata = strdata.Take(strdata.Length - 1).ToArray(); - } - strdata.CopyTo(nick, 0); - } - } - - public override string OT_Name - { - get - { - if (Korean) - return StringConverter.GetString2KOR(otname, 0, otname.Length); - return StringConverter.GetString1(otname, 0, otname.Length, Japanese); - } - set - { - byte[] strdata = SetString(value, StringLength); - if (otname.Any(b => b == 0) && otname[StringLength - 1] == 0x50 && Array.FindIndex(otname, b => b == 0) == strdata.Length - 1) // Handle JP Mew event with grace - { - int firstInd = Array.FindIndex(otname, b => b == 0); - for (int i = firstInd; i < StringLength - 1; i++) - if (otname[i] != 0) - break; - strdata = strdata.Take(strdata.Length - 1).ToArray(); - } - strdata.CopyTo(otname, 0); - } - } protected override byte[] Encrypt() => new PokeList2(this).Write(); - public override byte[] EncryptedPartyData => Encrypt(); - public override byte[] EncryptedBoxData => Encrypt(); - public override byte[] DecryptedBoxData => Encrypt(); - public override byte[] DecryptedPartyData => Encrypt(); - - private bool? _isnicknamed; - public override bool IsNicknamed - { - get => (bool)(_isnicknamed ?? (_isnicknamed = !nick.SequenceEqual(GetNonNickname()))); - set - { - _isnicknamed = value; - if (_isnicknamed == false) - SetNotNicknamed(); - } - } - public void SetNotNicknamed() => nick = GetNonNickname().ToArray(); - private IEnumerable GetNonNickname() - { - var name = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); - var bytes = SetString(name, StringLength); - var data = bytes.Concat(Enumerable.Repeat((byte) 0x50, nick.Length - bytes.Length)); - if (!Korean) - data = data.Select(b => (byte)(b == 0xF2 ? 0xE8 : b)); // Decimal point<->period fix - return data; - } - public bool IsNicknamedBank - { - get - { - var spName = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); - return Nickname != spName; - } - } - public override int Language - { - get - { - if (Japanese) - return (int)LanguageID.Japanese; - if (Korean) - return (int)LanguageID.Korean; - if (StringConverter.IsG12German(otname)) - return (int)LanguageID.German; // german - int lang = PKX.GetSpeciesNameLanguage(Species, Nickname, Format); - if (lang > 0) - return lang; - return 0; - } - set { } - } - private int GuessedLanguage(int fallback = (int)LanguageID.English) - { - int lang = Language; - if (lang > 0) - return lang; - if (fallback == (int)LanguageID.French || fallback == (int)LanguageID.German) // only other permitted besides English - return fallback; - return (int)LanguageID.English; - } #region Stored Attributes - public override int Species - { - get => Data[0]; - set => Data[0] = (byte)value; - } + public override int Species { get => Data[0]; set => Data[0] = (byte)value; } public override int SpriteItem => ItemConverter.GetG4Item((byte)HeldItem); public override int HeldItem { get => Data[0x1]; set => Data[0x1] = (byte)value; } public override int Move1 { get => Data[2]; set => Data[2] = (byte)value; } @@ -192,11 +35,7 @@ public override int Species public override int Move3 { get => Data[4]; set => Data[4] = (byte)value; } public override int Move4 { get => Data[5]; set => Data[5] = (byte)value; } public override int TID { get => BigEndian.ToUInt16(Data, 6); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 6); } - public override uint EXP - { - get => (BigEndian.ToUInt32(Data, 8) >> 8) & 0x00FFFFFF; - set => Array.Copy(BigEndian.GetBytes((value << 8) & 0xFFFFFF00), 0, Data, 8, 3); - } + public override uint EXP { get => BigEndian.ToUInt32(Data, 8) >> 8; set => Array.Copy(BigEndian.GetBytes(value << 8), 0, Data, 8, 3); } public override int EV_HP { get => BigEndian.ToUInt16(Data, 0xB); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xB); } public override int EV_ATK { get => BigEndian.ToUInt16(Data, 0xD); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xD); } public override int EV_DEF { get => BigEndian.ToUInt16(Data, 0xF); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0xF); } @@ -204,14 +43,7 @@ public override uint EXP public int EV_SPC { get => BigEndian.ToUInt16(Data, 0x13); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x13); } public override int EV_SPA { get => EV_SPC; set => EV_SPC = value; } public override int EV_SPD { get => EV_SPC; set { } } - public ushort DV16 { get => BigEndian.ToUInt16(Data, 0x15); set => BigEndian.GetBytes(value).CopyTo(Data, 0x15); } - public override int IV_HP { get => ((IV_ATK & 1) << 3) | ((IV_DEF & 1) << 2) | ((IV_SPE & 1) << 1) | ((IV_SPC & 1) << 0); set { } } - public override int IV_ATK { get => (DV16 >> 12) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 12)) | (ushort)((value > 0xF ? 0xF : value) << 12)); } - public override int IV_DEF { get => (DV16 >> 8) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 8)) | (ushort)((value > 0xF ? 0xF : value) << 8)); } - public override int IV_SPE { get => (DV16 >> 4) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 4)) | (ushort)((value > 0xF ? 0xF : value) << 4)); } - public int IV_SPC { get => (DV16 >> 0) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 0)) | (ushort)((value > 0xF ? 0xF : value) << 0)); } - public override int IV_SPA { get => IV_SPC; set => IV_SPC = value; } - public override int IV_SPD { get => IV_SPC; set { } } + public override ushort DV16 { get => BigEndian.ToUInt16(Data, 0x15); set => BigEndian.GetBytes(value).CopyTo(Data, 0x15); } public override int Move1_PP { get => Data[0x17] & 0x3F; set => Data[0x17] = (byte)((Data[0x17] & 0xC0) | Math.Min(63, value)); } public override int Move2_PP { get => Data[0x18] & 0x3F; set => Data[0x18] = (byte)((Data[0x18] & 0xC0) | Math.Min(63, value)); } public override int Move3_PP { get => Data[0x19] & 0x3F; set => Data[0x19] = (byte)((Data[0x19] & 0xC0) | Math.Min(63, value)); } @@ -222,8 +54,8 @@ public override uint EXP public override int Move4_PPUps { get => (Data[0x1A] & 0xC0) >> 6; set => Data[0x1A] = (byte)((Data[0x1A] & 0x3F) | ((value & 0x3) << 6)); } public override int CurrentFriendship { get => Data[0x1B]; set => Data[0x1B] = (byte)value; } private byte PKRS { get => Data[0x1C]; set => Data[0x1C] = value; } - public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)(PKRS & ~0xF | value); } - public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)(PKRS & 0xF | value << 4); } + public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); } + public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); } // Crystal only Caught Data public int CaughtData { get => BigEndian.ToUInt16(Data, 0x1D); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x1D); } public int Met_TimeOfDay { get => (CaughtData >> 14) & 0x3; set => CaughtData = (CaughtData & 0x3FFF) | ((value & 0x3) << 14); } @@ -251,111 +83,16 @@ public override int Stat_Level public override int Stat_SPD { get => BigEndian.ToUInt16(Data, 0x2E); set => BigEndian.GetBytes((ushort)value).CopyTo(Data, 0x2E); } #endregion - public override int GetMovePP(int move, int ppup) => Math.Min(61, base.GetMovePP(move, ppup)); - public override ushort[] GetStats(PersonalInfo p) - { - var lv = Stat_Level; - ushort[] Stats = - { - GetStat(p.HP , IV_HP , EV_HP , lv), - GetStat(p.ATK, IV_ATK, EV_ATK, lv), - GetStat(p.DEF, IV_DEF, EV_DEF, lv), - GetStat(p.SPE, IV_SPE, EV_SPE, lv), - GetStat(p.SPA, IV_SPA, EV_SPA, lv), - GetStat(p.SPD, IV_SPD, EV_SPD, lv), - }; - Stats[0] += (ushort)(5 + lv); // HP - return Stats; - } - - private static ushort GetStat(int BV, int IV, int EV, int LV) - { - EV = (ushort)Math.Sqrt(EV) >> 2; - return (ushort)((((2 * (BV + IV)) + EV) * LV / 100) + 5); - } - public override bool IsEgg { get; set; } - public override int Gender - { - get - { - int gv = PersonalInfo.Gender; - if (gv == 255) - return 2; - if (gv == 254) - return 1; - if (gv == 0) - return 0; - return IV_ATK > gv >> 4 ? 0 : 1; - } - set { } - } - public override bool HasOriginalMetLocation => CaughtData != 0; - - #region Future, Unused Attributes - public override bool IsGenderValid() => true; // not a separate property, derived via IVs - public override uint EncryptionConstant { get => 0; set { } } - public override uint PID { get => 0; set { } } - public override int Nature { get => 0; set { } } - - public override int AltForm - { - get - { - if (Species != 201) // Unown - return 0; - - uint formeVal = 0; - formeVal |= (uint)((IV_ATK & 0x6) << 5); - formeVal |= (uint)((IV_DEF & 0x6) << 3); - formeVal |= (uint)((IV_SPE & 0x6) << 1); - formeVal |= (uint)((IV_SPC & 0x6) >> 1); - return (int)(formeVal / 10); - } - set { } - } - - private int HPVal => GetHiddenPowerBitVal(new[] {IV_SPC, IV_SPE, IV_DEF, IV_ATK}); - public override int HPPower => (5 * HPVal + IV_SPC % 4) / 2 + 31; - public override int HPType - { - get => ((IV_ATK & 3) << 2) | (IV_DEF & 3); set - { - IV_DEF = ((IV_DEF >> 2) << 2) | (value & 3); - IV_DEF = ((IV_ATK >> 2) << 2) | ((value >> 2) & 3); - } - } - public override bool IsShiny => IV_DEF == 10 && IV_SPE == 10 && IV_SPC == 10 && (IV_ATK & 2) == 2; - public override ushort Sanity { get => 0; set { } } - public override bool ChecksumValid => true; - public override ushort Checksum { get => 0; set { } } - public override bool FatefulEncounter { get => false; set { } } - public override int TSV => 0x0000; - public override int PSV => 0xFFFF; - public override int Characteristic => -1; - public override int MarkValue { get => 0; protected set { } } - public override int Ability { get => -1; set { } } - public override int CurrentHandler { get => 0; set { } } - public override int Egg_Location { get => 0; set { } } - public override int OT_Friendship { get => 0; set { } } - public override int Ball { get => 0; set { } } public override int Version { get => HasOriginalMetLocation ? (int)GameVersion.C : (int)GameVersion.GSC; set { } } - public override int SID { get => 0; set { } } - #endregion // Maximums public override int MaxMoveID => Legal.MaxMoveID_2; public override int MaxSpeciesID => Legal.MaxSpeciesID_2; public override int MaxAbilityID => Legal.MaxAbilityID_2; public override int MaxItemID => Legal.MaxItemID_2; - public override int MaxBallID => -1; - public override int MaxGameID => -1; - public override int MaxIV => 15; - public override int MaxEV => ushort.MaxValue; - public override int OTLength => Japanese ? 5 : 7; - public override int NickLength => Japanese ? 5 : 10; public PK1 ConvertToPK1() { @@ -448,7 +185,9 @@ public PK7 ConvertToPK7() pk7.RefreshAbility(abil); // 0/1/2 (not 1/2/4) if (special) + { pk7.FatefulEncounter = true; + } else if (IsNicknamedBank) { pk7.IsNicknamed = true; diff --git a/PKHeX.Core/PKM/Shared/_K12.cs b/PKHeX.Core/PKM/Shared/_K12.cs new file mode 100644 index 000000000..4883eeb9b --- /dev/null +++ b/PKHeX.Core/PKM/Shared/_K12.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PKHeX.Core +{ + public abstract class _K12 : PKM + { + internal const int STRLEN_J = 6; + internal const int STRLEN_U = 11; + + public override int MaxBallID => -1; + public override int MaxGameID => -1; + public override int MaxIV => 15; + public override int MaxEV => ushort.MaxValue; + public override int OTLength => Japanese ? 5 : 7; + public override int NickLength => Japanese ? 5 : 10; + + public override string FileName + { + get + { + string form = AltForm > 0 ? $"-{AltForm:00}" : ""; + string star = IsShiny ? " ★" : ""; + return $"{Species:000}{form}{star} - {Nickname} - {SaveUtil.CRC16_CCITT(Encrypt()):X4}.{Extension}"; + } + } + + private int StringLength => Japanese ? STRLEN_J : STRLEN_U; + public override bool Japanese => otname.Length == STRLEN_J; + + protected _K12(byte[] decryptedData, string ident = null, bool jp = false) + { + int partySize = SIZE_PARTY; + Data = decryptedData ?? new byte[partySize]; + Identifier = ident; + if (Data.Length != partySize) + Array.Resize(ref Data, partySize); + int strLen = jp ? STRLEN_J : STRLEN_U; + + // initialize string buffers + otname = new byte[strLen]; + nick = new byte[strLen]; + for (int i = 0; i < otname.Length; i++) + otname[i] = nick[i] = 0x50; + } + + internal byte[] otname; + internal byte[] nick; + + // Trash Bytes + public override byte[] Nickname_Trash { get => nick; set { if (value?.Length == nick.Length) nick = value; } } + public override byte[] OT_Trash { get => otname; set { if (value?.Length == otname.Length) otname = value; } } + + public override byte[] EncryptedPartyData => Encrypt(); + public override byte[] EncryptedBoxData => Encrypt(); + public override byte[] DecryptedBoxData => Encrypt(); + public override byte[] DecryptedPartyData => Encrypt(); + + private bool? _isnicknamed; + + public override bool IsNicknamed + { + get => (bool)(_isnicknamed ?? (_isnicknamed = !nick.SequenceEqual(GetNonNickname()))); + set + { + _isnicknamed = value; + if (_isnicknamed == false) + SetNotNicknamed(); + } + } + + protected bool IsNicknamedBank + { + get + { + var spName = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); + return Nickname != spName; + } + } + + public override int Language + { + get + { + if (Japanese) + return (int)LanguageID.Japanese; + if (Korean) + return (int)LanguageID.Korean; + if (StringConverter.IsG12German(otname)) + return (int)LanguageID.German; // german + int lang = PKX.GetSpeciesNameLanguage(Species, Nickname, Format); + if (lang > 0) + return lang; + return 0; + } + set { } + } + + public override string Nickname + { + get + { + if (Korean) + return StringConverter.GetString2KOR(nick, 0, nick.Length); + return StringConverter.GetString1(nick, 0, nick.Length, Japanese); + } + set + { + if (!IsNicknamed && Nickname == value) + return; + + GetStringSpecial(value, StringLength).CopyTo(nick, 0); + } + } + + public override string OT_Name + { + get + { + if (Korean) + return StringConverter.GetString2KOR(otname, 0, otname.Length); + return StringConverter.GetString1(otname, 0, otname.Length, Japanese); + } + set => GetStringSpecial(value, StringLength).CopyTo(otname, 0); + } + + public override int Gender + { + get + { + int gv = PersonalInfo.Gender; + if (gv == 255) + return 2; + if (gv == 254) + return 1; + if (gv == 0) + return 0; + return IV_ATK > gv >> 4 ? 0 : 1; + } + set { } + } + + #region Future, Unused Attributes + public override bool IsGenderValid() => true; // not a separate property, derived via IVs + public override uint EncryptionConstant { get => 0; set { } } + public override uint PID { get => 0; set { } } + public override int Met_Level { get => 0; set { } } + public override int Nature { get => 0; set { } } + public override bool IsEgg { get => false; set { } } + public override int HeldItem { get => 0; set { } } + public override bool CanHoldItem(ushort[] ValidArray) => false; + public override ushort Sanity { get => 0; set { } } + public override bool ChecksumValid => true; + public override ushort Checksum { get => 0; set { } } + public override bool FatefulEncounter { get => false; set { } } + public override int TSV => 0x0000; + public override int PSV => 0xFFFF; + public override int Characteristic => -1; + public override int MarkValue { get => 0; protected set { } } + public override int CurrentFriendship { get => 0; set { } } + public override int Ability { get => -1; set { } } + public override int CurrentHandler { get => 0; set { } } + public override int Met_Location { get => 0; set { } } + public override int Egg_Location { get => 0; set { } } + public override int OT_Friendship { get => 0; set { } } + public override int OT_Gender { get => 0; set { } } + public override int Ball { get => 0; set { } } + public override int SID { get => 0; set { } } + #endregion + + public override bool IsShiny => IV_DEF == 10 && IV_SPE == 10 && IV_SPC == 10 && (IV_ATK & 2) == 2; + private int HPVal => GetHiddenPowerBitVal(new[] { IV_SPC, IV_SPE, IV_DEF, IV_ATK }); + public override int HPPower => (((5 * HPVal) + (IV_SPC % 4)) / 2) + 31; + + public override int HPType + { + get => ((IV_ATK & 3) << 2) | (IV_DEF & 3); set + { + IV_DEF = ((IV_DEF >> 2) << 2) | (value & 3); + IV_DEF = ((IV_ATK >> 2) << 2) | ((value >> 2) & 3); + } + } + + public override int AltForm + { + get + { + if (Species != 201) // Unown + return 0; + + uint formeVal = 0; + formeVal |= (uint)((IV_ATK & 0x6) << 5); + formeVal |= (uint)((IV_DEF & 0x6) << 3); + formeVal |= (uint)((IV_SPE & 0x6) << 1); + formeVal |= (uint)((IV_SPC & 0x6) >> 1); + return (int)(formeVal / 10); + } + set { } + } + + public abstract ushort DV16 { get; set; } + public override int IV_HP { get => ((IV_ATK & 1) << 3) | ((IV_DEF & 1) << 2) | ((IV_SPE & 1) << 1) | ((IV_SPC & 1) << 0); set { } } + public override int IV_ATK { get => (DV16 >> 12) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 12)) | (ushort)((value > 0xF ? 0xF : value) << 12)); } + public override int IV_DEF { get => (DV16 >> 8) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 8)) | (ushort)((value > 0xF ? 0xF : value) << 8)); } + public override int IV_SPE { get => (DV16 >> 4) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 4)) | (ushort)((value > 0xF ? 0xF : value) << 4)); } + public int IV_SPC { get => (DV16 >> 0) & 0xF; set => DV16 = (ushort)((DV16 & ~(0xF << 0)) | (ushort)((value > 0xF ? 0xF : value) << 0)); } + public override int IV_SPA { get => IV_SPC; set => IV_SPC = value; } + public override int IV_SPD { get => IV_SPC; set { } } + + public void SetNotNicknamed() => nick = GetNonNickname().ToArray(); + + private IEnumerable GetNonNickname() + { + var name = PKX.GetSpeciesNameGeneration(Species, GuessedLanguage(), Format); + var bytes = SetString(name, StringLength); + var data = bytes.Concat(Enumerable.Repeat((byte)0x50, nick.Length - bytes.Length)); + if (!Korean) + data = data.Select(b => (byte)(b == 0xF2 ? 0xE8 : b)); // Decimal point<->period fix + return data; + } + + protected int GuessedLanguage(int fallback = (int)LanguageID.English) + { + int lang = Language; + if (lang > 0) + return lang; + if (fallback == (int)LanguageID.French || fallback == (int)LanguageID.German) // only other permitted besides English + return fallback; + return (int)LanguageID.English; + } + + public override ushort[] GetStats(PersonalInfo p) + { + var lv = Stat_Level; + ushort[] Stats = + { + GetStat(p.HP , IV_HP , EV_HP , lv), + GetStat(p.ATK, IV_ATK, EV_ATK, lv), + GetStat(p.DEF, IV_DEF, EV_DEF, lv), + GetStat(p.SPE, IV_SPE, EV_SPE, lv), + GetStat(p.SPA, IV_SPA, EV_SPA, lv), + GetStat(p.SPD, IV_SPD, EV_SPD, lv), + }; + Stats[0] += (ushort)(5 + lv); // HP + return Stats; + } + + protected static ushort GetStat(int BV, int IV, int EV, int LV) + { + EV = (ushort)Math.Sqrt(EV) >> 2; + return (ushort)((((2 * (BV + IV)) + EV) * LV / 100) + 5); + } + + public override int GetMovePP(int move, int ppup) => Math.Min(61, base.GetMovePP(move, ppup)); + + protected string GetString(int Offset, int Count) + { + if (Korean) + return StringConverter.GetString2KOR(Data, Offset, Count); + return StringConverter.GetString1(Data, Offset, Count, Japanese); + } + + private byte[] SetString(string value, int maxLength) + { + if (Korean) + return StringConverter.SetString2KOR(value, maxLength - 1); + return StringConverter.SetString1(value, maxLength - 1, Japanese); + } + + private byte[] GetStringSpecial(string value, int length) + { + byte[] strdata = SetString(value, length); + if (!nick.Any(b => b == 0) || nick[length - 1] != 0x50 || Array.FindIndex(nick, b => b == 0) != strdata.Length - 1) + return strdata; + + int firstInd = Array.FindIndex(nick, b => b == 0); + for (int i = firstInd; i < length - 1; i++) + { + if (nick[i] != 0) + break; + } + + return strdata.Take(strdata.Length - 1).ToArray(); + } + } +}