public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : base(SaveUtil.SIZE_G3RAW) { if (version == GameVersion.FR || version == GameVersion.LG) { Version = GameVersion.FRLG; } else if (version == GameVersion.R || version == GameVersion.S) { Version = GameVersion.RS; } else { Version = version; } _personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; Japanese = japanese; LoadBlocks(out BlockOrder, out BlockOfs); // spoof block offsets BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray(); LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = BlockOfs[0] + 0x18; SeenFlagOffsets = Array.Empty <int>(); Initialize(); ClearBoxes(); }
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { LoadBlocks(out BlockOrder, out BlockOfs); Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]); _personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0; LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = BlockOfs[0] + 0x18; SeenFlagOffsets = Version switch { GameVersion.RS => new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C }, GameVersion.E => new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 }, _ => new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 } }; Initialize(); }
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) { Version = version switch { GameVersion.FR or GameVersion.LG => GameVersion.FRLG, GameVersion.R or GameVersion.S => GameVersion.RS, _ => version }; _personal = SaveUtil.GetG3Personal(Version); Japanese = japanese; BlockOrder = Array.Empty <short>(); LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = 0x18; SeenFlagOffsets = Array.Empty <int>(); Initialize(); ClearBoxes(); }
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { LoadBlocks(out BlockOrder); // Copy chunk to the allocated location LoadBlocks(Small, 0, 1); LoadBlocks(Large, 1, 5); LoadBlocks(Storage, 5, BLOCK_COUNT); Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Small); _personal = SaveUtil.GetG3Personal(Version); // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Small, 0x6) == 0; LegalKeyItems = Version switch { GameVersion.RS => Legal.Pouch_Key_RS, GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; PokeDex = 0x18; SeenFlagOffsets = Version switch { GameVersion.RS => new[] { 0x938, 0x3A8C }, GameVersion.E => new[] { 0x988, 0x3B24 }, _ => new[] { 0x5F8, 0x3A18 } }; Initialize(); }
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { LoadBlocks(); Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]); Personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0; Initialize(); }
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : base(SaveUtil.SIZE_G3RAW) { if (version == GameVersion.FR || version == GameVersion.LG) { Version = GameVersion.FRLG; } else if (version == GameVersion.R || version == GameVersion.S) { Version = GameVersion.RS; } else { Version = version; } Personal = SaveUtil.GetG3Personal(Version) ?? PersonalTable.RS; Japanese = japanese; LoadBlocks(); // spoof block offsets BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray(); Initialize(); ClearBoxes(); }
public SAV3(byte[] data = null, GameVersion versionOverride = GameVersion.Any) { Data = data ?? new byte[SaveUtil.SIZE_G3RAW]; BAK = (byte[])Data.Clone(); Exportable = !IsRangeEmpty(0, Data.Length); int[] BlockOrder1 = GetBlockOrder(0); if (Data.Length > SaveUtil.SIZE_G3RAWHALF) { int[] BlockOrder2 = GetBlockOrder(0xE000); ActiveSAV = GetActiveSaveIndex(BlockOrder1, BlockOrder2); BlockOrder = ActiveSAV == 0 ? BlockOrder1 : BlockOrder2; } else { ActiveSAV = 0; BlockOrder = BlockOrder1; } BlockOfs = new int[BLOCK_COUNT]; for (int i = 0; i < BLOCK_COUNT; i++) { int index = Array.IndexOf(BlockOrder, i); BlockOfs[i] = index < 0 ? int.MinValue : (index * SIZE_BLOCK) + ABO; } if (versionOverride != GameVersion.Any) { Version = versionOverride; } else if (data == null) { Version = GameVersion.FRLG; } else { Version = GetVersion(Data, BlockOfs[0]); } Personal = SaveUtil.GetG3Personal(Version); if (data == null) // spoof block offsets { BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray(); if (Version == GameVersion.FR || Version == GameVersion.LG) { Version = GameVersion.FRLG; } else if (Version == GameVersion.R || Version == GameVersion.LG) { Version = GameVersion.RS; } } // Set up PC data buffer beyond end of save file. Box = Data.Length; Array.Resize(ref Data, Data.Length + SIZE_RESERVED); // More than enough empty space. // Copy chunk to the allocated location for (int i = 5; i < BLOCK_COUNT; i++) { int blockIndex = Array.IndexOf(BlockOrder, i); if (blockIndex == -1) // block empty { continue; } Array.Copy(Data, (blockIndex * SIZE_BLOCK) + ABO, Data, Box + ((i - 5) * 0xF80), chunkLength[i]); } // Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT // OT name is stored at the top of the first block. Japanese = BitConverter.ToInt16(Data, BlockOfs[0] + 0x6) == 0 && Exportable; PokeDex = BlockOfs[0] + 0x18; switch (Version) { case GameVersion.RS: LegalKeyItems = Legal.Pouch_Key_RS; OFS_PCItem = BlockOfs[1] + 0x0498; OFS_PouchHeldItem = BlockOfs[1] + 0x0560; OFS_PouchKeyItem = BlockOfs[1] + 0x05B0; OFS_PouchBalls = BlockOfs[1] + 0x0600; OFS_PouchTMHM = BlockOfs[1] + 0x0640; OFS_PouchBerry = BlockOfs[1] + 0x0740; SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C }; EventFlag = BlockOfs[2] + 0x2A0; EventConst = EventFlag + (EventFlagMax / 8); Daycare = BlockOfs[4] + 0x11C; break; case GameVersion.E: LegalKeyItems = Legal.Pouch_Key_E; OFS_PCItem = BlockOfs[1] + 0x0498; OFS_PouchHeldItem = BlockOfs[1] + 0x0560; OFS_PouchKeyItem = BlockOfs[1] + 0x05D8; OFS_PouchBalls = BlockOfs[1] + 0x0650; OFS_PouchTMHM = BlockOfs[1] + 0x0690; OFS_PouchBerry = BlockOfs[1] + 0x0790; SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 }; EventFlag = BlockOfs[2] + 0x2F0; EventConst = EventFlag + (EventFlagMax / 8); Daycare = BlockOfs[4] + 0x1B0; break; case GameVersion.FRLG: LegalKeyItems = Legal.Pouch_Key_FRLG; OFS_PCItem = BlockOfs[1] + 0x0298; OFS_PouchHeldItem = BlockOfs[1] + 0x0310; OFS_PouchKeyItem = BlockOfs[1] + 0x03B8; OFS_PouchBalls = BlockOfs[1] + 0x0430; OFS_PouchTMHM = BlockOfs[1] + 0x0464; OFS_PouchBerry = BlockOfs[1] + 0x054C; SeenFlagOffsets = new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 }; EventFlag = BlockOfs[1] + 0xEE0; EventConst = BlockOfs[2] + 0x80; Daycare = BlockOfs[4] + 0x100; break; } LoadEReaderBerryData(); LegalItems = Legal.Pouch_Items_RS; LegalBalls = Legal.Pouch_Ball_RS; LegalTMHMs = Legal.Pouch_TMHM_RS; LegalBerries = Legal.Pouch_Berries_RS; HeldItems = Legal.HeldItems_RS; // Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet SeenFlagOffsets = SeenFlagOffsets?.Where(z => z >= 0).ToArray(); if (!Exportable) { ClearBoxes(); } }