From e6e3caabb2960d048ca38bfc1268a4c341fda6f5 Mon Sep 17 00:00:00 2001 From: Kaphotics Date: Sun, 26 Jun 2016 14:23:41 -0700 Subject: [PATCH] Sync basic gen3 support Still many things not yet working (item/inventory editing needs new string resources) --- Legality/Tables3.cs | 36 +++ PKHeX.csproj | 2 + PKM/PK3.cs | 17 +- PKM/PKM.cs | 17 +- PKM/PKX.cs | 88 ++++++- PKX/f1-Main.cs | 294 +++++++++++++++++++---- SAV/SAV_SimpleTrainer.Designer.cs | 181 +++++++------- SAV/SAV_SimpleTrainer.cs | 28 ++- Saves/SAV3.cs | 382 ++++++++++++++++++++++++++++++ Saves/SAV4.cs | 1 - Saves/SaveFile.cs | 10 +- Saves/SaveUtil.cs | 36 +++ 12 files changed, 928 insertions(+), 164 deletions(-) create mode 100644 Legality/Tables3.cs create mode 100644 Saves/SAV3.cs diff --git a/Legality/Tables3.cs b/Legality/Tables3.cs new file mode 100644 index 000000000..fb828819e --- /dev/null +++ b/Legality/Tables3.cs @@ -0,0 +1,36 @@ +using System.Linq; + +namespace PKHeX +{ + public static partial class Legal + { + // PKHeX Valid Array Storage + #region DP + internal static readonly ushort[] Pouch_Items_RS = { + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 93, 94, 95, 96, 97, 98, 103, 104, 106, 107, 108, 109, 110, 111, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 254, 255, 256, 257, 258 + }; + internal static readonly ushort[] Pouch_Key_RS = { + 259, 260, 261, 262, 263, 264, 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288 + }; + internal static readonly ushort[] Pouch_TM_RS = { + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, + }; + internal static readonly ushort[] Pouch_HM_RS = { + 339, 340, 341, 342, 343, 344, 345, 346 + }; + internal static readonly ushort[] Pouch_Berries_RS = { + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175 + }; + internal static readonly ushort[] Pouch_Ball_RS = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 + }; + internal static readonly ushort[] Pouch_Key_E = Pouch_Key_RS.Concat(new ushort[] { 375, 376 }).ToArray(); + internal static readonly ushort[] Pouch_Key_FRLG = Pouch_Key_RS.Concat(new ushort[] { 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374 }).ToArray(); + + internal static readonly ushort[] Pouch_TMHM_RS = Pouch_TM_RS.Concat(Pouch_HM_RS).ToArray(); + internal static readonly ushort[] HeldItems_RS = new ushort[1].Concat(Pouch_Items_RS).Concat(Pouch_Ball_RS).Concat(Pouch_Berries_RS).Concat(Pouch_TM_RS).ToArray(); + #endregion + + + } +} diff --git a/PKHeX.csproj b/PKHeX.csproj index bb15760cd..c3c813445 100644 --- a/PKHeX.csproj +++ b/PKHeX.csproj @@ -71,6 +71,7 @@ + @@ -92,6 +93,7 @@ + diff --git a/PKM/PK3.cs b/PKM/PK3.cs index d2e9f9a29..7594f9127 100644 --- a/PKM/PK3.cs +++ b/PKM/PK3.cs @@ -5,6 +5,10 @@ namespace PKHeX { public class PK3 : PKM // 3rd Generation PKM File { + internal static readonly byte[] ExtraBytes = + { + 0x2A, 0x2B + }; public override int SIZE_PARTY => PKX.SIZE_3PARTY; public override int SIZE_STORED => PKX.SIZE_3STORED; public override int Format => 3; @@ -22,7 +26,8 @@ public PK3(byte[] decryptedData = null, string ident = null) public override uint EncryptionConstant { get { return PID; } set { } } public override int Nature { get { return (int)(PID % 25); } set { } } public override int AltForm { get { return -1; } set { } } - public override bool IsNicknamed { get { return Nickname != PKX.getSpeciesName(Species, Language); } set { } } + + public override bool IsNicknamed { get { return PKX.getIsNicknamed(Species, Nickname); } set { } } public override int Gender { get { return PKX.getGender(Species, PID); } set { } } public override int Characteristic => -1; public override int CurrentFriendship { get { return OT_Friendship; } set { OT_Friendship = value; } } @@ -41,7 +46,7 @@ public PK3(byte[] decryptedData = null, string ident = null) if (strdata.Length > 10) Array.Resize(ref strdata, 10); strdata.CopyTo(Data, 0x08); } } - public override int Language { get { return BitConverter.ToUInt16(Data, 0x12); } set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x12); } } + public override int Language { get { return BitConverter.ToUInt16(Data, 0x12) & 0xFF; } set { BitConverter.GetBytes((ushort)(value | 0x200)).CopyTo(Data, 0x12); } } public override string OT_Name { get { return PKX.getG3Str(Data.Skip(0x14).Take(7).ToArray(), Japanese); } set { byte[] strdata = PKX.setG3Str(value, Japanese); @@ -55,7 +60,9 @@ public PK3(byte[] decryptedData = null, string ident = null) #region Block A public override int Species { get { return PKX.getG4Species(BitConverter.ToUInt16(Data, 0x20)); } set { BitConverter.GetBytes((ushort)PKX.getG3Species(value)).CopyTo(Data, 0x20); } } - public override int HeldItem { get { return BitConverter.ToUInt16(Data, 0x22); } set { BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); } } + public override int HeldItem { get { return PKX.getG4Item((ushort)G3Item); } set { } } + public int G3Item { get { return PKX.getG4Item(BitConverter.ToUInt16(Data, 0x22)); } set { BitConverter.GetBytes((ushort) value).CopyTo(Data, 0x22); } } + public override uint EXP { get { return BitConverter.ToUInt32(Data, 0x24); } set { BitConverter.GetBytes(value).CopyTo(Data, 0x24); } } private byte PPUps { get { return Data[0x28]; } set { Data[0x28] = value; } } public override int Move1_PPUps { get { return (PPUps >> 0) & 3; } set { PPUps = (byte)((PPUps & ~(3 << 0)) | value); } } @@ -154,7 +161,7 @@ public PK3(byte[] decryptedData = null, string ident = null) public override byte[] Encrypt() { - throw new NotImplementedException(); + return PKX.encryptArray3(Data); } public override bool getGenderIsValid() { @@ -277,7 +284,7 @@ public PK4 convertToPK4() // Set Final Data pk4.Met_Level = PKX.getLevel(pk4.Species, pk4.EXP); pk4.Gender = PKX.getGender(pk4.Species, pk4.PID); - pk4.IsNicknamed |= pk4.Nickname != PKX.getSpeciesName(pk4.Species, pk4.Language); + pk4.IsNicknamed = IsNicknamed; // Unown Form if (Species == 201) diff --git a/PKM/PKM.cs b/PKM/PKM.cs index c97495089..eabb37437 100644 --- a/PKM/PKM.cs +++ b/PKM/PKM.cs @@ -19,13 +19,20 @@ public abstract class PKM public byte[] DecryptedPartyData => Write().Take(SIZE_PARTY).ToArray(); public byte[] DecryptedBoxData => Write().Take(SIZE_STORED).ToArray(); - public ushort CalculateChecksum() + protected ushort CalculateChecksum() { ushort chk = 0; - for (int i = 8; i < SIZE_STORED; i += 2) // Loop through the entire PK5 - chk += BitConverter.ToUInt16(Data, i); - - return chk; + switch (Format) + { + case 3: + for (int i = 32; i < SIZE_STORED; i += 2) + chk += BitConverter.ToUInt16(Data, i); + return chk; + default: // 4+ + for (int i = 8; i < SIZE_STORED; i += 2) + chk += BitConverter.ToUInt16(Data, i); + return chk; + } } public abstract byte[] Encrypt(); public abstract int Format { get; } diff --git a/PKM/PKX.cs b/PKM/PKX.cs index ce0a66a5b..1df311e9f 100644 --- a/PKM/PKX.cs +++ b/PKM/PKX.cs @@ -176,6 +176,11 @@ internal static string getSpeciesName(int species, int lang) try { return SpeciesLang[lang][species]; } catch { return ""; } } + internal static bool getIsNicknamed(int species, string nick) + { + try { return SpeciesLang.All(list => list[species].ToUpper() != nick); } + catch { return false; } + } internal static readonly PersonalInfo[] Personal = Legal.PersonalAO; // Stat Fetching @@ -1628,7 +1633,7 @@ internal static int getGender(int species, uint PID) new byte[] {0x1a, 0x1a}, // 381 new byte[] {0x02, 0x02}, // 382 new byte[] {0x46, 0x46}, // 383 - new byte[] {0x4c, 0x4c}, // 384 + new byte[] {0x4d, 0x4d}, // 384 new byte[] {0x20, 0x20}, // 385 new byte[] {0x2e, 0x2e}, // 386 }; @@ -2039,5 +2044,86 @@ internal static int getGender(int species, uint PID) new byte[] { }, // Unused new byte[] { 0x74, 0x20, 0x0D, 0x02, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA1, 0x0C, 0x02, 0xE0, 0xFF }, }; + + internal static byte[] decryptArray3(byte[] ekm) + { + if (ekm.Length != SIZE_3PARTY && ekm.Length != SIZE_3STORED) + return null; + + uint PID = BitConverter.ToUInt32(ekm, 0); + uint OID = BitConverter.ToUInt32(ekm, 4); + uint seed = PID ^ OID; + + byte[] xorkey = BitConverter.GetBytes(seed); + for (int i = 32; i < 80; i++) + ekm[i] ^= xorkey[i % 4]; + return shuffleArray3(ekm, PID%24); + } + internal static byte[] shuffleArray3(byte[] data, uint sv) + { + byte[] sdata = new byte[data.Length]; + Array.Copy(data, sdata, 32); // Copy unshuffled bytes + + // Shuffle Away! + for (int block = 0; block < 4; block++) + Array.Copy(data, 32 + 12 * blockPosition[block][sv], sdata, 32 + 12 * block, 12); + + // Fill the Battle Stats back + if (data.Length > SIZE_3STORED) + Array.Copy(data, SIZE_3STORED, sdata, SIZE_3STORED, data.Length - SIZE_3STORED); + + return sdata; + } + internal static byte[] encryptArray3(byte[] pkm) + { + if (pkm.Length != SIZE_3PARTY && pkm.Length != SIZE_3STORED) + return null; + + uint PID = BitConverter.ToUInt32(pkm, 0); + uint OID = BitConverter.ToUInt32(pkm, 4); + uint seed = PID ^ OID; + + byte[] ekm = shuffleArray3(pkm, blockPositionInvert[PID%24]); + byte[] xorkey = BitConverter.GetBytes(seed); + for (int i = 32; i < 80; i++) + ekm[i] ^= xorkey[i % 4]; + return ekm; + } + + internal static ushort getG4Item(ushort g3val) + { + ushort[] arr = + { + 0,1,2,3,4,5,6,7,8,9, + 10,11,12,17,18,19,20,21,22,23, + 24,25,26,27,28,29,30,31,32,33, + 34,35,36,37,38,39,40,41,42,65, + 66,67,68,69,43,44,70,71,72,73, + 74,75,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,45,46,47,48,49,50,51, + 52,53,0xFFFF,55,56,57,58,59,60,0xFFFF, + 63,64,0xFFFF,76,77,78,79,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,80,81,82,83,84,85,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,86,87,0xFFFF,88,89,90,91, + 92,93,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,149,150,151,152,153,154,155, + 156,157,158,159,160,161,162,163,164,165, + 166,167,168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183,201,202, + 203,204,205,206,207,208,0xFFFF,0xFFFF,0xFFFF,213, + 214,215,216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231,232,233, + 234,235,236,237,238,239,240,241,242,243, + 244,245,246,247,248,249,250,251,252,253, + 254,255,256,257,258,259,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF, + 0xFFFF,0xFFFF,0xFFFF,0xFFFF,260,261,262,263,264, + }; + if (g3val > arr.Length) + return 0xFFFF; + return arr[g3val]; + } } } diff --git a/PKX/f1-Main.cs b/PKX/f1-Main.cs index deb0812db..d51c5a9e5 100644 --- a/PKX/f1-Main.cs +++ b/PKX/f1-Main.cs @@ -724,48 +724,38 @@ private void openSAV(byte[] input, string path) // Generational Interface byte[] extraBytes = new byte[1]; Tip1.RemoveAll(); Tip2.RemoveAll(); Tip3.RemoveAll(); // TSV/PSV + + CB_Country.Visible = CB_SubRegion.Visible = CB_3DSReg.Visible = + Label_Country.Visible = Label_SubRegion.Visible = Label_3DSRegion.Visible = SAV.Generation >= 6; + CB_Ability.Visible = TB_AbilityNumber.Visible = SAV.Generation >= 6; + Label_EncryptionConstant.Visible = BTN_RerollEC.Visible = TB_EC.Visible = SAV.Generation >= 6; + GB_nOT.Visible = GB_RelearnMoves.Visible = BTN_History.Visible = BTN_Ribbons.Visible = SAV.Generation >= 6; + PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = SAV.Generation >= 6; + + PB_MarkPentagon.Visible = SAV.Generation == 6; + PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = SAV.Generation == 6; + TB_GameSync.Visible = TB_Secure1.Visible = TB_Secure2.Visible = L_GameSync.Visible = L_Secure1.Visible = L_Secure2.Visible = SAV.Generation == 6; + + CB_Form.Visible = Label_Form.Visible = CHK_AsEgg.Visible = GB_EggConditions.Visible = + Label_MetDate.Visible = CAL_MetDate.Visible = PB_Mark5.Visible = PB_Mark6.Visible = SAV.Generation >= 4; + + DEV_Ability.Enabled = DEV_Ability.Visible = SAV.Generation < 6 || HaX; + TB_AbilityNumber.Visible = SAV.Generation >= 6 && !HaX; + CB_Ability.Visible = SAV.Generation >= 6 && !HaX; + switch (SAV.Generation) { + case 3: + extraBytes = PK3.ExtraBytes; + break; case 4: - TB_GameSync.Visible = TB_Secure1.Visible = TB_Secure2.Visible = false; - L_GameSync.Visible = L_Secure1.Visible = L_Secure2.Visible = false; - CB_Country.Visible = CB_SubRegion.Visible = CB_3DSReg.Visible = false; - Label_Country.Visible = Label_SubRegion.Visible = Label_3DSRegion.Visible = false; - CB_Ability.Visible = TB_AbilityNumber.Visible = false; - GB_nOT.Visible = GB_RelearnMoves.Visible = BTN_History.Visible = BTN_Ribbons.Visible = false; - Label_EncryptionConstant.Visible = BTN_RerollEC.Visible = TB_EC.Visible = false; - PB_MarkPentagon.Visible = false; - PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = false; - - DEV_Ability.Enabled = DEV_Ability.Visible = true; + extraBytes = PK4.ExtraBytes; break; case 5: - TB_GameSync.Visible = TB_Secure1.Visible = TB_Secure2.Visible = false; - L_GameSync.Visible = L_Secure1.Visible = L_Secure2.Visible = false; - CB_Country.Visible = CB_SubRegion.Visible = CB_3DSReg.Visible = false; - Label_Country.Visible = Label_SubRegion.Visible = Label_3DSRegion.Visible = false; - CB_Ability.Visible = TB_AbilityNumber.Visible = false; - GB_nOT.Visible = GB_RelearnMoves.Visible = BTN_History.Visible = BTN_Ribbons.Visible = false; - Label_EncryptionConstant.Visible = BTN_RerollEC.Visible = TB_EC.Visible = false; - PB_MarkPentagon.Visible = false; - PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = false; - - DEV_Ability.Enabled = DEV_Ability.Visible = true; extraBytes = PK5.ExtraBytes; break; case 6: - TB_GameSync.Visible = TB_Secure1.Visible = TB_Secure2.Visible = true; - L_GameSync.Visible = L_Secure1.Visible = L_Secure2.Visible = true; - CB_Country.Visible = CB_SubRegion.Visible = CB_3DSReg.Visible = true; - Label_Country.Visible = Label_SubRegion.Visible = Label_3DSRegion.Visible = true; - DEV_Ability.Enabled = DEV_Ability.Visible = TB_AbilityNumber.Visible = HaX; - GB_nOT.Visible = BTN_History.Visible = true; - Label_EncryptionConstant.Visible = BTN_RerollEC.Visible = TB_EC.Visible = true; - PB_MarkPentagon.Visible = true; - PB_Legal.Visible = PB_WarnMove1.Visible = PB_WarnMove2.Visible = PB_WarnMove3.Visible = PB_WarnMove4.Visible = true; - - CB_Ability.Visible = !HaX; extraBytes = PK6.ExtraBytes; TB_GameSync.Enabled = (SAV as SAV6).GameSyncID != 0; TB_GameSync.Text = (SAV as SAV6).GameSyncID.ToString("X16"); @@ -1017,7 +1007,7 @@ private void populateFilteredDataSources() CB_Ball.DataSource = new BindingSource(BallDataSource.Where(b => b.Value <= SAV.MaxBallID), null); CB_Species.DataSource = new BindingSource(SpeciesDataSource.Where(s => s.Value <= SAV.MaxSpeciesID), null); DEV_Ability.DataSource = new BindingSource(AbilityDataSource.Where(a => a.Value <= SAV.MaxAbilityID), null); - CB_GameOrigin.DataSource = new BindingSource(VersionDataSource.Where(g => g.Value <= SAV.MaxGameID), null); + CB_GameOrigin.DataSource = new BindingSource(VersionDataSource.Where(g => g.Value <= SAV.MaxGameID || SAV.Generation >= 3 && g.Value == 15), null); // Set the Move ComboBoxes too.. foreach (ComboBox cb in new[] { CB_Move1, CB_Move2, CB_Move3, CB_Move4, CB_RelearnMove1, CB_RelearnMove2, CB_RelearnMove3, CB_RelearnMove4 }) @@ -1040,6 +1030,9 @@ public void populateFields(PKM pk, bool focus = true) Util.Alert("PKX File has an invalid checksum."); switch (pkm.Format) { + case 3: + populateFieldsPK3(pkm as PK3); + break; case 4: populateFieldsPK4(pkm as PK4); break; @@ -1077,6 +1070,97 @@ public void populateFields(PKM pk, bool focus = true) dragout.Image = pk.Sprite; updateLegality(); } + private void populateFieldsPK3(PK3 pk3) + { + // Do first + pk3.Stat_Level = PKX.getLevel(pk3.Species, pk3.EXP); + if (pk3.Stat_Level == 100) + pk3.EXP = PKX.getEXP(pk3.Stat_Level, pk3.Species); + + CB_Species.SelectedValue = pk3.Species; + TB_Level.Text = pk3.Stat_Level.ToString(); + TB_EXP.Text = pk3.EXP.ToString(); + + // Load rest + CHK_Fateful.Checked = pk3.FatefulEncounter; + CHK_IsEgg.Checked = pk3.IsEgg; + CHK_Nicknamed.Checked = pk3.IsNicknamed; + Label_OTGender.Text = gendersymbols[pk3.OT_Gender]; + Label_OTGender.ForeColor = pk3.OT_Gender == 1 ? Color.Red : Color.Blue; + TB_PID.Text = pk3.PID.ToString("X8"); + CB_HeldItem.SelectedValue = pk3.HeldItem; + setAbilityList(TB_AbilityNumber, pk3.Species, CB_Ability, CB_Form); + DEV_Ability.SelectedValue = pk3.Ability; + CB_Nature.SelectedValue = pk3.Nature; + TB_TID.Text = pk3.TID.ToString("00000"); + TB_SID.Text = pk3.SID.ToString("00000"); + TB_Nickname.Text = pk3.Nickname; + TB_OT.Text = pk3.OT_Name; + TB_Friendship.Text = pk3.CurrentFriendship.ToString(); + GB_OT.BackgroundImage = null; + CB_Language.SelectedValue = pk3.Language; + CB_GameOrigin.SelectedValue = pk3.Version; + CB_EncounterType.SelectedValue = pk3.Gen4 ? pk3.EncounterType : 0; + CB_Ball.SelectedValue = pk3.Ball; + + CB_MetLocation.SelectedValue = pk3.Met_Location; + + TB_MetLevel.Text = pk3.Met_Level.ToString(); + + // Reset Label and ComboBox visibility, as well as non-data checked status. + Label_PKRS.Visible = CB_PKRSStrain.Visible = CHK_Infected.Checked = pk3.PKRS_Strain != 0; + Label_PKRSdays.Visible = CB_PKRSDays.Visible = pk3.PKRS_Days != 0; + + // Set SelectedIndexes for PKRS + CB_PKRSStrain.SelectedIndex = pk3.PKRS_Strain; + CHK_Cured.Checked = pk3.PKRS_Strain > 0 && pk3.PKRS_Days == 0; + CB_PKRSDays.SelectedIndex = Math.Min(CB_PKRSDays.Items.Count - 1, pk3.PKRS_Days); // to strip out bad hacked 'rus + + TB_Cool.Text = pk3.CNT_Cool.ToString(); + TB_Beauty.Text = pk3.CNT_Beauty.ToString(); + TB_Cute.Text = pk3.CNT_Cute.ToString(); + TB_Smart.Text = pk3.CNT_Smart.ToString(); + TB_Tough.Text = pk3.CNT_Tough.ToString(); + TB_Sheen.Text = pk3.CNT_Sheen.ToString(); + + TB_HPIV.Text = pk3.IV_HP.ToString(); + TB_ATKIV.Text = pk3.IV_ATK.ToString(); + TB_DEFIV.Text = pk3.IV_DEF.ToString(); + TB_SPEIV.Text = pk3.IV_SPE.ToString(); + TB_SPAIV.Text = pk3.IV_SPA.ToString(); + TB_SPDIV.Text = pk3.IV_SPD.ToString(); + CB_HPType.SelectedValue = pk3.HPType; + + TB_HPEV.Text = pk3.EV_HP.ToString(); + TB_ATKEV.Text = pk3.EV_ATK.ToString(); + TB_DEFEV.Text = pk3.EV_DEF.ToString(); + TB_SPEEV.Text = pk3.EV_SPE.ToString(); + TB_SPAEV.Text = pk3.EV_SPA.ToString(); + TB_SPDEV.Text = pk3.EV_SPD.ToString(); + + CB_Move1.SelectedValue = pk3.Move1; + CB_Move2.SelectedValue = pk3.Move2; + CB_Move3.SelectedValue = pk3.Move3; + CB_Move4.SelectedValue = pk3.Move4; + CB_PPu1.SelectedIndex = pk3.Move1_PPUps; + CB_PPu2.SelectedIndex = pk3.Move2_PPUps; + CB_PPu3.SelectedIndex = pk3.Move3_PPUps; + CB_PPu4.SelectedIndex = pk3.Move4_PPUps; + TB_PP1.Text = pk3.Move1_PP.ToString(); + TB_PP2.Text = pk3.Move2_PP.ToString(); + TB_PP3.Text = pk3.Move3_PP.ToString(); + TB_PP4.Text = pk3.Move4_PP.ToString(); + + // Load Extrabyte Value + TB_ExtraByte.Text = pk3.Data[Convert.ToInt32(CB_ExtraBytes.Text, 16)].ToString(); + + updateStats(); + setIsShiny(); + + TB_EXP.Text = pk3.EXP.ToString(); + Label_Gender.Text = gendersymbols[pk3.Gender]; + Label_Gender.ForeColor = pk3.Gender == 2 ? Label_Species.ForeColor : (pk3.Gender == 1 ? Color.Red : Color.Blue); + } private void populateFieldsPK4(PK4 pk4) { // Do first @@ -1493,6 +1577,27 @@ internal static void setAbilityList(MaskedTextBox tb_abil, int species, ComboBox cb_abil.SelectedIndex = newabil < 3 ? newabil : 0; } + private void setAbilityList3(MaskedTextBox tb_abil, int species, ComboBox cb_abil, ComboBox cb_forme) + { + if (!fieldsInitialized && string.IsNullOrWhiteSpace(tb_abil.Text)) + return; + int newabil = Convert.ToInt16(tb_abil.Text) >> 1; + + int form = cb_forme.SelectedIndex; + byte[] abils = PKX.Gen3Abilities[Util.getIndex(CB_Species)]; + if (abils[0] == 76) abils[0] = 77; // Air Lock +1 + if (abils[1] == 76) abils[1] = 77; // Air Lock +1 + + // Build Ability List + List ability_list = new List + { + abilitylist[abils[0]] + " (1)", + abilitylist[abils[1]] + " (2)", + }; + cb_abil.DataSource = ability_list; + + cb_abil.SelectedIndex = newabil < 3 ? newabil : 0; + } // PKX Data Calculation Functions // private void setIsShiny() { @@ -1789,7 +1894,10 @@ private void updateIVs(object sender, EventArgs e) TB_IVTotal.Text = pkm.IVs.Sum().ToString(); - L_Characteristic.Text = characteristics[pkm.Characteristic]; + int characteristic = pkm.Characteristic; + L_Characteristic.Visible = characteristic > -1; + if (characteristic > -1) + L_Characteristic.Text = characteristics[pkm.Characteristic]; updateStats(); } private void updateEVs(object sender, EventArgs e) @@ -2232,27 +2340,26 @@ private void updateNatureModification(object sender, EventArgs e) private void updateNickname(object sender, EventArgs e) { if (fieldsInitialized && ModifierKeys == Keys.Control && sender != null) // Import Showdown - { - clickShowdownImportPK6(sender, e); - return; - } + { clickShowdownImportPK6(sender, e); return; } if (fieldsInitialized && ModifierKeys == Keys.Alt && sender != null) // Export Showdown - { - clickShowdownExportPK6(sender, e); - return; - } - if (!fieldsInitialized || CHK_Nicknamed.Checked) return; + { clickShowdownExportPK6(sender, e); return; } + if (!fieldsInitialized || CHK_Nicknamed.Checked) + return; // Fetch Current Species and set it as Nickname Text int species = Util.getIndex(CB_Species); - if (species == 0 || species > 721) + if (species < 1 || species > SAV.MaxSpeciesID) TB_Nickname.Text = ""; else { // get language int lang = Util.getIndex(CB_Language); if (CHK_IsEgg.Checked) species = 0; // Set species to 0 to get the egg name. - TB_Nickname.Text = PKX.getSpeciesName(CHK_IsEgg.Checked ? 0 : species, lang); + string nick = PKX.getSpeciesName(CHK_IsEgg.Checked ? 0 : species, lang); + + if (SAV.Generation < 5) // All caps GenIV and previous + nick = nick.ToUpper(); + TB_Nickname.Text = nick; } } private void updateNicknameClick(object sender, MouseEventArgs e) @@ -2666,6 +2773,9 @@ public PKM preparePKM(bool click = true) PKM pk = null; switch (pkm.Format) { + case 3: + pk = preparePK3(); + break; case 4: pk = preparePK4(); break; @@ -2678,6 +2788,95 @@ public PKM preparePKM(bool click = true) } return pk?.Clone(); } + private PK3 preparePK3() + { + PK3 pk3 = pkm as PK3; + if (pk3 == null) + return null; + + pk3.Species = Util.getIndex(CB_Species); + pk3.G3Item = Util.getIndex(CB_HeldItem); + pk3.TID = Util.ToInt32(TB_TID.Text); + pk3.SID = Util.ToInt32(TB_SID.Text); + pk3.EXP = Util.ToUInt32(TB_EXP.Text); + pk3.PID = Util.getHEXval(TB_PID.Text); + pk3.Ability = (byte)Util.getIndex(DEV_Ability); + //pk4.Ability = (byte)Array.IndexOf(abilitylist, CB_Ability.Text.Remove(CB_Ability.Text.Length - 4)); + + pk3.FatefulEncounter = CHK_Fateful.Checked; + pk3.Gender = PKX.getGender(Label_Gender.Text); + pk3.EV_HP = Util.ToInt32(TB_HPEV.Text); + pk3.EV_ATK = Util.ToInt32(TB_ATKEV.Text); + pk3.EV_DEF = Util.ToInt32(TB_DEFEV.Text); + pk3.EV_SPE = Util.ToInt32(TB_SPEEV.Text); + pk3.EV_SPA = Util.ToInt32(TB_SPAEV.Text); + pk3.EV_SPD = Util.ToInt32(TB_SPDEV.Text); + + pk3.CNT_Cool = Util.ToInt32(TB_Cool.Text); + pk3.CNT_Beauty = Util.ToInt32(TB_Beauty.Text); + pk3.CNT_Cute = Util.ToInt32(TB_Cute.Text); + pk3.CNT_Smart = Util.ToInt32(TB_Smart.Text); + pk3.CNT_Tough = Util.ToInt32(TB_Tough.Text); + pk3.CNT_Sheen = Util.ToInt32(TB_Sheen.Text); + + pk3.PKRS_Days = CB_PKRSDays.SelectedIndex; + pk3.PKRS_Strain = CB_PKRSStrain.SelectedIndex; + pk3.Nickname = TB_Nickname.Text; + pk3.Move1 = Util.getIndex(CB_Move1); + pk3.Move2 = Util.getIndex(CB_Move2); + pk3.Move3 = Util.getIndex(CB_Move3); + pk3.Move4 = Util.getIndex(CB_Move4); + pk3.Move1_PP = Util.getIndex(CB_Move1) > 0 ? Util.ToInt32(TB_PP1.Text) : 0; + pk3.Move2_PP = Util.getIndex(CB_Move2) > 0 ? Util.ToInt32(TB_PP2.Text) : 0; + pk3.Move3_PP = Util.getIndex(CB_Move3) > 0 ? Util.ToInt32(TB_PP3.Text) : 0; + pk3.Move4_PP = Util.getIndex(CB_Move4) > 0 ? Util.ToInt32(TB_PP4.Text) : 0; + pk3.Move1_PPUps = Util.getIndex(CB_Move1) > 0 ? CB_PPu1.SelectedIndex : 0; + pk3.Move2_PPUps = Util.getIndex(CB_Move2) > 0 ? CB_PPu2.SelectedIndex : 0; + pk3.Move3_PPUps = Util.getIndex(CB_Move3) > 0 ? CB_PPu3.SelectedIndex : 0; + pk3.Move4_PPUps = Util.getIndex(CB_Move4) > 0 ? CB_PPu4.SelectedIndex : 0; + + pk3.IV_HP = Util.ToInt32(TB_HPIV.Text); + pk3.IV_ATK = Util.ToInt32(TB_ATKIV.Text); + pk3.IV_DEF = Util.ToInt32(TB_DEFIV.Text); + pk3.IV_SPE = Util.ToInt32(TB_SPEIV.Text); + pk3.IV_SPA = Util.ToInt32(TB_SPAIV.Text); + pk3.IV_SPD = Util.ToInt32(TB_SPDIV.Text); + pk3.IsEgg = CHK_IsEgg.Checked; + pk3.IsNicknamed = CHK_Nicknamed.Checked; + + pk3.OT_Name = TB_OT.Text; + pk3.CurrentFriendship = Util.ToInt32(TB_Friendship.Text); + + pk3.Ball = Util.getIndex(CB_Ball); + pk3.Met_Level = Util.ToInt32(TB_MetLevel.Text); + pk3.OT_Gender = PKX.getGender(Label_OTGender.Text); + pk3.Version = Util.getIndex(CB_GameOrigin); + pk3.Language = Util.getIndex(CB_Language); + + pk3.Met_Location = Util.getIndex(CB_MetLocation); + + // Toss in Party Stats + Array.Resize(ref pk3.Data, pk3.SIZE_PARTY); + pk3.Stat_Level = Util.ToInt32(TB_Level.Text); + pk3.Stat_HPCurrent = Util.ToInt32(Stat_HP.Text); + pk3.Stat_HPMax = Util.ToInt32(Stat_HP.Text); + pk3.Stat_ATK = Util.ToInt32(Stat_ATK.Text); + pk3.Stat_DEF = Util.ToInt32(Stat_DEF.Text); + pk3.Stat_SPE = Util.ToInt32(Stat_SPE.Text); + pk3.Stat_SPA = Util.ToInt32(Stat_SPA.Text); + pk3.Stat_SPD = Util.ToInt32(Stat_SPD.Text); + + if (HaX) + { + pk3.Stat_Level = (byte)Math.Min(Convert.ToInt32(MT_Level.Text), byte.MaxValue); + } + + // Fix Moves if a slot is empty + pk3.FixMoves(); + + pk3.RefreshChecksum(); + return pk3; + } private PK4 preparePK4() { PK4 pk4 = pkm as PK4; @@ -2692,8 +2891,7 @@ private PK4 preparePK4() pk4.PID = Util.getHEXval(TB_PID.Text); pk4.Ability = (byte)Util.getIndex(DEV_Ability); //pk4.Ability = (byte)Array.IndexOf(abilitylist, CB_Ability.Text.Remove(CB_Ability.Text.Length - 4)); - - pk4.Nature = (byte)Util.getIndex(CB_Nature); + pk4.FatefulEncounter = CHK_Fateful.Checked; pk4.Gender = PKX.getGender(Label_Gender.Text); pk4.AltForm = (MT_Form.Enabled ? Convert.ToInt32(MT_Form.Text) : CB_Form.SelectedIndex) & 0x1F; diff --git a/SAV/SAV_SimpleTrainer.Designer.cs b/SAV/SAV_SimpleTrainer.Designer.cs index e95c93130..5ccfc3300 100644 --- a/SAV/SAV_SimpleTrainer.Designer.cs +++ b/SAV/SAV_SimpleTrainer.Designer.cs @@ -28,11 +28,11 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.GB_Adventure = new System.Windows.Forms.GroupBox(); this.L_Started = new System.Windows.Forms.Label(); this.CAL_AdventureStartDate = new System.Windows.Forms.DateTimePicker(); this.CAL_HoFDate = new System.Windows.Forms.DateTimePicker(); - this.L_LastSaved = new System.Windows.Forms.Label(); + this.L_Fame = new System.Windows.Forms.Label(); this.MT_Seconds = new System.Windows.Forms.MaskedTextBox(); this.MT_Hours = new System.Windows.Forms.MaskedTextBox(); this.L_Seconds = new System.Windows.Forms.Label(); @@ -62,8 +62,8 @@ private void InitializeComponent() this.L_X = new System.Windows.Forms.Label(); this.B_Save = new System.Windows.Forms.Button(); this.B_Cancel = new System.Windows.Forms.Button(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.GB_Trainer = new System.Windows.Forms.GroupBox(); + this.GB_Badges = new System.Windows.Forms.GroupBox(); this.CHK_H8 = new System.Windows.Forms.CheckBox(); this.CHK_H7 = new System.Windows.Forms.CheckBox(); this.CHK_H6 = new System.Windows.Forms.CheckBox(); @@ -80,36 +80,36 @@ private void InitializeComponent() this.CHK_3 = new System.Windows.Forms.CheckBox(); this.CHK_2 = new System.Windows.Forms.CheckBox(); this.CHK_1 = new System.Windows.Forms.CheckBox(); - this.groupBox2.SuspendLayout(); + this.GB_Adventure.SuspendLayout(); this.GB_Map.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.NUD_Z)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_M)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_Y)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_X)).BeginInit(); - this.groupBox1.SuspendLayout(); - this.groupBox3.SuspendLayout(); + this.GB_Trainer.SuspendLayout(); + this.GB_Badges.SuspendLayout(); this.SuspendLayout(); // - // groupBox2 + // GB_Adventure // - this.groupBox2.Controls.Add(this.L_Started); - this.groupBox2.Controls.Add(this.CAL_AdventureStartDate); - this.groupBox2.Controls.Add(this.CAL_HoFDate); - this.groupBox2.Controls.Add(this.L_LastSaved); - this.groupBox2.Controls.Add(this.MT_Seconds); - this.groupBox2.Controls.Add(this.MT_Hours); - this.groupBox2.Controls.Add(this.L_Seconds); - this.groupBox2.Controls.Add(this.L_Hours); - this.groupBox2.Controls.Add(this.MT_Minutes); - this.groupBox2.Controls.Add(this.L_Minutes); - this.groupBox2.Controls.Add(this.CAL_AdventureStartTime); - this.groupBox2.Controls.Add(this.CAL_HoFTime); - this.groupBox2.Location = new System.Drawing.Point(12, 98); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(200, 151); - this.groupBox2.TabIndex = 70; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "Adventure Info"; + this.GB_Adventure.Controls.Add(this.L_Started); + this.GB_Adventure.Controls.Add(this.CAL_AdventureStartDate); + this.GB_Adventure.Controls.Add(this.CAL_HoFDate); + this.GB_Adventure.Controls.Add(this.L_Fame); + this.GB_Adventure.Controls.Add(this.MT_Seconds); + this.GB_Adventure.Controls.Add(this.MT_Hours); + this.GB_Adventure.Controls.Add(this.L_Seconds); + this.GB_Adventure.Controls.Add(this.L_Hours); + this.GB_Adventure.Controls.Add(this.MT_Minutes); + this.GB_Adventure.Controls.Add(this.L_Minutes); + this.GB_Adventure.Controls.Add(this.CAL_AdventureStartTime); + this.GB_Adventure.Controls.Add(this.CAL_HoFTime); + this.GB_Adventure.Location = new System.Drawing.Point(11, 98); + this.GB_Adventure.Name = "GB_Adventure"; + this.GB_Adventure.Size = new System.Drawing.Size(200, 151); + this.GB_Adventure.TabIndex = 70; + this.GB_Adventure.TabStop = false; + this.GB_Adventure.Text = "Adventure Info"; // // L_Started // @@ -141,14 +141,14 @@ private void InitializeComponent() this.CAL_HoFDate.TabIndex = 31; this.CAL_HoFDate.Value = new System.DateTime(2000, 1, 1, 0, 0, 0, 0); // - // L_LastSaved + // L_Fame // - this.L_LastSaved.Location = new System.Drawing.Point(4, 107); - this.L_LastSaved.Name = "L_LastSaved"; - this.L_LastSaved.Size = new System.Drawing.Size(80, 20); - this.L_LastSaved.TabIndex = 32; - this.L_LastSaved.Text = "Hall of Fame:"; - this.L_LastSaved.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.L_Fame.Location = new System.Drawing.Point(4, 107); + this.L_Fame.Name = "L_Fame"; + this.L_Fame.Size = new System.Drawing.Size(80, 20); + this.L_Fame.TabIndex = 32; + this.L_Fame.Text = "Hall of Fame:"; + this.L_Fame.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // // MT_Seconds // @@ -340,7 +340,7 @@ private void InitializeComponent() this.GB_Map.Controls.Add(this.L_CurrentMap); this.GB_Map.Controls.Add(this.L_Z); this.GB_Map.Controls.Add(this.L_X); - this.GB_Map.Location = new System.Drawing.Point(218, 100); + this.GB_Map.Location = new System.Drawing.Point(217, 100); this.GB_Map.Name = "GB_Map"; this.GB_Map.Size = new System.Drawing.Size(157, 125); this.GB_Map.TabIndex = 71; @@ -438,7 +438,7 @@ private void InitializeComponent() // // B_Save // - this.B_Save.Location = new System.Drawing.Point(300, 226); + this.B_Save.Location = new System.Drawing.Point(299, 226); this.B_Save.Name = "B_Save"; this.B_Save.Size = new System.Drawing.Size(75, 23); this.B_Save.TabIndex = 73; @@ -448,7 +448,7 @@ private void InitializeComponent() // // B_Cancel // - this.B_Cancel.Location = new System.Drawing.Point(219, 226); + this.B_Cancel.Location = new System.Drawing.Point(218, 226); this.B_Cancel.Name = "B_Cancel"; this.B_Cancel.Size = new System.Drawing.Size(75, 23); this.B_Cancel.TabIndex = 72; @@ -456,49 +456,49 @@ private void InitializeComponent() this.B_Cancel.UseVisualStyleBackColor = true; this.B_Cancel.Click += new System.EventHandler(this.B_Cancel_Click); // - // groupBox1 + // GB_Trainer // - this.groupBox1.Controls.Add(this.TB_OTName); - this.groupBox1.Controls.Add(this.B_MaxCash); - this.groupBox1.Controls.Add(this.MT_SID); - this.groupBox1.Controls.Add(this.MT_TID); - this.groupBox1.Controls.Add(this.L_Money); - this.groupBox1.Controls.Add(this.L_SID); - this.groupBox1.Controls.Add(this.CB_Gender); - this.groupBox1.Controls.Add(this.MT_Money); - this.groupBox1.Controls.Add(this.L_TID); - this.groupBox1.Controls.Add(this.L_TrainerName); - this.groupBox1.Location = new System.Drawing.Point(12, 12); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(200, 82); - this.groupBox1.TabIndex = 74; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Trainer"; + this.GB_Trainer.Controls.Add(this.TB_OTName); + this.GB_Trainer.Controls.Add(this.B_MaxCash); + this.GB_Trainer.Controls.Add(this.MT_SID); + this.GB_Trainer.Controls.Add(this.MT_TID); + this.GB_Trainer.Controls.Add(this.L_Money); + this.GB_Trainer.Controls.Add(this.L_SID); + this.GB_Trainer.Controls.Add(this.CB_Gender); + this.GB_Trainer.Controls.Add(this.MT_Money); + this.GB_Trainer.Controls.Add(this.L_TID); + this.GB_Trainer.Controls.Add(this.L_TrainerName); + this.GB_Trainer.Location = new System.Drawing.Point(11, 12); + this.GB_Trainer.Name = "GB_Trainer"; + this.GB_Trainer.Size = new System.Drawing.Size(200, 82); + this.GB_Trainer.TabIndex = 74; + this.GB_Trainer.TabStop = false; + this.GB_Trainer.Text = "Trainer"; // - // groupBox3 + // GB_Badges // - this.groupBox3.Controls.Add(this.CHK_H8); - this.groupBox3.Controls.Add(this.CHK_H7); - this.groupBox3.Controls.Add(this.CHK_H6); - this.groupBox3.Controls.Add(this.CHK_H5); - this.groupBox3.Controls.Add(this.CHK_H4); - this.groupBox3.Controls.Add(this.CHK_H3); - this.groupBox3.Controls.Add(this.CHK_H2); - this.groupBox3.Controls.Add(this.CHK_H1); - this.groupBox3.Controls.Add(this.CHK_8); - this.groupBox3.Controls.Add(this.CHK_7); - this.groupBox3.Controls.Add(this.CHK_6); - this.groupBox3.Controls.Add(this.CHK_5); - this.groupBox3.Controls.Add(this.CHK_4); - this.groupBox3.Controls.Add(this.CHK_3); - this.groupBox3.Controls.Add(this.CHK_2); - this.groupBox3.Controls.Add(this.CHK_1); - this.groupBox3.Location = new System.Drawing.Point(219, 12); - this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(157, 82); - this.groupBox3.TabIndex = 75; - this.groupBox3.TabStop = false; - this.groupBox3.Text = "Badges"; + this.GB_Badges.Controls.Add(this.CHK_H8); + this.GB_Badges.Controls.Add(this.CHK_H7); + this.GB_Badges.Controls.Add(this.CHK_H6); + this.GB_Badges.Controls.Add(this.CHK_H5); + this.GB_Badges.Controls.Add(this.CHK_H4); + this.GB_Badges.Controls.Add(this.CHK_H3); + this.GB_Badges.Controls.Add(this.CHK_H2); + this.GB_Badges.Controls.Add(this.CHK_H1); + this.GB_Badges.Controls.Add(this.CHK_8); + this.GB_Badges.Controls.Add(this.CHK_7); + this.GB_Badges.Controls.Add(this.CHK_6); + this.GB_Badges.Controls.Add(this.CHK_5); + this.GB_Badges.Controls.Add(this.CHK_4); + this.GB_Badges.Controls.Add(this.CHK_3); + this.GB_Badges.Controls.Add(this.CHK_2); + this.GB_Badges.Controls.Add(this.CHK_1); + this.GB_Badges.Location = new System.Drawing.Point(218, 12); + this.GB_Badges.Name = "GB_Badges"; + this.GB_Badges.Size = new System.Drawing.Size(157, 82); + this.GB_Badges.TabIndex = 75; + this.GB_Badges.TabStop = false; + this.GB_Badges.Text = "Badges"; // // CHK_H8 // @@ -664,37 +664,38 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(386, 261); - this.Controls.Add(this.groupBox3); - this.Controls.Add(this.groupBox1); + this.ClientSize = new System.Drawing.Size(384, 261); + this.Controls.Add(this.GB_Badges); + this.Controls.Add(this.GB_Trainer); this.Controls.Add(this.B_Save); this.Controls.Add(this.B_Cancel); this.Controls.Add(this.GB_Map); - this.Controls.Add(this.groupBox2); + this.Controls.Add(this.GB_Adventure); + this.MinimumSize = new System.Drawing.Size(400, 300); this.Name = "SAV_SimpleTrainer"; this.Text = "Trainer Data Editor"; - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); + this.GB_Adventure.ResumeLayout(false); + this.GB_Adventure.PerformLayout(); this.GB_Map.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.NUD_Z)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_M)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_Y)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.NUD_X)).EndInit(); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.groupBox3.ResumeLayout(false); - this.groupBox3.PerformLayout(); + this.GB_Trainer.ResumeLayout(false); + this.GB_Trainer.PerformLayout(); + this.GB_Badges.ResumeLayout(false); + this.GB_Badges.PerformLayout(); this.ResumeLayout(false); } #endregion - private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox GB_Adventure; private System.Windows.Forms.Label L_Started; private System.Windows.Forms.DateTimePicker CAL_AdventureStartDate; private System.Windows.Forms.DateTimePicker CAL_HoFDate; - private System.Windows.Forms.Label L_LastSaved; + private System.Windows.Forms.Label L_Fame; private System.Windows.Forms.MaskedTextBox MT_Seconds; private System.Windows.Forms.MaskedTextBox MT_Hours; private System.Windows.Forms.Label L_Seconds; @@ -724,8 +725,8 @@ private void InitializeComponent() private System.Windows.Forms.Label L_X; private System.Windows.Forms.Button B_Save; private System.Windows.Forms.Button B_Cancel; - private System.Windows.Forms.GroupBox groupBox1; - private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.GroupBox GB_Trainer; + private System.Windows.Forms.GroupBox GB_Badges; private System.Windows.Forms.CheckBox CHK_8; private System.Windows.Forms.CheckBox CHK_7; private System.Windows.Forms.CheckBox CHK_6; diff --git a/SAV/SAV_SimpleTrainer.cs b/SAV/SAV_SimpleTrainer.cs index 4f19aa6c7..59fd633e6 100644 --- a/SAV/SAV_SimpleTrainer.cs +++ b/SAV/SAV_SimpleTrainer.cs @@ -20,6 +20,20 @@ public SAV_SimpleTrainer() MT_TID.Text = SAV.TID.ToString("00000"); MT_SID.Text = SAV.SID.ToString("00000"); MT_Money.Text = SAV.Money.ToString(); + MT_Hours.Text = SAV.PlayedHours.ToString(); + MT_Minutes.Text = SAV.PlayedMinutes.ToString(); + MT_Seconds.Text = SAV.PlayedSeconds.ToString(); + + if (SAV is SAV3) + { + GB_Map.Visible = false; + GB_Badges.Visible = false; // todo + + L_Started.Visible = L_Fame.Visible = false; + CAL_AdventureStartDate.Visible = CAL_HoFDate.Visible = false; + CAL_AdventureStartTime.Visible = CAL_HoFTime.Visible = false; + return; + } int badgeval = 0; if (SAV is SAV4) @@ -54,9 +68,6 @@ public SAV_SimpleTrainer() cba[i].Checked = (badgeval & 1 << i) != 0; } - MT_Hours.Text = SAV.PlayedHours.ToString(); - MT_Minutes.Text = SAV.PlayedMinutes.ToString(); - MT_Seconds.Text = SAV.PlayedSeconds.ToString(); CAL_HoFDate.Value = new DateTime(2000, 1, 1).AddSeconds(SAV.SecondsToFame); CAL_HoFTime.Value = new DateTime(2000, 1, 1).AddSeconds(SAV.SecondsToFame % 86400); CAL_AdventureStartDate.Value = new DateTime(2000, 1, 1).AddSeconds(SAV.SecondsToStart); @@ -81,6 +92,10 @@ private void B_Save_Click(object sender, EventArgs e) SAV.SID = (ushort)Util.ToUInt32(MT_SID.Text); SAV.Money = Util.ToUInt32(MT_Money.Text); + SAV.PlayedHours = ushort.Parse(MT_Hours.Text); + SAV.PlayedMinutes = ushort.Parse(MT_Minutes.Text) % 60; + SAV.PlayedSeconds = ushort.Parse(MT_Seconds.Text) % 60; + // Copy Badges int badgeval = 0; for (int i = 0; i < cba.Length; i++) @@ -108,12 +123,7 @@ private void B_Save_Click(object sender, EventArgs e) s.Y = (int)NUD_Y.Value; s.Badges = badgeval & 0xFF; } - - // Save PlayTime - SAV.PlayedHours = ushort.Parse(MT_Hours.Text); - SAV.PlayedMinutes = ushort.Parse(MT_Minutes.Text) % 60; - SAV.PlayedSeconds = ushort.Parse(MT_Seconds.Text) % 60; - + SAV.SecondsToStart = getSeconds(CAL_AdventureStartDate, CAL_AdventureStartTime); SAV.SecondsToFame = getSeconds(CAL_HoFDate, CAL_HoFTime); diff --git a/Saves/SAV3.cs b/Saves/SAV3.cs new file mode 100644 index 000000000..4ddd69c0d --- /dev/null +++ b/Saves/SAV3.cs @@ -0,0 +1,382 @@ +using System; +using System.Linq; + +namespace PKHeX +{ + public sealed class SAV3 : SaveFile + { + public override string BAKName => $"{FileName} [{OT} ({Version})" +/* - {LastSavedTime}*/ "].bak"; + public override string Filter => "Main SAV|*.*"; + public override string Extension => ""; + + /* SAV3 Structure: + * 0xE000 per save file + * 14 blocks @ 0x1000 each. + * Blocks do not use all 0x1000 bytes allocated. + * Via: http://bulbapedia.bulbagarden.net/wiki/Save_data_structure_in_Generation_III + */ + private readonly int[] chunkLength = + { + 0xf2c, // 0 | Trainer info + 0xf80, // 1 | Team / items + 0xf80, // 2 | Unknown + 0xf80, // 3 | Unknown + 0xf08, // 4 | Rival info + 0xf80, // 5 | PC Block 0 + 0xf80, // 6 | PC Block 1 + 0xf80, // 7 | PC Block 2 + 0xf80, // 8 | PC Block 3 + 0xf80, // 9 | PC Block 4 + 0xf80, // A | PC Block 5 + 0xf80, // B | PC Block 6 + 0xf80, // C | PC Block 7 + 0x7d0 // D | PC Block 8 + }; + + public SAV3(byte[] data = null) + { + Data = data == null ? new byte[SaveUtil.SIZE_G3RAW] : (byte[])data.Clone(); + BAK = (byte[])Data.Clone(); + Exportable = !Data.SequenceEqual(new byte[Data.Length]); + + BlockOrder = new int[14]; + ActiveSAV = SaveUtil.SIZE_G3RAWHALF == data.Length || BitConverter.ToUInt32(Data, 0xFFC) > BitConverter.ToUInt32(Data, 0xEFFC) ? 0 : 1; + for (int i = 0; i < 14; i++) + BlockOrder[i] = BitConverter.ToInt16(Data, ABO + i*0x1000 + 0xFF4); + + // Detect RS/E/FRLG + // Section 0 stores Game Code @ 0x00AC; 0 for RS, 1 for FRLG, else for Emerald + Trainer1 = Array.IndexOf(BlockOrder, 0)*0x1000 + ABO; + Party = Array.IndexOf(BlockOrder, 1)*0x1000 + ABO; + Block2 = Array.IndexOf(BlockOrder, 2)*0x1000 + ABO; + Block3 = Array.IndexOf(BlockOrder, 3)*0x1000 + ABO; + + uint GameCode = BitConverter.ToUInt32(Data, ABO + Array.IndexOf(BlockOrder, 0)*0x1000); + SaveVersion = GameCode < 2 ? (int)GameCode : 2; + + // Set up PC data buffer beyond end of save file. + Box = Data.Length; + Array.Resize(ref Data, Data.Length + 0x10000); // More than enough empty space. + + // Copy chunk to the allocated location + for (int i = 5; i < 14; i++) + { + int blockIndex = Array.IndexOf(BlockOrder, i); + if (blockIndex == -1) // block empty + continue; + Array.Copy(Data, blockIndex * 0x1000 + ABO, Data, Box + (i - 5)*0xF80, chunkLength[i]); + } + + HeldItems = Legal.HeldItems_RS; + + if (!Exportable) + resetBoxes(); + } + + private readonly int SaveVersion; + private readonly int ActiveSAV; + private int ABO => ActiveSAV*0xE000; + private readonly int[] BlockOrder; + + // Configuration + public override byte[] BAK { get; } + public override bool Exportable { get; } + public override SaveFile Clone() { return new SAV3(Data.Take(Box).ToArray()); } + + public override int SIZE_STORED => PKX.SIZE_3STORED; + public override int SIZE_PARTY => PKX.SIZE_3PARTY; + public override PKM BlankPKM => new PK3(); + protected override Type PKMType => typeof(PK3); + + public override int MaxMoveID => 354; + public override int MaxSpeciesID => 386; + public override int MaxAbilityID => 77; + public override int MaxItemID => 374; + public override int MaxBallID => 0xC; + public override int MaxGameID => 5; + + public override int BoxCount => 14; + public override int MaxEV => 252; + public override int Generation => 3; + protected override int GiftCountMax => 1; + public override int OTLength => 8; + public override int NickLength => 10; + + private int Block2, Block3; + + // Checksums + protected override void setChecksums() + { + for (int i = 0; i < 14; i++) + { + byte[] chunk = Data.Skip(ABO + i*0x1000).Take(chunkLength[BlockOrder[i]]).ToArray(); + ushort chk = SaveUtil.check32(chunk); + BitConverter.GetBytes(chk).CopyTo(Data, ABO + i + 0xFF4); + } + } + public override bool ChecksumsValid + { + get + { + for (int i = 0; i < 14; i++) + { + byte[] chunk = Data.Skip(ABO + i * 0x1000).Take(chunkLength[BlockOrder[i]]).ToArray(); + ushort chk = SaveUtil.check32(chunk); + if (chk != BitConverter.ToUInt16(Data, ABO + i*0xFF4)) + return false; + } + return true; + } + } + public override string ChecksumInfo + { + get + { + string r = ""; + for (int i = 0; i < 14; i++) + { + byte[] chunk = Data.Skip(ABO + i * 0x1000).Take(chunkLength[BlockOrder[i]]).ToArray(); + ushort chk = SaveUtil.check32(chunk); + if (chk != BitConverter.ToUInt16(Data, ABO + i * 0xFF4)) + r += $"Block {BlockOrder[i]} @ {i*0x1000} (len {chunkLength[BlockOrder[i]]}) invalid." + Environment.NewLine; + } + return r.Length == 0 ? "Checksums valid." : r.TrimEnd(); + } + } + + // Trainer Info + public override GameVersion Version + { + get + { + switch (SaveVersion) + { + case 0: return GameVersion.RS; + case 1: return GameVersion.FRLG; + case 2: return GameVersion.E; + } + return GameVersion.Unknown; + } + } + + private bool Japanese; + private uint SecurityKey + { + get + { + switch (Version) + { + case GameVersion.E: return BitConverter.ToUInt32(Data, Trainer1 + 0xAC); + case GameVersion.FRLG: return BitConverter.ToUInt32(Data, Trainer1 + 0xAF8); + default: return 0; + } + } + } + public override string OT + { + get + { + return PKX.getG3Str(Data.Skip(Trainer1).Take(0x10).ToArray(), Japanese) + .Replace("\uE08F", "\u2640") // Nidoran ♂ + .Replace("\uE08E", "\u2642") // Nidoran ♀ + .Replace("\u2019", "\u0027"); // Farfetch'd + } + set + { + if (value.Length > 7) + value = value.Substring(0, 7); // Hard cap + string TempNick = value // Replace Special Characters and add Terminator + .Replace("\u2640", "\uE08F") // Nidoran ♂ + .Replace("\u2642", "\uE08E") // Nidoran ♀ + .Replace("\u0027", "\u2019"); // Farfetch'd + PKX.setG3Str(TempNick, Japanese).CopyTo(Data, Trainer1); + } + } + public override int Gender + { + get { return Data[Trainer1 + 8]; } + set { Data[Trainer1 + 8] = (byte)value; } + } + public override ushort TID + { + get { return BitConverter.ToUInt16(Data, Trainer1 + 0xA + 0); } + set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0xA + 0); } + } + public override ushort SID + { + get { return BitConverter.ToUInt16(Data, Trainer1 + 0xC); } + set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0xC); } + } + public override int PlayedHours + { + get { return BitConverter.ToUInt16(Data, Trainer1 + 0xE); } + set { BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0xE); } + } + public override int PlayedMinutes + { + get { return Data[Trainer1 + 0x10]; } + set { Data[Trainer1 + 0x10] = (byte)value; } + } + public override int PlayedSeconds + { + get { return Data[Trainer1 + 0x11]; } + set { Data[Trainer1 + 0x11] = (byte)value; } + } + public int PlayedFrames + { + get { return Data[Trainer1 + 0x12]; } + set { Data[Trainer1 + 0x12] = (byte)value; } + } + + public override uint Money + { + get + { + switch (Version) + { + case GameVersion.RS: + case GameVersion.E: return BitConverter.ToUInt32(Data, Party + 0x0490) ^ SecurityKey; + case GameVersion.FRLG: return BitConverter.ToUInt32(Data, Party + 0x0290) ^ SecurityKey; + default: return 0; + } + } + set + { + switch (Version) + { + case GameVersion.RS: + case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, Party + 0x0490); break; + case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, Party + 0x0290); break; + } + } + } + public uint Coin + { + get + { + switch (Version) + { + case GameVersion.RS: + case GameVersion.E: return BitConverter.ToUInt32(Data, Party + 0x0494) ^ SecurityKey; + case GameVersion.FRLG: return BitConverter.ToUInt32(Data, Party + 0x0294) ^ SecurityKey; + default: return 0; + } + } + set + { + switch (Version) + { + case GameVersion.RS: + case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, Party + 0x0494); break; + case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, Party + 0x0294); break; + } + } + } + public int BP + { + get { return Data[Trainer1 + 0xEB8]; } + set { Data[Trainer1 + 0xEB8] = (byte)value; } + } + + public override InventoryPouch[] Inventory + { + get + { + return null; + + } + set { } + } + public override int Language { get { return 0; } set { } } + + public override int getDaycareSlotOffset(int loc, int slot) + { + return Daycare + slot * SIZE_PARTY; + } + public override ulong? getDaycareRNGSeed(int loc) + { + return null; + } + public override uint? getDaycareEXP(int loc, int slot) + { + int ofs = Daycare + (slot + 1) * SIZE_PARTY - 4; + return BitConverter.ToUInt32(Data, ofs); + } + public override bool? getDaycareOccupied(int loc, int slot) + { + return null; + } + public override void setDaycareEXP(int loc, int slot, uint EXP) + { + int ofs = Daycare + (slot + 1) * SIZE_PARTY - 4; + BitConverter.GetBytes(EXP).CopyTo(Data, ofs); + } + public override void setDaycareOccupied(int loc, int slot, bool occupied) + { + + } + + // Storage + public override int PartyCount + { + get + { + int ofs = 0x34; + if (GameVersion.FRLG != Version) + ofs += 0x200; + return Data[Party + ofs]; + + } + protected set + { + int ofs = 0x34; + if (GameVersion.FRLG != Version) + ofs += 0x200; + Data[Party + ofs] = (byte)value; + } + } + public override int getBoxOffset(int box) + { + return Box + 4 + SIZE_STORED * box * 30; + } + public override int getPartyOffset(int slot) + { + int ofs = 0x38; + if (GameVersion.FRLG != Version) + ofs += 0x200; + return Party + ofs + SIZE_PARTY * slot; + } + public override int CurrentBox + { + get { return Data[Box]; } + set { Data[Box] = (byte)value; } + } + public override int getBoxWallpaper(int box) + { + // Box Wallpaper is directly after the Box Names + int offset = getBoxOffset(BoxCount); + offset += BoxCount * 0x9; + return Data[offset]; + } + public override string getBoxName(int box) + { + int offset = getBoxOffset(BoxCount); + return PKX.getG3Str(Data.Skip(offset + box * 9).Take(9).ToArray(), Japanese); + } + public override void setBoxName(int box, string value) + { + if (value.Length > 8) + value = value.Substring(0, 8); // Hard cap + int offset = getBoxOffset(BoxCount); + PKX.setG3Str(value, Japanese).CopyTo(Data, offset + box * 9); + } + public override PKM getPKM(byte[] data) + { + return new PK3(data); + } + public override byte[] decryptPKM(byte[] data) + { + return PKX.decryptArray3(data); + } + } +} diff --git a/Saves/SAV4.cs b/Saves/SAV4.cs index 1e9fe9277..784898fb7 100644 --- a/Saves/SAV4.cs +++ b/Saves/SAV4.cs @@ -568,7 +568,6 @@ public override byte[] decryptPKM(byte[] data) } // Daycare - public override int DaycareSeedSize => 8; public override int getDaycareSlotOffset(int loc, int slot) { return Daycare + slot * SIZE_PARTY; diff --git a/Saves/SaveFile.cs b/Saves/SaveFile.cs index 1daf83b72..9cfc82206 100644 --- a/Saves/SaveFile.cs +++ b/Saves/SaveFile.cs @@ -84,7 +84,7 @@ public byte[] Write(bool DSV) protected virtual int GiftFlagMax { get; } = 0x800; protected virtual int EventFlagMax { get; } = int.MinValue; protected virtual int EventConstMax { get; } = int.MinValue; - public abstract int DaycareSeedSize { get; } + public virtual int DaycareSeedSize { get; } = 0; public abstract int OTLength { get; } public abstract int NickLength { get; } @@ -249,8 +249,8 @@ public ushort[] EventConsts protected int OFS_MailItems { get; set; } = int.MinValue; // Mystery Gift - protected abstract bool[] MysteryGiftReceivedFlags { get; set; } - protected abstract MysteryGift[] MysteryGiftCards { get; set; } + protected virtual bool[] MysteryGiftReceivedFlags { get { return null; } set { } } + protected virtual MysteryGift[] MysteryGiftCards { get { return null; } set { } } public virtual MysteryGiftAlbum GiftAlbum { get @@ -285,8 +285,8 @@ public virtual MysteryGiftAlbum GiftAlbum public abstract int PlayedHours { get; set; } public abstract int PlayedMinutes { get; set; } public abstract int PlayedSeconds { get; set; } - public abstract int SecondsToStart { get; set; } - public abstract int SecondsToFame { get; set; } + public virtual int SecondsToStart { get; set; } + public virtual int SecondsToFame { get; set; } public abstract uint Money { get; set; } public abstract int BoxCount { get; } public abstract int PartyCount { get; protected set; } diff --git a/Saves/SaveUtil.cs b/Saves/SaveUtil.cs index af9a41b32..678af2428 100644 --- a/Saves/SaveUtil.cs +++ b/Saves/SaveUtil.cs @@ -14,6 +14,8 @@ public enum GameVersion X = 24, Y = 25, AS = 26, OR = 27, SN = 28, MN = 29, + RS = 200, + FRLG = 201, DP = 100, HGSS = 101, } @@ -28,11 +30,15 @@ internal static class SaveUtil internal const int SIZE_G5BW = 0x24000; internal const int SIZE_G5B2W2 = 0x26000; internal const int SIZE_G4RAW = 0x80000; + internal const int SIZE_G3RAW = 0x20000; + internal const int SIZE_G3RAWHALF = 0x10000; internal static readonly byte[] FOOTER_DSV = Encoding.ASCII.GetBytes("|-DESMUME SAVE-|"); internal static int getSAVGeneration(byte[] data) { + if (getIsG3SAV(data) != -1) + return 3; if (getIsG4SAV(data) != -1) return 4; if (getIsG5SAV(data) != -1) @@ -41,6 +47,24 @@ internal static int getSAVGeneration(byte[] data) return 6; return -1; } + internal static int getIsG3SAV(byte[] data) + { + if (data.Length != SIZE_G3RAW && data.Length != SIZE_G3RAWHALF) + return -1; + + int[] BlockOrder = new int[14]; + for (int i = 0; i < 14; i++) + BlockOrder[i] = BitConverter.ToInt16(data, i * 0x1000 + 0xFF4); + + if (BlockOrder.Any(i => i > 0xD || i < 0)) + return -1; + + // Detect RS/E/FRLG + // Section 0 stores Game Code @ 0x00AC; 0 for RS, 1 for FRLG, else for Emerald + + uint GameCode = BitConverter.ToUInt32(data, Array.IndexOf(BlockOrder, 0) * 0x1000); + return GameCode < 2 ? (int)GameCode : 2; + } internal static int getIsG4SAV(byte[] data) { if (data.Length != SIZE_G4RAW) @@ -103,6 +127,8 @@ internal static SaveFile getVariantSAV(byte[] data) { switch (getSAVGeneration(data)) { + case 3: + return new SAV3(data); case 4: return new SAV4(data); case 5: @@ -276,6 +302,16 @@ internal static void writeG6CHK(byte[] savefile) BitConverter.GetBytes(ccitt16(array)).CopyTo(savefile, verificationOffset + 6 + i * 8); } } + /// Calculates the 32bit checksum over an input byte array. Used in GBA save files. + /// Input byte array + /// Checksum + internal static ushort check32(byte[] data) + { + uint val = 0; + for (int i = 0; i < data.Length; i += 4) + val += BitConverter.ToUInt32(data, i); + return (ushort)(val + val >> 16); + } internal static int getDexFormIndexXY(int species, int formct) {