using System; using System.Linq; using System.Text; namespace PKHeX { public class BlockInfo { public int Offset; public int Length; public ushort ID; public ushort Checksum; } public enum GameVersion { X = 24, Y = 25, AS = 26, OR = 27, SN = 28, MN = 29, Unknown = -1, } public class SAV6 : PKX { internal const int SIZE_XY = 0x65600; internal const int SIZE_ORAS = 0x76000; internal const int SIZE_ORASDEMO = 0x5A00; internal const int BEEF = 0x42454546; // Global Settings internal static bool SetUpdateDex = true; internal static bool SetUpdatePK6 = true; // Save Data Attributes public byte[] Data; public bool Edited; public readonly bool Exportable; public readonly byte[] BAK; public string FileName, FilePath; public string BAKName => $"{FileName} [{OT} ({Version}) - {LastSavedTime}].bak"; public SAV6(byte[] data = null) { Data = (byte[])(data ?? new byte[SIZE_ORAS]).Clone(); BAK = (byte[])Data.Clone(); Exportable = !Data.SequenceEqual(new byte[Data.Length]); // Load Info getBlockInfo(); getSAVOffsets(); } public void getSAVOffsets() { if (ORASDEMO) { /* 00: */ Item = 0x00000; Items = new Inventory(Item, 1); /* 01: */ // = 0x00C00; // Select Bound Items /* 02: */ AdventureInfo = 0x00E00; /* 03: */ Trainer1 = 0x01000; /* 04: */ // = 0x01200; [00004] // ??? /* 05: */ PlayTime = 0x01400; /* 06: */ // = 0x01600; [00024] // FFFFFFFF /* 07: */ // = 0x01800; [02100] // Overworld Data /* 08: */ Trainer2 = 0x03A00; /* 09: */ TrainerCard = 0x03C00; /* 10: */ Party = 0x03E00; /* 11: */ EventConst = 0x04600; EventAsh = EventConst + 0x78; EventFlag = EventConst + 0x2FC; /* 12: */ // = 0x04C00; [00004] // 87B1A23F const /* 13: */ // = 0x04E00; [00048] // Repel Info, (Swarm?) and other overworld info /* 14: */ SUBE = 0x05000; /* 15: */ PSSStats = 0x05400; Box = BattleBox = GTS = Daycare = EonTicket = Fused = Puff = SuperTrain = SecretBase = BoxWallpapers = LastViewedBox = PCLayout = PCBackgrounds = PCFlags = WondercardFlags = WondercardData = BerryField = OPower = Accessories = PokeDex = PokeDexLanguageFlags = Spinda = EncounterCount = HoF = PSS = JPEG = Contest -1; MaisonStats = 0; // Make things work? } else if (XY) { Box = 0x22600; TrainerCard = 0x14000; Party = 0x14200; BattleBox = 0x04A00; Daycare = 0x1B200; GTS = 0x17800; Fused = 0x16000; SUBE = 0x1D890; Puff = 0x00000; Item = 0x00400; Items = new Inventory(Item, 0); AdventureInfo = 0x01200; Trainer1 = 0x1400; Trainer2 = 0x4200; PCLayout = 0x4400; PCBackgrounds = PCLayout + 0x41E; PCFlags = PCLayout + 0x43D; WondercardFlags = 0x1BC00; WondercardData = WondercardFlags + 0x100; BerryField = 0x1B800; OPower = 0x16A00; EventConst = 0x14A00; EventAsh = -1; EventFlag = EventConst + 0x2FC; PokeDex = 0x15000; PokeDexLanguageFlags = PokeDex + 0x3C8; Spinda = PokeDex + 0x648; EncounterCount = -1; HoF = 0x19400; SuperTrain = 0x1F200; JPEG = 0x57200; MaisonStats = 0x1B1C0; PSS = 0x05000; PSSStats = 0x1E400; BoxWallpapers = 0x481E; SecretBase = -1; EonTicket = -1; Contest = -1; PlayTime = 0x1800; Accessories = 0x1A00; LastViewedBox = PCLayout + 0x43F; } else if (ORAS) { Box = 0x33000; // Confirmed TrainerCard = 0x14000; // Confirmed Party = 0x14200; // Confirmed BattleBox = 0x04A00; // Confirmed Daycare = 0x1BC00; // Confirmed (thanks Rei) GTS = 0x18200; // Confirmed Fused = 0x16A00; // Confirmed SUBE = 0x1D890; // ****not in use, not updating?**** Puff = 0x00000; // Confirmed Item = 0x00400; // Confirmed Items = new Inventory(Item, 1); AdventureInfo = 0x01200; Trainer1 = 0x01400; // Confirmed Trainer2 = 0x04200; // Confirmed PCLayout = 0x04400; // Confirmed PCBackgrounds = PCLayout + 0x41E; PCFlags = PCLayout + 0x43D; WondercardFlags = 0x1CC00; // Confirmed WondercardData = WondercardFlags + 0x100; BerryField = 0x1C400; // ****changed**** OPower = 0x17400; // ****changed**** EventConst = 0x14A00; EventAsh = EventConst + 0x78; EventFlag = EventConst + 0x2FC; PokeDex = 0x15000; Spinda = PokeDex + 0x680; EncounterCount = PokeDex + 0x686; PokeDexLanguageFlags = PokeDex + 0x400; HoF = 0x19E00; // Confirmed SuperTrain = 0x20200; Contest = 0x23600; // Confirmed JPEG = 0x67C00; // Confirmed MaisonStats = 0x1BBC0; PSS = 0x05000; // Confirmed (thanks Rei) PSSStats = 0x1F400; BoxWallpapers = 0x481E; SecretBase = 0x23A00; EonTicket = 0x319B8; PlayTime = 0x1800; Accessories = 0x1A00; LastViewedBox = PCLayout + 0x43F; } DaycareSlot = new[] { Daycare, Daycare + 0x1F0 }; } public class Inventory { public readonly int HeldItem, KeyItem, Medicine, TMHM, Berry; public Inventory(int Offset, int Game) { switch (Game) { case 0: HeldItem = Offset + 0; KeyItem = Offset + 0x640; TMHM = Offset + 0x7C0; Medicine = Offset + 0x968; Berry = Offset + 0xA68; break; case 1: HeldItem = Offset + 0; KeyItem = Offset + 0x640; TMHM = Offset + 0x7C0; Medicine = Offset + 0x970; Berry = Offset + 0xA70; break; } } } public Inventory Items = new Inventory(0, -1); public int BattleBox, GTS, Daycare, EonTicket, Fused, SUBE, Puff, Item, AdventureInfo, Trainer1, Trainer2, SuperTrain, PSSStats, MaisonStats, SecretBase, BoxWallpapers, LastViewedBox, PCLayout, PCBackgrounds, PCFlags, WondercardFlags, WondercardData, BerryField, OPower, EventConst, EventFlag, EventAsh, PlayTime, Accessories, PokeDex, PokeDexLanguageFlags, Spinda, EncounterCount, HoF, PSS, JPEG, Contest; public int TrainerCard = 0x14000; public int Box = 0x33000, Party = 0x14200; public int[] DaycareSlot; public int DaycareIndex = 0; public GameVersion Version { get { switch (Game) { case 24: return GameVersion.X; case 25: return GameVersion.Y; case 26: return GameVersion.AS; case 27: return GameVersion.OR; } return GameVersion.Unknown; } } public bool ORASDEMO => Data.Length == SIZE_ORASDEMO; public bool ORAS => Version == GameVersion.OR || Version == GameVersion.AS; public bool XY => Version == GameVersion.X || Version == GameVersion.Y; // Save Information private int BlockInfoOffset; private BlockInfo[] Blocks; private void getBlockInfo() { BlockInfoOffset = Data.Length - 0x200 + 0x10; if (BitConverter.ToUInt32(Data, BlockInfoOffset) != BEEF) BlockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future? int count = (Data.Length - BlockInfoOffset - 0x8) / 8; BlockInfoOffset += 4; Blocks = new BlockInfo[count]; int CurrentPosition = 0; for (int i = 0; i < Blocks.Length; i++) { Blocks[i] = new BlockInfo { Offset = CurrentPosition, Length = BitConverter.ToInt32(Data, BlockInfoOffset + 0 + 8*i), ID = BitConverter.ToUInt16(Data, BlockInfoOffset + 4 + 8*i), Checksum = BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + 8*i) }; // Expand out to nearest 0x200 CurrentPosition += Blocks[i].Length % 0x200 == 0 ? Blocks[i].Length : 0x200 - Blocks[i].Length % 0x200 + Blocks[i].Length; if ((Blocks[i].ID != 0) || i == 0) continue; count = i; break; } // Fix Final Array Lengths Array.Resize(ref Blocks, count); } private void setChecksums() { // Check for invalid block lengths if (Blocks.Length < 3) // arbitrary... { Console.WriteLine("Not enough blocks ({0}), aborting setChecksums", Blocks.Length); return; } // Apply checksums for (int i = 0; i < Blocks.Length; i++) { byte[] array = Data.Skip(Blocks[i].Offset).Take(Blocks[i].Length).ToArray(); BitConverter.GetBytes(ccitt16(array)).CopyTo(Data, BlockInfoOffset + 6 + i * 8); } } public bool ChecksumsValid => verifyG6SAV(Data); public string ChecksumInfo => verifyG6CHK(Data); public byte[] Write() { setChecksums(); return Data; } public ulong Secure1 { get { return BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14); } set { BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0x14); } } public ulong Secure2 { get { return BitConverter.ToUInt64(Data, BlockInfoOffset - 0xC); } set { BitConverter.GetBytes(value).CopyTo(Data, BlockInfoOffset - 0xC); } } public int CurrentBox { get { return Data[LastViewedBox]; } set { Data[LastViewedBox] = (byte)value; } } // Player Information public ushort TID { get { return BitConverter.ToUInt16(Data, TrainerCard + 0); } set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 0); } } public ushort SID { get { return BitConverter.ToUInt16(Data, TrainerCard + 2); } set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 2); } } public int Game { get { return Data[TrainerCard + 4]; } set { Data[TrainerCard + 4] = (byte)value; } } public int Gender { get { return Data[TrainerCard + 5]; } set { Data[TrainerCard + 5] = (byte)value; } } public int Sprite { get { return Data[TrainerCard + 7]; } set { Data[TrainerCard + 7] = (byte)value; } } public ulong GameSyncID { get { return BitConverter.ToUInt64(Data, TrainerCard + 8); } set { BitConverter.GetBytes(value).CopyTo(Data, TrainerCard + 8); } } public int SubRegion { get { return Data[TrainerCard + 0x26]; } set { Data[TrainerCard + 0x26] = (byte)value; } } public int Country { get { return Data[TrainerCard + 0x27]; } set { Data[TrainerCard + 0x27] = (byte)value; } } public int ConsoleRegion { get { return Data[TrainerCard + 0x2C]; } set { Data[TrainerCard + 0x2C] = (byte)value; } } public int Language { get { return Data[TrainerCard + 0x2D]; } set { Data[TrainerCard + 0x2D] = (byte)value; } } public string OT { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x48, 0x1A)); } set { Encoding.Unicode.GetBytes(value.PadRight(13, '\0')).CopyTo(Data, TrainerCard + 0x48); } } public string Saying1 { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 0, 0x22)); } set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 0); } } public string Saying2 { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 1, 0x22)); } set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 1); } } public string Saying3 { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 2, 0x22)); } set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 2); } } public string Saying4 { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 3, 0x22)); } set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 3); } } public string Saying5 { get { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, TrainerCard + 0x7C + 0x22 * 4, 0x22)); } set { Encoding.Unicode.GetBytes(value.PadRight(value.Length + 1, '\0')).CopyTo(Data, TrainerCard + 0x7C + 0x22 * 4); } } public int M { get { return BitConverter.ToUInt16(Data, Trainer1 + 0x02); } set { BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x02); } } public float X { get { return BitConverter.ToSingle(Data, Trainer1 + 0x10) / 18; } set { BitConverter.GetBytes(value * 18).CopyTo(Data, Trainer1 + 0x10); } } public float Z { get { return BitConverter.ToSingle(Data, Trainer1 + 0x14); } set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0x14); } } public float Y { get { return BitConverter.ToSingle(Data, Trainer1 + 0x18) / 18; } set { BitConverter.GetBytes(value * 18).CopyTo(Data, Trainer1 + 0x18); } } public int Style { get { return Data[Trainer1 + 0x14D]; } set { Data[Trainer1 + 0x14D] = (byte)value; } } public uint Money { get { return BitConverter.ToUInt32(Data, Trainer2 + 0x8); } set { BitConverter.GetBytes(value).CopyTo(Data, Trainer2 + 0x8); } } public int Badges { get { return Data[Trainer2 + 0xC]; } set { Data[Trainer2 + 0xC] = (byte)value; } } public int BP { get { int offset = Trainer2 + 0x3C; if (ORAS) offset -= 0xC; // 0x30 return BitConverter.ToUInt16(Data, offset); } set { int offset = Trainer2 + 0x3C; if (ORAS) offset -= 0xC; // 0x30 BitConverter.GetBytes((ushort)value).CopyTo(Data, offset); } } public int Vivillon { get { int offset = Trainer2 + 0x50; if (ORAS) offset -= 0xC; // 0x44 return Data[offset]; } set { int offset = Trainer2 + 0x50; if (ORAS) offset -= 0xC; // 0x44 Data[offset] = (byte)value; } } public int PlayedHours{ get { return BitConverter.ToUInt16(Data, PlayTime); } set { BitConverter.GetBytes((ushort)value).CopyTo(Data, PlayTime); } } public int PlayedMinutes { get { return Data[PlayTime + 2]; } set { Data[PlayTime + 2] = (byte)value; } } public int PlayedSeconds { get { return Data[PlayTime + 3]; } set { Data[PlayTime + 3] = (byte)value; } } public uint LastSaved { get { return BitConverter.ToUInt32(Data, PlayTime + 0x4); } set { BitConverter.GetBytes(value).CopyTo(Data, PlayTime + 0x4); } } public int LastSavedYear { get { return (int)(LastSaved & 0xFFF); } set { LastSaved = LastSaved & 0xFFFFF000 | (uint)value; } } public int LastSavedMonth { get { return (int)(LastSaved >> 12 & 0xF); } set { LastSaved = LastSaved & 0xFFFF0FFF | ((uint)value & 0xF) << 12; } } public int LastSavedDay { get { return (int)(LastSaved >> 16 & 0x1F); } set { LastSaved = LastSaved & 0xFFE0FFFF | ((uint)value & 0x1F) << 16; } } public int LastSavedHour { get { return (int)(LastSaved >> 21 & 0x1F); } set { LastSaved = LastSaved & 0xFC1FFFFF | ((uint)value & 0x1F) << 21; } } public int LastSavedMinute { get { return (int)(LastSaved >> 26 & 0x3F); } set { LastSaved = LastSaved & 0x03FFFFFF | ((uint)value & 0x3F) << 26; } } public string LastSavedTime => $"{LastSavedYear.ToString("0000")}{LastSavedMonth.ToString("00")}{LastSavedDay.ToString("00")}{LastSavedHour.ToString("00")}{LastSavedMinute.ToString("00")}"; public int ResumeYear { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x4); } set { BitConverter.GetBytes(value).CopyTo(Data,AdventureInfo + 0x4); } } public int ResumeMonth { get { return Data[AdventureInfo + 0x8]; } set { Data[AdventureInfo + 0x8] = (byte)value; } } public int ResumeDay { get { return Data[AdventureInfo + 0x9]; } set { Data[AdventureInfo + 0x9] = (byte)value; } } public int ResumeHour { get { return Data[AdventureInfo + 0xB]; } set { Data[AdventureInfo + 0xB] = (byte)value; } } public int ResumeMinute { get { return Data[AdventureInfo + 0xC]; } set { Data[AdventureInfo + 0xC] = (byte)value; } } public int ResumeSeconds { get { return Data[AdventureInfo + 0xD]; } set { Data[AdventureInfo + 0xD] = (byte)value; } } public int SecondsToStart { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x18); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x18); } } public int SecondsToFame { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x20); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x20); } } public uint getPSSStat(int index) { return BitConverter.ToUInt32(Data, PSSStats + 4*index); } public void setPSSStat(int index, uint value) { BitConverter.GetBytes(value).CopyTo(Data, PSSStats + 4*index); } public ushort getMaisonStat(int index) { return BitConverter.ToUInt16(Data, MaisonStats + 2 * index); } public void setMaisonStat(int index, ushort value) { BitConverter.GetBytes(value).CopyTo(Data, MaisonStats + 2*index); } public uint getEncounterCount(int index) { return BitConverter.ToUInt16(Data, EncounterCount + 2*index); } public void setEncounterCount(int index, ushort value) { BitConverter.GetBytes(value).CopyTo(Data, EncounterCount + 2*index); } // Misc Properties public int PartyCount { get { return Data[Party + 6 * PK6.SIZE_PARTY]; } set { Data[Party + 6 * PK6.SIZE_PARTY] = (byte)value; } } public bool BattleBoxLocked { get { return Data[BattleBox + 6 * PK6.SIZE_STORED] != 0; } set { Data[BattleBox + 6 * PK6.SIZE_STORED] = (byte)(value ? 1 : 0); } } public ulong DaycareRNGSeed { get { return BitConverter.ToUInt64(Data, DaycareSlot[DaycareIndex] + 0x1E8); } set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + 0x1E8); } } public bool DaycareHasEgg { get { return Data[DaycareSlot[DaycareIndex] + 0x1E0] == 1; } set { Data[DaycareSlot[DaycareIndex] + 0x1E0] = (byte)(value ? 1 : 0); } } public uint DaycareEXP1 { get { return BitConverter.ToUInt32(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0 + 4); } set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0 + 4); } } public uint DaycareEXP2 { get { return BitConverter.ToUInt32(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*1 + 4); } set { BitConverter.GetBytes(value).CopyTo(Data, DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*1 + 4); } } public bool DaycareOccupied1 { get { return Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0] != 0; } set { Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8)*0] = (byte)(value ? 1 : 0); } } public bool DaycareOccupied2 { get { return Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8) * 1] != 0; } set { Data[DaycareSlot[DaycareIndex] + (PK6.SIZE_STORED + 8) * 1] = (byte)(value ? 1 : 0); } } public byte[] Puffs { get { return Data.Skip(Puff).Take(100).ToArray(); } set { value.CopyTo(Data, Puff); } } public int PuffCount { get { return BitConverter.ToInt32(Data, Puff + 100); } set { BitConverter.GetBytes(value).CopyTo(Data, Puff + 100); } } public byte[] getWondercard(int i) { return Data.Skip(WondercardData + i*WC6.Size).Take(WC6.Size).ToArray(); } public void setWondercard(int i, byte[] data) { data.CopyTo(data, WondercardData + i * WC6.Size); } public string JPEGTitle => JPEG > -1 ? null : Util.TrimFromZero(Encoding.Unicode.GetString(Data, JPEG, 0x1A)); public byte[] JPEGData => JPEG > -1 || Data[JPEG + 0x54] != 0xFF ? null : Data.Skip(JPEG + 0x54).Take(0xE004).ToArray(); // Data Accessing public byte[] getData(int Offset, int Length) { return Data.Skip(Offset).Take(Length).ToArray(); } public void setData(byte[] input, int Offset) { input.CopyTo(Data, Offset); Edited = true; } // Pokémon Requests public PK6 getPK6Party(int offset) { return new PK6(decryptArray(getData(offset , PK6.SIZE_PARTY))); } public PK6 getPK6Stored(int offset) { return new PK6(decryptArray(getData(offset, PK6.SIZE_STORED))); } public void setPK6Party(PK6 pk6, int offset, bool? trade = null, bool? dex = null) { if (pk6 == null) return; if (trade ?? SetUpdatePK6) setPK6(pk6); if (dex ?? SetUpdateDex) setDex(pk6); setData(pk6.EncryptedPartyData, offset); Edited = true; } public void setPK6Stored(PK6 pk6, int offset, bool? trade = null, bool? dex = null) { if (pk6 == null) return; if (trade ?? SetUpdatePK6) setPK6(pk6); if (dex ?? SetUpdateDex) setDex(pk6); setData(pk6.EncryptedBoxData, offset); Edited = true; } public void setEK6Stored(byte[] ek6, int offset, bool? trade = null, bool? dex = null) { if (ek6 == null) return; PK6 pk6 = new PK6(decryptArray(ek6)); if (trade ?? SetUpdatePK6) setPK6(pk6); if (dex ?? SetUpdateDex) setDex(pk6); Array.Resize(ref ek6, PK6.SIZE_STORED); setData(ek6, offset); Edited = true; } public void setEK6Party(byte[] ek6, int offset, bool? trade = null, bool? dex = null) { if (ek6 == null) return; PK6 pk6 = new PK6(decryptArray(ek6)); if (trade ?? SetUpdatePK6) setPK6(pk6); if (dex ?? SetUpdateDex) setDex(pk6); Array.Resize(ref ek6, PK6.SIZE_PARTY); setData(ek6, offset); Edited = true; } // Meta public void setPK6(PK6 pk6) { // Apply to this Save File int CT = pk6.CurrentHandler; DateTime Date = DateTime.Now; pk6.Trade(OT, TID, SID, Country, SubRegion, Gender, false, Date.Day, Date.Month, Date.Year); if (CT != pk6.CurrentHandler) // Logic updated Friendship { // Copy over the Friendship Value only under certain circumstances if (pk6.Moves.Contains(216)) // Return pk6.CurrentFriendship = pk6.OppositeFriendship; else if (pk6.Moves.Contains(218)) // Frustration pk6.CurrentFriendship = pk6.OppositeFriendship; else if (pk6.CurrentHandler == 1) // OT->HT, needs new Friendship/Affection pk6.TradeFriendshipAffection(OT); } pk6.RefreshChecksum(); } public void setDex(PK6 pk6) { if (PokeDex < 0) return; if (pk6.Species == 0) return; if (Version == GameVersion.Unknown) return; int bit = pk6.Species - 1; int lang = pk6.Language - 1; if (lang > 5) lang--; // 0-6 language vals int origin = pk6.Version; int gender = pk6.Gender % 2; // genderless -> male int shiny = pk6.IsShiny ? 1 : 0; int shiftoff = shiny * 0x60 * 2 + gender * 0x60 + 0x60; // Set the [Species/Gender/Shiny] Owned Flag Data[PokeDex + shiftoff + bit / 8 + 0x8] |= (byte)(1 << (bit % 8)); // Owned quality flag if (origin < 0x18 && bit < 649 && !ORAS) // Species: 1-649 for X/Y, and not for ORAS; Set the Foreign Owned Flag Data[PokeDex + 0x64C + bit / 8] |= (byte)(1 << (bit % 8)); else if (origin >= 0x18 || ORAS) // Set Native Owned Flag (should always happen) Data[PokeDex + bit / 8 + 0x8] |= (byte)(1 << (bit % 8)); // Set the Display flag if none are set bool Displayed = false; Displayed |= (Data[PokeDex + 0x60*5 + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0; Displayed |= (Data[PokeDex + 0x60*6 + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0; Displayed |= (Data[PokeDex + 0x60*7 + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0; Displayed |= (Data[PokeDex + 0x60*8 + bit/8 + 0x8] & (byte) (1 << (bit%8))) != 0; if (!Displayed) // offset is already biased by 0x60, reuse shiftoff but for the display flags. Data[PokeDex + shiftoff + 0x60 * 4 + bit / 8 + 0x8] |= (byte)(1 << (bit % 8)); // Set the Language if (lang < 0) lang = 1; Data[PokeDexLanguageFlags + (bit * 7 + lang) / 8] |= (byte)(1 << ((bit * 7 + lang) % 8)); // Set Form flags int fc = Personal[pk6.Species].FormeCount; int f = ORAS ? getDexFormIndexORAS(pk6.Species, fc) : getDexFormIndexXY(pk6.Species, fc); if (f >= 0) { int FormLen = ORAS ? 0x26 : 0x18; int FormDex = PokeDex + 0x368; bit = f + pk6.AltForm; // Set Seen Flag Data[FormDex + FormLen*shiny + bit/8] |= (byte)(1 << (bit%8)); // Set Displayed Flag if necessary, check all flags bool FormDisplayed = false; for (int i = 0; i < fc; i++) { bit = f + i; FormDisplayed |= (Data[FormDex + FormLen*2 + bit/8] & (byte)(1 << (bit%8))) != 0; // Nonshiny FormDisplayed |= (Data[FormDex + FormLen*3 + bit/8] & (byte)(1 << (bit%8))) != 0; // Shiny } if (!FormDisplayed) { bit = f + pk6.AltForm; Data[FormDex + FormLen*(2+shiny) + bit/8] |= (byte)(1 << (bit%8)); } } // Set DexNav count (only if not encountered previously) if (ORAS && getEncounterCount(pk6.Species - 1) == 0) setEncounterCount(pk6.Species - 1, 1); } public int getBoxWallpaper(int box) { return 1 + Data[BoxWallpapers + box]; } public string getBoxName(int box) { return Util.TrimFromZero(Encoding.Unicode.GetString(Data, PCLayout + 0x22*box, 0x22)); } public void setBoxName(int box, string val) { Encoding.Unicode.GetBytes(val.PadRight(0x11, '\0')).CopyTo(Data, PCLayout + 0x22*box); Edited = true; } public void setParty() { byte partymembers = 0; // start off with a ctr of 0 for (int i = 0; i < 6; i++) { // Gather all the species byte[] data = new byte[PK6.SIZE_PARTY]; Array.Copy(Data, Party + i * PK6.SIZE_PARTY, data, 0, PK6.SIZE_PARTY); byte[] decdata = decryptArray(data); int species = BitConverter.ToInt16(decdata, 8); if ((species != 0) && (species < 722)) Array.Copy(data, 0, Data, Party + partymembers++ * PK6.SIZE_PARTY, PK6.SIZE_PARTY); } // Write in the current party count PartyCount = partymembers; // Zero out the party slots that are empty. for (int i = 0; i < 6; i++) if (i >= partymembers) Array.Copy(encryptArray(new byte[PK6.SIZE_PARTY]), 0, Data, Party + i * PK6.SIZE_PARTY, PK6.SIZE_PARTY); if (BattleBox < 0) return; // Repeat for Battle Box. byte battlemem = 0; for (int i = 0; i < 6; i++) { // Gather all the species byte[] data = new byte[PK6.SIZE_PARTY]; Array.Copy(Data, BattleBox + i * PK6.SIZE_STORED, data, 0, PK6.SIZE_STORED); byte[] decdata = decryptArray(data); int species = BitConverter.ToInt16(decdata, 8); if ((species != 0) && (species < 722)) Array.Copy(data, 0, Data, BattleBox + battlemem++ * PK6.SIZE_STORED, PK6.SIZE_STORED); } // Zero out the party slots that are empty. for (int i = 0; i < 6; i++) if (i >= battlemem) Array.Copy(encryptArray(new byte[PK6.SIZE_PARTY]), 0, Data, BattleBox + i * PK6.SIZE_STORED, PK6.SIZE_STORED); BattleBoxLocked &= battlemem != 0; } public void sortBoxes() { const int len = 31 * 30; // amount of pk6's in boxes // Fetch encrypted box data byte[][] bdata = new byte[len][]; for (int i = 0; i < len; i++) bdata[i] = getData(Box + i * PK6.SIZE_STORED, PK6.SIZE_STORED); // Sorting Method: Data will sort empty slots to the end, then from the filled slots eggs will be last, then species will be sorted. var query = from i in bdata let p = new PK6(decryptArray(i)) orderby p.Species == 0 ascending, p.IsEgg ascending, p.Species ascending, p.IsNicknamed ascending select i; byte[][] sorted = query.ToArray(); // Write data back for (int i = 0; i < len; i++) setData(sorted[i], Box + i * PK6.SIZE_STORED); } // Informational public PK6[] BoxData { get { PK6[] data = new PK6[31*30]; for (int i = 0; i < data.Length; i++) { data[i] = getPK6Stored(Box + PK6.SIZE_STORED * i); data[i].Identifier = $"B{(i/30 + 1).ToString("00")}:{(i%30 + 1).ToString("00")}"; } return data; } set { if (value == null) throw new ArgumentNullException(); if (value.Length != 31*30) throw new ArgumentException("Expected 930, got " + value.Length); for (int i = 0; i < value.Length; i++) setPK6Stored(value[i], Box + PK6.SIZE_STORED * i); } } public PK6[] PartyData { get { PK6[] data = new PK6[PartyCount]; for (int i = 0; i < data.Length; i++) data[i] = getPK6Party(Party + PK6.SIZE_PARTY * i); return data; } set { if (value == null) throw new ArgumentNullException(); if (value.Length == 0 || value.Length > 6) throw new ArgumentException("Expected 1-6, got " + value.Length); if (value[0].Species == 0) throw new ArgumentException("Can't have an empty first slot." + value.Length); PK6[] newParty = value.Where(pk => pk.Species != 0).ToArray(); PartyCount = newParty.Length; Array.Resize(ref newParty, 6); for (int i = PartyCount; i < newParty.Length; i++) newParty[i] = new PK6(); for (int i = 0; i < newParty.Length; i++) setPK6Party(newParty[i], Party + PK6.SIZE_PARTY*i); } } public PK6[] BattleBoxData { get { PK6[] data = new PK6[6]; for (int i = 0; i < data.Length; i++) { data[i] = getPK6Stored(BattleBox + PK6.SIZE_STORED * i); if (data[i].Species == 0) return data.Take(i).ToArray(); } return data; } } // Writeback Validity public string checkChunkFF() { string r = ""; byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); for (int i = 0; i < Data.Length / 0x200; i++) { if (!FFFF.SequenceEqual(Data.Skip(i * 0x200).Take(0x200))) continue; r = $"0x200 chunk @ 0x{(i*0x200).ToString("X5")} is FF'd." + Environment.NewLine + "Cyber will screw up (as of August 31st 2014)." + Environment.NewLine + Environment.NewLine; // Check to see if it is in the Pokedex if (i * 0x200 > PokeDex && i * 0x200 < PokeDex + 0x900) { r += "Problem lies in the Pokedex. "; if (i * 0x200 == PokeDex + 0x400) r += "Remove a language flag for a species < 585, ie Petilil"; } break; } return r; } // Debug public string getBlockInfoString() { return Blocks.Aggregate("", (current, b) => current + $"{b.ID.ToString("00")}: {b.Offset.ToString("X5")}-{(b.Offset + b.Length).ToString("X5")}, {b.Length.ToString("X5")}{Environment.NewLine}"); } } }