From 49541e607396be1b87e6f76af8fe7e19971ebede Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 7 Mar 2021 23:22:07 -0800 Subject: [PATCH] Rework mainline sav3 to not operate off chunks Previous style was to resize the sav buffer and concat all the storage chunks at the end. Allocated a double save file. Now, just allocate 3 buffers. --- PKHeX.Core/Editing/Saves/Slots/Extensions.cs | 2 +- .../Editing/Saves/Slots/Info/SlotInfoMisc.cs | 2 +- PKHeX.Core/Saves/SAV3.cs | 498 +++++++++--------- .../Saves/Substructures/Gen3/Record3.cs | 23 +- .../Saves/Substructures/Gen3/Roamer3.cs | 54 +- PKHeX.Core/Saves/Substructures/Mail/Mail3.cs | 1 + .../Save Editors/Gen3/SAV_Misc3.Designer.cs | 24 +- .../Subforms/Save Editors/Gen3/SAV_Misc3.cs | 139 +++-- .../Subforms/Save Editors/SAV_MailBox.cs | 24 +- 9 files changed, 370 insertions(+), 397 deletions(-) diff --git a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs index 17dac8da4..8de2c58ff 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Extensions.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Extensions.cs @@ -70,7 +70,7 @@ private static List GetExtraSlots3(SAV3 sav) return None; return new List { - new(sav.Data, 0, sav.GetBlockOffset(4) + 0xE18) {Type = StorageSlotType.Daycare } + new(sav.Large, 0, 0x3C98) {Type = StorageSlotType.Daycare } }; } diff --git a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs index 4c6fd5839..952030803 100644 --- a/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs +++ b/PKHeX.Core/Editing/Saves/Slots/Info/SlotInfoMisc.cs @@ -19,7 +19,7 @@ public SlotInfoMisc(SaveFile sav, int slot, int offset, bool party = false) Slot = slot; Offset = offset; PartyFormat = party; - Data = sav is SAV4 s ? s.General : sav.Data; + Data = sav is SAV4 s ? s.General : sav is SAV3 s3 ? s3.Large : sav.Data; } public SlotInfoMisc(byte[] data, int slot, int offset, bool party = false) diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index ec3f9793b..fce75b1b6 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -18,24 +18,44 @@ public sealed class SAV3 : SaveFile, ILangDeviantSave public bool Korean => false; public bool IndeterminateGame => Version == GameVersion.Unknown; - /* 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 - */ + // Similar to future games, the Generation 3 Mainline save files are comprised of two separate objects: + // Object 1 - Small Block, containing misc configuration data & the Pokédex. + // Object 2 - Large Block, containing everything else that isn't PC Storage system data. + // Object 3 - Storage Block, containing all the data for the PC storage system. + + // When the objects are serialized to the savedata, the game breaks up each object into chunks < 0x1000 bytes. + // Each serialized save occupies 14 chunks; there are a total of two serialized saves. + // After the serialized save data, there is "extra data", for stuff like Hall of Fame and battle videos. + private const int SIZE_BLOCK = 0x1000; private const int BLOCK_COUNT = 14; - private const int SIZE_RESERVED = 0x10000; // unpacked box data will start after the save data public const int SIZE_BLOCK_USED = 0xF80; + private const int COUNT_BOX = 14; + private const int COUNT_SLOTSPERBOX = 30; + // Use the largest of structure sizes, as zeroes being fed into checksum function don't change the value. + private const int SIZE_SMALL = 0xF2C; // maximum size for R/S/E/FR/LG structures + private const int SIZE_LARGE = (3 * 0xF80) + 0xF08; // maximum size for R/S/E/FR/LG structures + + public readonly byte[] Small = new byte[SIZE_SMALL]; + public readonly byte[] Large = new byte[SIZE_LARGE]; + public readonly byte[] Storage = new byte[SIZE_PC]; + protected override byte[] BoxBuffer => Storage; + protected override byte[] PartyBuffer => Large; + + // 0x83D0 + private const int SIZE_PC = sizeof(int) // Current Box + + (COUNT_BOX * (COUNT_SLOTSPERBOX * PokeCrypto.SIZE_3STORED)) // Slots + + (COUNT_BOX * (8 + 1)) // Box Names + + (COUNT_BOX * 1); // Box Wallpapers + private static readonly ushort[] chunkLength = { - 0xf2c, // 0 | Small Block (Trainer Info) + 0xf2c, // 0 | Small Block (Trainer Info) [0x890 RS, 0xf24 FR/LG] 0xf80, // 1 | Large Block Part 1 0xf80, // 2 | Large Block Part 2 0xf80, // 3 | Large Block Part 3 - 0xf08, // 4 | Large Block Part 4 + 0xf08, // 4 | Large Block Part 4 [0xc40 RS, 0xee8 FR/LG] 0xf80, // 5 | PC Block 0 0xf80, // 6 | PC Block 1 0xf80, // 7 | PC Block 2 @@ -47,36 +67,11 @@ public sealed class SAV3 : SaveFile, ILangDeviantSave 0x7d0 // D | PC Block 8 }; - public static void GetLargeBlockOffset(int contiguousOffset, out int chunk, out int chunkOffset) - { - for (chunk = 1; chunk <= 4; chunk++) - { - int chunkSize = chunkLength[chunk]; - if (chunkSize > contiguousOffset) - break; - contiguousOffset -= chunkSize; - } - chunkOffset = contiguousOffset; - } - - public static int GetLargeBlockOffset(int chunk, int chunkOffset) - { - if (chunk == 1) - return chunkOffset; - for (int i = 1; i <= 4; i++) - { - if (chunk == i) - break; - chunkOffset += chunkLength[i]; - } - return chunkOffset; - } - private PersonalTable _personal; public override PersonalTable Personal => _personal; public override IReadOnlyList HeldItems => Legal.HeldItems_RS; - public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : base(SaveUtil.SIZE_G3RAW) + public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) { Version = version switch { @@ -87,9 +82,7 @@ public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : bas _personal = SaveUtil.GetG3Personal(Version); Japanese = japanese; - LoadBlocks(out BlockOrder, out BlockOfs); - // spoof block offsets - BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray(); + BlockOrder = Array.Empty(); LegalKeyItems = Version switch { @@ -97,7 +90,7 @@ public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : bas GameVersion.E => Legal.Pouch_Key_E, _ => Legal.Pouch_Key_FRLG }; - PokeDex = BlockOfs[0] + 0x18; + PokeDex = 0x18; SeenFlagOffsets = Array.Empty(); Initialize(); @@ -106,14 +99,20 @@ public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false) : bas public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) { - LoadBlocks(out BlockOrder, out BlockOfs); - Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]); + 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(Data, BlockOfs[0] + 0x6) == 0; + Japanese = BitConverter.ToInt16(Small, 0x6) == 0; LegalKeyItems = Version switch { @@ -122,12 +121,12 @@ public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(d _ => Legal.Pouch_Key_FRLG }; - PokeDex = BlockOfs[0] + 0x18; + PokeDex = 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 } + GameVersion.RS => new[] { 0x938, 0x3A8C }, + GameVersion.E => new[] { 0x988, 0x3B24 }, + _ => new[] { 0x5F8, 0x3A18 } }; Initialize(); @@ -135,55 +134,44 @@ public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(d private void Initialize() { - // 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 (short 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]); - } + Box = 0; switch (Version) { case GameVersion.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; - EventFlag = BlockOfs[2] + 0x2A0; - EventConst = EventFlag + (EventFlagMax / 8); - OFS_Decorations = BlockOfs[3] + 0x7A0; - DaycareOffset = BlockOfs[4] + 0x11C; + OFS_PCItem = 0x0498; + OFS_PouchHeldItem = 0x0560; + OFS_PouchKeyItem = 0x05B0; + OFS_PouchBalls = 0x0600; + OFS_PouchTMHM = 0x0640; + OFS_PouchBerry = 0x0740; + EventFlag = 0x1220; + EventConst = 0x1340; + OFS_Decorations = 0x26A0; + DaycareOffset = 0x2F9C; break; case GameVersion.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; - EventFlag = BlockOfs[2] + 0x2F0; - EventConst = EventFlag + (EventFlagMax / 8); - OFS_Decorations = BlockOfs[3] + 0x834; - DaycareOffset = BlockOfs[4] + 0x1B0; + OFS_PCItem = 0x0498; + OFS_PouchHeldItem = 0x0560; + OFS_PouchKeyItem = 0x05D8; + OFS_PouchBalls = 0x0650; + OFS_PouchTMHM = 0x0690; + OFS_PouchBerry = 0x0790; + EventFlag = 0x1270; + EventConst = 0x139C; + OFS_Decorations = 0x2734; + DaycareOffset = 0x3030; break; case GameVersion.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; - EventFlag = BlockOfs[1] + 0xEE0; - EventConst = BlockOfs[2] + 0x80; - DaycareOffset = BlockOfs[4] + 0x100; + OFS_PCItem = 0x0298; + OFS_PouchHeldItem = 0x0310; + OFS_PouchKeyItem = 0x03B8; + OFS_PouchBalls = 0x0430; + OFS_PouchTMHM = 0x0464; + OFS_PouchBerry = 0x054C; + EventFlag = 0xEE0; + EventConst = 0x1000; + DaycareOffset = 0x2F80; break; default: throw new ArgumentException(nameof(Version)); @@ -195,7 +183,37 @@ private void Initialize() SeenFlagOffsets = SeenFlagOffsets.Where(z => z >= 0).ToArray(); } - private void LoadBlocks(out short[] blockOrder, out int[] blockOfs) + private void LoadBlocks(byte[] dest, short start, short end) + { + for (short i = start; i < end; i++) + { + int blockIndex = Array.IndexOf(BlockOrder, i); + if (blockIndex == -1) // block empty + continue; + + var sOfs = (blockIndex * SIZE_BLOCK) + ABO; + var dOfs = (i - start) * SIZE_BLOCK_USED; + var count = chunkLength[i]; + Buffer.BlockCopy(Data, sOfs, dest, dOfs, count); + } + } + + private void SaveBlocks(byte[] dest, short start, short end) + { + for (short i = start; i < end; i++) + { + int blockIndex = Array.IndexOf(BlockOrder, i); + if (blockIndex == -1) // block empty + continue; + + var sOfs = (blockIndex * SIZE_BLOCK) + ABO; + var dOfs = (i - start) * SIZE_BLOCK_USED; + var count = chunkLength[i]; + Buffer.BlockCopy(dest, dOfs, Data, sOfs, count); + } + } + + private void LoadBlocks(out short[] blockOrder) { var o1 = GetBlockOrder(0); if (Data.Length > SaveUtil.SIZE_G3RAWHALF) @@ -209,13 +227,6 @@ private void LoadBlocks(out short[] blockOrder, out int[] blockOfs) ActiveSAV = 0; blockOrder = o1; } - - blockOfs = new int[BLOCK_COUNT]; - for (short i = 0; i < BLOCK_COUNT; i++) - { - int index = Array.IndexOf(blockOrder, i); - blockOfs[i] = index < 0 ? int.MinValue : (index * SIZE_BLOCK) + ABO; - } } private short[] GetBlockOrder(int ofs) @@ -239,9 +250,9 @@ private int GetActiveSaveIndex(short[] BlockOrder1, short[] BlockOrder2) return count1 > count2 ? 0 : 1; } - public static GameVersion GetVersion(byte[] data, int block0Ofs) + public static GameVersion GetVersion(byte[] data, int offset = 0) { - uint GameCode = BitConverter.ToUInt32(data, block0Ofs + 0xAC); + uint GameCode = BitConverter.ToUInt32(data, offset + 0xAC); switch (GameCode) { case 1: return GameVersion.FRLG; // fixed value @@ -252,9 +263,9 @@ public static GameVersion GetVersion(byte[] data, int block0Ofs) // 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 // ^ byte pattern in Emerald saves, is all zero in Ruby/Sapphire as far as I can tell. // Some saves have had data @ 0x550 - if (BitConverter.ToUInt64(data, block0Ofs + 0xEE0) != 0) + if (BitConverter.ToUInt64(data, offset + 0xEE0) != 0) return GameVersion.E; - if (BitConverter.ToUInt64(data, block0Ofs + 0xEE8) != 0) + if (BitConverter.ToUInt64(data, offset + 0xEE8) != 0) return GameVersion.E; return GameVersion.RS; } @@ -263,25 +274,15 @@ public static GameVersion GetVersion(byte[] data, int block0Ofs) protected override byte[] GetFinalData() { // Copy Box data back - for (short i = 5; i < BLOCK_COUNT; i++) - { - int blockIndex = Array.IndexOf(BlockOrder, i); - if (blockIndex == -1) // block empty - continue; - Array.Copy(Data, Box + ((i - 5) * 0xF80), Data, (blockIndex * SIZE_BLOCK) + ABO, chunkLength[i]); - } - - SetChecksums(); - var result = new byte[Data.Length - SIZE_RESERVED]; - Buffer.BlockCopy(Data, 0, result, 0, result.Length); - return result; + SaveBlocks(Small, 0, 1); + SaveBlocks(Large, 1, 5); + SaveBlocks(Storage, 5, BLOCK_COUNT); + return base.GetFinalData(); } private int ActiveSAV; private int ABO => ActiveSAV*SIZE_BLOCK*0xE; private readonly short[] BlockOrder; - private readonly int[] BlockOfs; - public int GetBlockOffset(int block) => BlockOfs[block]; // Configuration protected override SaveFile CloneInternal() => new SAV3(Write(), Version); @@ -406,61 +407,61 @@ public override string ChecksumInfo public uint SecurityKey => Version switch { - GameVersion.E => BitConverter.ToUInt32(Data, BlockOfs[0] + 0xAC), - GameVersion.FRLG => BitConverter.ToUInt32(Data, BlockOfs[0] + 0xF20), + GameVersion.E => BitConverter.ToUInt32(Small, 0xAC), + GameVersion.FRLG => BitConverter.ToUInt32(Small, 0xF20), _ => 0u }; public override string OT { - get => GetString(BlockOfs[0], 0x10); + get => GetString(Small, 0, 0x10); set { int len = Japanese ? 5 : OTLength; - SetString(value, len, PadToSize: len, PadWith: 0xFF).CopyTo(Data, BlockOfs[0]); + SetString(value, len, PadToSize: len, PadWith: 0xFF).CopyTo(Small, 0); } } public override int Gender { - get => Data[BlockOfs[0] + 8]; - set => Data[BlockOfs[0] + 8] = (byte)value; + get => Small[8]; + set => Small[8] = (byte)value; } public override int TID { - get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xA + 0); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xA + 0); + get => BitConverter.ToUInt16(Small, 0xA); + set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xA); } public override int SID { - get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xC); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xC); + get => BitConverter.ToUInt16(Small, 0xC); + set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xC); } public override int PlayedHours { - get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xE); - set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xE); + get => BitConverter.ToUInt16(Small, 0xE); + set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xE); } public override int PlayedMinutes { - get => Data[BlockOfs[0] + 0x10]; - set => Data[BlockOfs[0] + 0x10] = (byte)value; + get => Small[0x10]; + set => Small[0x10] = (byte)value; } public override int PlayedSeconds { - get => Data[BlockOfs[0] + 0x11]; - set => Data[BlockOfs[0] + 0x11] = (byte)value; + get => Small[0x11]; + set => Small[0x11] = (byte)value; } public int PlayedFrames { - get => Data[BlockOfs[0] + 0x12]; - set => Data[BlockOfs[0] + 0x12] = (byte)value; + get => Small[0x12]; + set => Small[0x12] = (byte)value; } public override bool GetEventFlag(int flagNumber) @@ -469,11 +470,6 @@ public override bool GetEventFlag(int flagNumber) throw new ArgumentException($"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax})."); var start = EventFlag; - if (Version == GameVersion.FRLG && flagNumber >= 0x500) - { - flagNumber -= 0x500; - start = BlockOfs[2]; - } return GetFlag(start + (flagNumber >> 3), flagNumber & 7); } @@ -483,16 +479,31 @@ public override void SetEventFlag(int flagNumber, bool value) throw new ArgumentException($"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax})."); var start = EventFlag; - if (Version == GameVersion.FRLG && flagNumber >= 0x500) - { - flagNumber -= 0x500; - start = BlockOfs[2]; - } SetFlag(start + (flagNumber >> 3), flagNumber & 7, value); } - public ushort GetEventConst(int index) => BitConverter.ToUInt16(Data, EventConst + (index * 2)); - public void SetEventConst(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, EventConst + (index * 2)); + public override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex); + public override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Large, offset, bitIndex, value); + + public ushort GetEventConst(int index) => BitConverter.ToUInt16(Large, EventConst + (index * 2)); + public void SetEventConst(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Large, EventConst + (index * 2)); + + public override ushort[] GetEventConsts() + { + ushort[] Constants = new ushort[EventConstMax]; + for (int i = 0; i < Constants.Length; i++) + Constants[i] = GetEventConst(i); + return Constants; + } + + public override void SetEventConsts(ushort[] value) + { + if (value.Length != EventConstMax) + return; + + for (int i = 0; i < value.Length; i++) + SetEventConst(i, value[i]); + } public int Badges { @@ -535,8 +546,8 @@ public override uint Money switch (Version) { case GameVersion.RS: - case GameVersion.E: return BitConverter.ToUInt32(Data, BlockOfs[1] + 0x0490) ^ SecurityKey; - case GameVersion.FRLG: return BitConverter.ToUInt32(Data, BlockOfs[1] + 0x0290) ^ SecurityKey; + case GameVersion.E: return BitConverter.ToUInt32(Large, 0x0490) ^ SecurityKey; + case GameVersion.FRLG: return BitConverter.ToUInt32(Large, 0x0290) ^ SecurityKey; default: return 0; } } @@ -545,8 +556,8 @@ public override uint Money switch (Version) { case GameVersion.RS: - case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, BlockOfs[1] + 0x0490); break; - case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, BlockOfs[1] + 0x0290); break; + case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0490); break; + case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0290); break; } } } @@ -558,8 +569,8 @@ public uint Coin switch (Version) { case GameVersion.RS: - case GameVersion.E: return (ushort)(BitConverter.ToUInt16(Data, BlockOfs[1] + 0x0494) ^ SecurityKey); - case GameVersion.FRLG: return (ushort)(BitConverter.ToUInt16(Data, BlockOfs[1] + 0x0294) ^ SecurityKey); + case GameVersion.E: return (ushort)(BitConverter.ToUInt16(Large, 0x0494) ^ SecurityKey); + case GameVersion.FRLG: return (ushort)(BitConverter.ToUInt16(Large, 0x0294) ^ SecurityKey); default: return 0; } } @@ -570,31 +581,31 @@ public uint Coin switch (Version) { case GameVersion.RS: - case GameVersion.E: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Data, BlockOfs[1] + 0x0494); break; - case GameVersion.FRLG: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Data, BlockOfs[1] + 0x0294); break; + case GameVersion.E: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0494); break; + case GameVersion.FRLG: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0294); break; } } } public uint BP { - get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xEB8); + get => BitConverter.ToUInt16(Small, 0xEB8); set { if (value > 9999) value = 9999; - BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xEB8); + BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEB8); } } public uint BPEarned { - get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xEBA); + get => BitConverter.ToUInt16(Small, 0xEBA); set { if (value > 65535) value = 65535; - BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xEBA); + BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEBA); } } @@ -604,13 +615,13 @@ public uint BerryPowder { if (Version != GameVersion.FRLG) return 0; - return BitConverter.ToUInt32(Data, BlockOfs[0] + 0xAF8) ^ SecurityKey; + return BitConverter.ToUInt32(Small, 0xAF8) ^ SecurityKey; } set { if (Version != GameVersion.FRLG) return; - SetData(BitConverter.GetBytes(value ^ SecurityKey), BlockOfs[0] + 0xAF8); + SetData(Small, BitConverter.GetBytes(value ^ SecurityKey), 0xAF8); } } @@ -642,16 +653,16 @@ public override IReadOnlyList Inventory if (p.Type != InventoryType.PCItems) ((InventoryPouch3)p).SecurityKey = SecurityKey; } - return pouch.LoadAll(Data); + return pouch.LoadAll(Large); } - set => value.SaveAll(Data); + set => value.SaveAll(Large); } private int DaycareSlotSize => RS ? SIZE_STORED : SIZE_STORED + 0x3C; // 0x38 mail + 4 exp public override int DaycareSeedSize => E ? 8 : 4; // 32bit, 16bit - public override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Data, GetDaycareEXPOffset(slot)); - public override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Data, GetDaycareEXPOffset(slot)); - public override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Data, GetDaycareSlotOffset(loc, slot)); + public override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Large, GetDaycareEXPOffset(slot)); + public override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Large, GetDaycareEXPOffset(slot)); + public override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Large, GetDaycareSlotOffset(loc, slot)); public override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ } public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize); @@ -669,8 +680,8 @@ private int GetDaycareEXPOffset(int slot) public override string GetDaycareRNGSeed(int loc) { if (Version == GameVersion.E) - return BitConverter.ToUInt32(Data, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter - return BitConverter.ToUInt16(Data, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter + return BitConverter.ToUInt32(Large, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter + return BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter } public override void SetDaycareRNGSeed(int loc, string seed) @@ -678,12 +689,12 @@ public override void SetDaycareRNGSeed(int loc, string seed) if (Version == GameVersion.E) // egg pid { var val = Util.GetHexValue(seed); - BitConverter.GetBytes(val).CopyTo(Data, GetDaycareSlotOffset(0, 2)); + BitConverter.GetBytes(val).CopyTo(Large, GetDaycareSlotOffset(0, 2)); } // egg pid half { var val = (ushort)Util.GetHexValue(seed); - BitConverter.GetBytes(val).CopyTo(Data, GetDaycareEXPOffset(2)); + BitConverter.GetBytes(val).CopyTo(Large, GetDaycareEXPOffset(2)); } } @@ -695,14 +706,14 @@ public override int PartyCount int ofs = 0x34; if (GameVersion.FRLG != Version) ofs += 0x200; - return Data[BlockOfs[1] + ofs]; + return Large[ofs]; } protected set { int ofs = 0x34; if (GameVersion.FRLG != Version) ofs += 0x200; - Data[BlockOfs[1] + ofs] = (byte)value; + Large[ofs] = (byte)value; } } @@ -716,32 +727,50 @@ public override int GetPartyOffset(int slot) int ofs = 0x38; if (GameVersion.FRLG != Version) ofs += 0x200; - return BlockOfs[1] + ofs + (SIZE_PARTY * slot); + return ofs + (SIZE_PARTY * slot); } public override int CurrentBox { - get => Data[Box]; - set => Data[Box] = (byte)value; + get => Storage[0]; + set => Storage[0] = (byte)value; + } + + public override int GetBoxWallpaper(int box) + { + if (box > COUNT_BOX) + return box; + int offset = GetBoxWallpaperOffset(box); + return Storage[offset]; + } + + private const int COUNT_BOXNAME = 8 + 1; + + public override void SetBoxWallpaper(int box, int value) + { + if (box > COUNT_BOX) + return; + int offset = GetBoxWallpaperOffset(box); + Storage[offset] = (byte)value; } protected override int GetBoxWallpaperOffset(int box) { - int offset = GetBoxOffset(BoxCount); - offset += (BoxCount * 0x9) + box; + int offset = GetBoxOffset(COUNT_BOX); + offset += (COUNT_BOX * COUNT_BOXNAME) + box; return offset; } public override string GetBoxName(int box) { - int offset = GetBoxOffset(BoxCount); - return StringConverter3.GetString3(Data, offset + (box * 9), 9, Japanese); + int offset = GetBoxOffset(COUNT_BOX); + return StringConverter3.GetString3(Storage, offset + (box * COUNT_BOXNAME), COUNT_BOXNAME, Japanese); } public override void SetBoxName(int box, string value) { - int offset = GetBoxOffset(BoxCount); - SetString(value, 8).CopyTo(Data, offset + (box * 9)); + int offset = GetBoxOffset(COUNT_BOX); + SetString(value, COUNT_BOXNAME - 1).CopyTo(Storage, offset + (box * COUNT_BOXNAME)); } protected override PKM GetPKM(byte[] data) @@ -754,7 +783,7 @@ protected override byte[] DecryptPKM(byte[] data) return PokeCrypto.DecryptArray3(data); } - // Pokédex + /// Mirrors of the Seen Flags (inside the Large block) private int[] SeenFlagOffsets; protected override void SetDex(PKM pkm) @@ -784,13 +813,11 @@ private bool CanSetDex(int species) return false; if (Version == GameVersion.Invalid) return false; - if (BlockOfs.Any(z => z < 0)) - return false; return true; } - public uint DexPIDUnown { get => BitConverter.ToUInt32(Data, PokeDex + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, PokeDex + 0x4); } - public uint DexPIDSpinda { get => BitConverter.ToUInt32(Data, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, PokeDex + 0x8); } + public uint DexPIDUnown { get => BitConverter.ToUInt32(Small, PokeDex + 0x4); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x4); } + public uint DexPIDSpinda { get => BitConverter.ToUInt32(Small, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x8); } public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown); public override bool GetCaught(int species) @@ -798,7 +825,7 @@ public override bool GetCaught(int species) int bit = species - 1; int ofs = bit >> 3; int caughtOffset = PokeDex + 0x10; - return GetFlag(caughtOffset + ofs, bit & 7); + return FlagUtil.GetFlag(Small, caughtOffset + ofs, bit & 7); } public override void SetCaught(int species, bool caught) @@ -806,7 +833,7 @@ public override void SetCaught(int species, bool caught) int bit = species - 1; int ofs = bit >> 3; int caughtOffset = PokeDex + 0x10; - SetFlag(caughtOffset + ofs, bit & 7, caught); + FlagUtil.SetFlag(Small, caughtOffset + ofs, bit & 7, caught); } public override bool GetSeen(int species) @@ -814,7 +841,7 @@ public override bool GetSeen(int species) int bit = species - 1; int ofs = bit >> 3; int seenOffset = PokeDex + 0x44; - return GetFlag(seenOffset + ofs, bit & 7); + return FlagUtil.GetFlag(Small, seenOffset + ofs, bit & 7); } public override void SetSeen(int species, bool seen) @@ -822,32 +849,34 @@ public override void SetSeen(int species, bool seen) int bit = species - 1; int ofs = bit >> 3; + int seenOffset = PokeDex + 0x44; + FlagUtil.SetFlag(Small, seenOffset + ofs, bit & 7, seen); foreach (int o in SeenFlagOffsets) - SetFlag(o + ofs, bit & 7, seen); + FlagUtil.SetFlag(Large, o + ofs, bit & 7, seen); } public byte PokedexSort { - get => Data[PokeDex + 0x01]; - set => Data[PokeDex + 0x01] = value; + get => Small[PokeDex + 0x01]; + set => Small[PokeDex + 0x01] = value; } public byte PokedexMode { - get => Data[PokeDex + 0x01]; - set => Data[PokeDex + 0x01] = value; + get => Small[PokeDex + 0x01]; + set => Small[PokeDex + 0x01] = value; } public byte PokedexNationalMagicRSE { - get => Data[PokeDex + 0x02]; - set => Data[PokeDex + 0x02] = value; + get => Small[PokeDex + 0x02]; + set => Small[PokeDex + 0x02] = value; } public byte PokedexNationalMagicFRLG { - get => Data[PokeDex + 0x03]; - set => Data[PokeDex + 0x03] = value; + get => Small[PokeDex + 0x03]; + set => Small[PokeDex + 0x03] = value; } private const int PokedexNationalUnlockRSE = 0xDA; @@ -859,8 +888,6 @@ public bool NationalDex { get { - if (BlockOfs.Any(z => z < 0)) - return false; return Version switch // only check natdex status in Block0 { // enable nat dex option magic value @@ -871,9 +898,6 @@ public bool NationalDex } set { - if (BlockOfs.Any(z => z < 0)) - return; - PokedexMode = value ? 1 : 0; // mode switch (Version) { @@ -912,9 +936,9 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0, // Offset and checksum code based from // https://github.com/suloku/wc-tool by Suloku private const int SIZE_EBERRY = 0x530; - private const int OFFSET_EBERRY = 0x2E0; + private const int OFFSET_EBERRY = 0x2E80 + 0x2E0; - private uint EBerryChecksum => BitConverter.ToUInt32(Data, BlockOfs[4] + OFFSET_EBERRY + SIZE_EBERRY - 4); + private uint EBerryChecksum => BitConverter.ToUInt32(Large, OFFSET_EBERRY + SIZE_EBERRY - 4); private bool IsEBerryChecksumValid { get; set; } public string EBerryName @@ -923,7 +947,7 @@ public string EBerryName { if (!GameVersion.RS.Contains(Version) || !IsEBerryChecksumValid) return string.Empty; - return StringConverter3.GetString3(Data, BlockOfs[4] + OFFSET_EBERRY, 7, Japanese).Trim(); + return StringConverter3.GetString3(Large, OFFSET_EBERRY, 7, Japanese).Trim(); } } @@ -934,7 +958,7 @@ private void LoadEReaderBerryData() if (!GameVersion.RS.Contains(Version)) return; - byte[] data = GetData(BlockOfs[4] + OFFSET_EBERRY, SIZE_EBERRY - 4); + byte[] data = GetData(Large, OFFSET_EBERRY, SIZE_EBERRY - 4); // 8 bytes are 0x00 for chk calculation for (int i = 0; i < 8; i++) @@ -950,15 +974,13 @@ public RTC3 ClockInitial { if (FRLG) throw new ArgumentException(nameof(ClockInitial)); - int block0 = GetBlockOffset(0); - return new RTC3(GetData(block0 + 0x98, RTC3.Size)); + return new RTC3(GetData(Small, 0x98, RTC3.Size)); } set { if (FRLG) return; - int block0 = GetBlockOffset(0); - SetData(value.Data, block0 + 0x98); + SetData(Small, value.Data, 0x98); } } @@ -968,15 +990,13 @@ public RTC3 ClockElapsed { if (FRLG) throw new ArgumentException(nameof(ClockElapsed)); - int block0 = GetBlockOffset(0); - return new RTC3(GetData(block0 + 0xA0, RTC3.Size)); + return new RTC3(GetData(Small, 0xA0, RTC3.Size)); } set { if (FRLG) return; - int block0 = GetBlockOffset(0); - SetData(value.Data, block0 + 0xA0); + SetData(Small, value.Data, 0xA0); } } @@ -987,9 +1007,9 @@ public PokeBlock3Case PokeBlocks var ofs = PokeBlockOffset; if (ofs < 0) throw new ArgumentException($"Game does not support {nameof(PokeBlocks)}."); - return new PokeBlock3Case(Data, ofs); + return new PokeBlock3Case(Large, ofs); } - set => SetData(value.Write(), PokeBlockOffset); + set => SetData(Large, value.Write(), PokeBlockOffset); } private int PokeBlockOffset @@ -997,52 +1017,38 @@ private int PokeBlockOffset get { if (Version == GameVersion.E) - return BlockOfs[1] + 0x848; + return 0x848; if (Version == GameVersion.RS) - return BlockOfs[1] + 0x7F8; + return 0x7F8; return -1; } } public int GetMailOffset(int index) { - GetMailBlockOffset(Version, ref index, out int block, out int offset); - return (index * Mail3.SIZE) + GetBlockOffset(block) + offset; + var offset = Version switch + { + GameVersion.RS => 0x2B4C, + GameVersion.E => 0x2BE0, + _ => 0x2CD0, // FRLG + }; + return (index * Mail3.SIZE) + offset; } - private static void GetMailBlockOffset(GameVersion game, ref int index, out int block, out int offset) + private int ExternalEventFlags => Version switch { - block = 3; - if (game == GameVersion.E) - { - offset = 0xCE0; - } - else if (GameVersion.RS.Contains(game)) - { - offset = 0xC4C; - } - else // FRLG - { - if (index >= 12) - { - block = 4; - offset = 0; - index -= 12; - } - else - { - offset = 0xDD0; - } - } - } + GameVersion.RS => 0x312F, + GameVersion.E => 0x31C7, + _ => throw new IndexOutOfRangeException(), + }; public bool HasReceivedWishmkrJirachi { - get => GameVersion.RS.Contains(Version) && GetFlag(BlockOfs[4] + 0x2B1, 0); + get => !GameVersion.FRLG.Contains(Version) && GetFlag(ExternalEventFlags + 2, 0); set { - if (GameVersion.RS.Contains(Version)) - SetFlag(BlockOfs[4] + 0x2B1, 0, value); + if (!GameVersion.FRLG.Contains(Version)) + SetFlag(ExternalEventFlags + 2, 0, value); } } @@ -1060,13 +1066,13 @@ public DecorationInventory3 Decorations { if (Version == GameVersion.FRLG) throw new Exception(); - return Data.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure(); + return Large.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure(); } set { if (Version == GameVersion.FRLG) throw new Exception(); - SetData(value.ToBytes(), OFS_Decorations); + SetData(Large, value.ToBytes(), OFS_Decorations); } } } diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs index d8aa0ef85..4a41b7246 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Record3.cs @@ -7,30 +7,25 @@ public sealed class Record3 { private readonly SAV3 SAV; - public uint GetRecord(int record) => BitConverter.ToUInt32(SAV.Data, GetRecordOffset(record)) ^ GetKey(SAV); - public void SetRecord(int record, uint value) => SAV.SetData(BitConverter.GetBytes(value ^ GetKey(SAV)), GetRecordOffset(record)); + public uint GetRecord(int record) => BitConverter.ToUInt32(SAV.Large, GetRecordOffset(record)) ^ GetKey(SAV); + public void SetRecord(int record, uint value) => SAV.SetData(SAV.Large, BitConverter.GetBytes(value ^ GetKey(SAV)), GetRecordOffset(record)); private int GetRecordOffset(int record) { var baseOffset = GetOffset(SAV.Version); var offset = baseOffset + (4 * record); - - SAV3.GetLargeBlockOffset(offset, out var chunk, out var ofs); - return SAV.GetBlockOffset(chunk) + ofs; + return offset; } public Record3(SAV3 sav) => SAV = sav; - public static int GetOffset(GameVersion ver) + public static int GetOffset(GameVersion ver) => ver switch { - return ver switch - { - GameVersion.RS => 0x1540, - GameVersion.FRLG => 0x1200, - GameVersion.E => 0x159C, - _ => throw new ArgumentException(nameof(ver)) - }; - } + GameVersion.RS => 0x1540, + GameVersion.E => 0x159C, + GameVersion.FRLG => 0x1200, + _ => throw new ArgumentException(nameof(ver)) + }; public static uint GetKey(SAV3 sav) { diff --git a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs index 87f5c7a30..f2514ca19 100644 --- a/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs +++ b/PKHeX.Core/Saves/Substructures/Gen3/Roamer3.cs @@ -4,62 +4,62 @@ namespace PKHeX.Core { public sealed class Roamer3 : IContestStats, IContestStatsMutable { - private readonly SaveFile SAV; private readonly int Offset; public bool IsGlitched { get; } + private readonly byte[] Data; + private readonly SAV3 SAV; public Roamer3(SAV3 sav) { - SAV = sav; - Offset = sav.GetBlockOffset(4); - if (GameVersion.FRLG.Contains(SAV.Version)) - Offset += 0x250; - else if (SAV.Version == GameVersion.E) - Offset += 0x35C; - else // RS - Offset += 0x2C4; - IsGlitched = SAV.Version != GameVersion.E; + Data = (SAV = sav).Large; + Offset = sav.Version switch + { + GameVersion.RS => 0x3144, + GameVersion.E => 0x31DC, + _ => 0x30D0, // FRLG + }; + IsGlitched = sav.Version != GameVersion.E; } private uint IV32 { - get => BitConverter.ToUInt32(SAV.Data, Offset); - set => SAV.SetData(BitConverter.GetBytes(value), Offset); + get => BitConverter.ToUInt32(Data, Offset); + set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset); } public uint PID { - get => BitConverter.ToUInt32(SAV.Data, Offset + 4); - set => SAV.SetData(BitConverter.GetBytes(value), Offset + 4); + get => BitConverter.ToUInt32(Data, Offset + 4); + set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 4); } public int Species { - get => SpeciesConverter.GetG4Species(BitConverter.ToInt16(SAV.Data, Offset + 8)); - set => SAV.SetData(BitConverter.GetBytes((ushort)SpeciesConverter.GetG3Species(value)), Offset + 8); + get => SpeciesConverter.GetG4Species(BitConverter.ToInt16(Data, Offset + 8)); + set => SAV.SetData(Data, BitConverter.GetBytes((ushort)SpeciesConverter.GetG3Species(value)), Offset + 8); } public int HP_Current { - get => BitConverter.ToInt16(SAV.Data, Offset + 10); - set => SAV.SetData(BitConverter.GetBytes((short)value), Offset + 10); + get => BitConverter.ToInt16(Data, Offset + 10); + set => SAV.SetData(Data, BitConverter.GetBytes((short)value), Offset + 10); } public int CurrentLevel { - get => SAV.Data[Offset + 12]; - set => SAV.Data[Offset + 12] = (byte)value; + get => Data[Offset + 12]; + set => Data[Offset + 12] = (byte)value; } - public int Status { get => SAV.Data[Offset + 0x0D]; set => SAV.Data[Offset + 0x0D] = (byte)value; } + public int Status { get => Data[Offset + 0x0D]; set => Data[Offset + 0x0D] = (byte)value; } - public byte CNT_Cool { get => SAV.Data[Offset + 0x0E]; set => SAV.Data[Offset + 0x0E] = value; } - public byte CNT_Beauty { get => SAV.Data[Offset + 0x0F]; set => SAV.Data[Offset + 0x0F] = value; } - public byte CNT_Cute { get => SAV.Data[Offset + 0x10]; set => SAV.Data[Offset + 0x10] = value; } - public byte CNT_Smart { get => SAV.Data[Offset + 0x11]; set => SAV.Data[Offset + 0x11] = value; } - public byte CNT_Tough { get => SAV.Data[Offset + 0x12]; set => SAV.Data[Offset + 0x12] = value; } + public byte CNT_Cool { get => Data[Offset + 0x0E]; set => Data[Offset + 0x0E] = value; } + public byte CNT_Beauty { get => Data[Offset + 0x0F]; set => Data[Offset + 0x0F] = value; } + public byte CNT_Cute { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; } + public byte CNT_Smart { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = value; } + public byte CNT_Tough { get => Data[Offset + 0x12]; set => Data[Offset + 0x12] = value; } public byte CNT_Sheen { get => 0; set { } } - public bool Active { get => SAV.Data[Offset + 0x13] == 1; set => SAV.Data[Offset + 0x13] = value ? 1 : 0; } + public bool Active { get => Data[Offset + 0x13] == 1; set => Data[Offset + 0x13] = value ? 1 : 0; } // Derived Properties private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } diff --git a/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs b/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs index 21c090351..900a1e99c 100644 --- a/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs +++ b/PKHeX.Core/Saves/Substructures/Mail/Mail3.cs @@ -28,6 +28,7 @@ private void ResetData() public override ushort GetMessage(int index1, int index2) => BitConverter.ToUInt16(Data, ((index1 * 3) + index2) * 2); public override void SetMessage(int index1, int index2, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, ((index1 * 3) + index2) * 2); + public override void CopyTo(SaveFile sav) => sav.SetData(((SAV3)sav).Large, DataOffset); public override string AuthorName { diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.Designer.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.Designer.cs index 60799fc7c..735268f93 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.Designer.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.Designer.cs @@ -43,7 +43,7 @@ private void InitializeComponent() this.CB_TCM1 = new System.Windows.Forms.ComboBox(); this.NUD_Coins = new System.Windows.Forms.NumericUpDown(); this.L_Coins = new System.Windows.Forms.Label(); - this.TB_OTName = new System.Windows.Forms.TextBox(); + this.TB_RivalName = new System.Windows.Forms.TextBox(); this.L_TrainerName = new System.Windows.Forms.Label(); this.NUD_BP = new System.Windows.Forms.NumericUpDown(); this.L_BP = new System.Windows.Forms.Label(); @@ -177,7 +177,7 @@ private void InitializeComponent() this.TAB_Main.Controls.Add(this.GB_TCM); this.TAB_Main.Controls.Add(this.NUD_Coins); this.TAB_Main.Controls.Add(this.L_Coins); - this.TAB_Main.Controls.Add(this.TB_OTName); + this.TAB_Main.Controls.Add(this.TB_RivalName); this.TAB_Main.Controls.Add(this.L_TrainerName); this.TAB_Main.Controls.Add(this.NUD_BP); this.TAB_Main.Controls.Add(this.L_BP); @@ -310,16 +310,16 @@ private void InitializeComponent() this.L_Coins.Text = "Coins:"; this.L_Coins.TextAlign = System.Drawing.ContentAlignment.MiddleRight; // - // TB_OTName + // TB_RivalName // - this.TB_OTName.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.TB_OTName.Location = new System.Drawing.Point(120, 93); - this.TB_OTName.MaxLength = 7; - this.TB_OTName.Name = "TB_OTName"; - this.TB_OTName.Size = new System.Drawing.Size(63, 20); - this.TB_OTName.TabIndex = 31; - this.TB_OTName.Text = "WWWWWWW"; - this.TB_OTName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + this.TB_RivalName.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.TB_RivalName.Location = new System.Drawing.Point(120, 93); + this.TB_RivalName.MaxLength = 7; + this.TB_RivalName.Name = "TB_RivalName"; + this.TB_RivalName.Size = new System.Drawing.Size(63, 20); + this.TB_RivalName.TabIndex = 31; + this.TB_RivalName.Text = "WWWWWWW"; + this.TB_RivalName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; // // L_TrainerName // @@ -1112,7 +1112,7 @@ private void InitializeComponent() private System.Windows.Forms.Label L_B5Score; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label4; - private System.Windows.Forms.TextBox TB_OTName; + private System.Windows.Forms.TextBox TB_RivalName; private System.Windows.Forms.Label L_TrainerName; private System.Windows.Forms.NumericUpDown NUD_BP; private System.Windows.Forms.Label L_BP; diff --git a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs index 138e64bad..d81eab0c4 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/Gen3/SAV_Misc3.cs @@ -37,31 +37,22 @@ public SAV_Misc3(SaveFile sav) if (SAV.FRLG) { - TB_OTName.Text = SAV.GetString(SAV.GetBlockOffset(4) + 0xBCC, 8); + TB_RivalName.Text = SAV.GetString(SAV.Large, 0x3A4C, 8); + + // Trainer Card Species ComboBox[] cba = { CB_TCM1, CB_TCM2, CB_TCM3, CB_TCM4, CB_TCM5, CB_TCM6 }; - int[] HoennListMixed = { - 277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300, - 304,305,309,310,392,393,394,311,312,306,307,364,365,366,301,302,303,370,371,372,335,336,350,320,315,316, - 322,355,382,383,384,356,357,337,338,353,354,386,387,363,367,368,330,331,313,314, - 339,340,321,351,352,308,332,333,334,344,345,358,359,380,379,348,349,323,324, - 326,327,318,319,388,389,390,391,328,329,385,317,377,378,361,362,369,411,376,360, - 346,347,341,342,343,373,374,375,381,325,395,396,397,398,399,400, - 401,402,403,407,408,404,405,406,409,410 - }; - var speciesList = GameInfo.SpeciesDataSource.Where(v => v.Value <= SAV.MaxSpeciesID).Select(v => - new ComboItem (v.Text, v.Value < 252 ? v.Value : HoennListMixed[v.Value - 252]) - ).ToList(); - int ofsTCM = SAV.GetBlockOffset(2) + 0x106; + var legal = GameInfo.SpeciesDataSource.Where(v => v.Value <= SAV.MaxSpeciesID); + var speciesList = legal.Select(v => new ComboItem(v.Text, SpeciesConverter.GetG3Species(v.Value))).ToList(); for (int i = 0; i < cba.Length; i++) { cba[i].Items.Clear(); cba[i].InitializeBinding(); cba[i].DataSource = new BindingSource(speciesList, null); - cba[i].SelectedValue = (int)BitConverter.ToUInt16(SAV.Data, ofsTCM + (i << 1)); + cba[i].SelectedValue = SAV.GetEventConst(0x43 + i); } } else - { TB_OTName.Visible = L_TrainerName.Visible = GB_TCM.Visible = false; } + { TB_RivalName.Visible = L_TrainerName.Visible = GB_TCM.Visible = false; } NUD_Coins.Value = Math.Min(NUD_Coins.Maximum, SAV.Coin); } @@ -76,11 +67,10 @@ private void B_Save_Click(object sender, EventArgs e) SaveBattleFrontier(); if (SAV.FRLG) { - SAV.SetData(SAV.SetString(TB_OTName.Text, TB_OTName.MaxLength), SAV.GetBlockOffset(4) + 0xBCC); + SAV.SetData(SAV.Large, SAV.SetString(TB_RivalName.Text, TB_RivalName.MaxLength), 0x3A4C); ComboBox[] cba = { CB_TCM1, CB_TCM2, CB_TCM3, CB_TCM4, CB_TCM5, CB_TCM6 }; - int ofsTCM = SAV.GetBlockOffset(2) + 0x106; for (int i = 0; i < cba.Length; i++) - BitConverter.GetBytes((ushort)(int)cba[i].SelectedValue).CopyTo(SAV.Data, ofsTCM + (i << 1)); + SAV.SetEventConst(0x43 + i, (ushort)(int)cba[i].SelectedValue); } if (!SAV.RS) @@ -102,40 +92,40 @@ private void ReadJoyful() switch (SAV.Version) { case GameVersion.E: - JUMPS_IN_ROW = SAV.GetBlockOffset(0) + 0x1fc; - JUMPS_SCORE = SAV.GetBlockOffset(0) + 0x208; - JUMPS_5_IN_ROW = SAV.GetBlockOffset(0) + 0x200; + JUMPS_IN_ROW = 0x1fc; + JUMPS_SCORE = 0x208; + JUMPS_5_IN_ROW = 0x200; - BERRIES_IN_ROW = SAV.GetBlockOffset(0) + 0x210; - BERRIES_SCORE = SAV.GetBlockOffset(0) + 0x20c; - BERRIES_5_IN_ROW = SAV.GetBlockOffset(0) + 0x212; + BERRIES_IN_ROW = 0x210; + BERRIES_SCORE = 0x20c; + BERRIES_5_IN_ROW = 0x212; break; case GameVersion.FRLG: - JUMPS_IN_ROW = SAV.GetBlockOffset(0) + 0xB00; - JUMPS_SCORE = SAV.GetBlockOffset(0) + 0xB0C; - JUMPS_5_IN_ROW = SAV.GetBlockOffset(0) + 0xB04; + JUMPS_IN_ROW = 0xB00; + JUMPS_SCORE = 0xB0C; + JUMPS_5_IN_ROW = 0xB04; - BERRIES_IN_ROW = SAV.GetBlockOffset(0) + 0xB14; - BERRIES_SCORE = SAV.GetBlockOffset(0) + 0xB10; - BERRIES_5_IN_ROW = SAV.GetBlockOffset(0) + 0xB16; + BERRIES_IN_ROW = 0xB14; + BERRIES_SCORE = 0xB10; + BERRIES_5_IN_ROW = 0xB16; break; } - TB_J1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, JUMPS_IN_ROW)).ToString(); - TB_J2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, JUMPS_SCORE)).ToString(); - TB_J3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, JUMPS_5_IN_ROW)).ToString(); - TB_B1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, BERRIES_IN_ROW)).ToString(); - TB_B2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, BERRIES_SCORE)).ToString(); - TB_B3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, BERRIES_5_IN_ROW)).ToString(); + TB_J1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_IN_ROW)).ToString(); + TB_J2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_SCORE)).ToString(); + TB_J3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_5_IN_ROW)).ToString(); + TB_B1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_IN_ROW)).ToString(); + TB_B2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_SCORE)).ToString(); + TB_B3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_5_IN_ROW)).ToString(); } private void SaveJoyful() { - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J1.Text)).CopyTo(SAV.Data, JUMPS_IN_ROW); - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J2.Text)).CopyTo(SAV.Data, JUMPS_SCORE); - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J3.Text)).CopyTo(SAV.Data, JUMPS_5_IN_ROW); - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B1.Text)).CopyTo(SAV.Data, BERRIES_IN_ROW); - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B2.Text)).CopyTo(SAV.Data, BERRIES_SCORE); - BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B3.Text)).CopyTo(SAV.Data, BERRIES_5_IN_ROW); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J1.Text)).CopyTo(SAV.Small, JUMPS_IN_ROW); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J2.Text)).CopyTo(SAV.Small, JUMPS_SCORE); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J3.Text)).CopyTo(SAV.Small, JUMPS_5_IN_ROW); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B1.Text)).CopyTo(SAV.Small, BERRIES_IN_ROW); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B2.Text)).CopyTo(SAV.Small, BERRIES_SCORE); + BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B3.Text)).CopyTo(SAV.Small, BERRIES_5_IN_ROW); } #endregion @@ -234,9 +224,6 @@ private void SaveFerry() #endregion #region BattleFrontier - private int[] Symbols = null!; - private int ofsSymbols; - private Color[] SymbolColorA = null!; private Button[] SymbolButtonA = null!; private bool editingcont; private bool editingval; @@ -322,15 +309,16 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) SetValToSav = Array.IndexOf(BFV[BFF[Facility][0]], SetValToSav); if (SetValToSav < 0) return; - if (val > 9999) val = 9999; - BitConverter.GetBytes(val).CopyTo(SAV.Data, SAV.GetBlockOffset(0) + BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi)); + if (val > 9999) + val = 9999; + BitConverter.GetBytes(val).CopyTo(SAV.Small, BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi)); return; } if (SetValToSav == -1) { int p = BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi; - int offset = SAV.GetBlockOffset(0) + 0xCDC; - BitConverter.GetBytes((BitConverter.ToUInt32(SAV.Data, offset) & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p)).CopyTo(SAV.Data, offset); + const int offset = 0xCDC; + BitConverter.GetBytes((BitConverter.ToUInt32(SAV.Small, offset) & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p)).CopyTo(SAV.Small, offset); return; } if (!SetSavToVal) @@ -339,11 +327,12 @@ private void StatAddrControl(int SetValToSav = -2, bool SetSavToVal = false) editingval = true; for (int i = 0; i < BFV[BFF[Facility][0]].Length; i++) { - int vali = BitConverter.ToUInt16(SAV.Data, SAV.GetBlockOffset(0) + BFF[Facility][2 + i] + (4 * BattleType) + (2 * RBi)); - if (vali > 9999) vali = 9999; + int vali = BitConverter.ToUInt16(SAV.Small, BFF[Facility][2 + i] + (4 * BattleType) + (2 * RBi)); + if (vali > 9999) + vali = 9999; StatNUDA[BFV[BFF[Facility][0]][i]].Value = vali; } - CHK_Continue.Checked = (BitConverter.ToUInt32(SAV.Data, SAV.GetBlockOffset(0) + 0xCDC) & 1 << (BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi)) != 0; + CHK_Continue.Checked = (BitConverter.ToUInt32(SAV.Small, 0xCDC) & 1 << (BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi)) != 0; editingval = false; } @@ -395,14 +384,8 @@ private void ReadBattleFrontier() StatNUDA = new[] { NUD_Stat0, NUD_Stat1, NUD_Stat2, NUD_Stat3 }; StatLabelA = new[] { L_Stat0, L_Stat1, L_Stat2, L_Stat3 }; StatRBA = new[] { RB_Stats3_01, RB_Stats3_02 }; - SymbolColorA = new[] { Color.Transparent, Color.Silver, Color.Silver, Color.Gold }; SymbolButtonA = new[] { BTN_SymbolA, BTN_SymbolT, BTN_SymbolS, BTN_SymbolG, BTN_SymbolK, BTN_SymbolL, BTN_SymbolB }; - ofsSymbols = SAV.GetBlockOffset(2) + 0x408; - int iSymbols = BitConverter.ToInt32(SAV.Data, ofsSymbols) >> 4 & 0x7FFF; - CHK_ActivatePass.Checked = (iSymbols >> 14 & 1) != 0; - Symbols = new int[7]; - for (int i = 0; i < 7; i++) - Symbols[i] = iSymbols >> i * 2 & 3; + CHK_ActivatePass.Checked = SAV.GetEventFlag(0x860 + 0x72); SetFrontierSymbols(); CB_Stats1.Items.Clear(); @@ -416,33 +399,39 @@ private void ReadBattleFrontier() private void SetFrontierSymbols() { for (int i = 0; i < SymbolButtonA.Length; i++) - SymbolButtonA[i].BackColor = SymbolColorA[Symbols[i]]; + { + var flagIndex = 0x860 + 0x64 + (i * 2); + var silver = SAV.GetEventFlag(flagIndex); + var gold = SAV.GetEventFlag(flagIndex + 1); + var value = silver ? gold ? Color.Gold : Color.Silver : Color.Transparent; + SymbolButtonA[i].BackColor = value; + } } private void SaveBattleFrontier() { - uint iSymbols = 0; for (int i = 0; i < 7; i++) - iSymbols |= (uint)((Symbols[i] & 3) << i * 2); - if (CHK_ActivatePass.Checked) - iSymbols |= 1 << 14; + { + var color = SymbolButtonA[i].BackColor; + bool silver = color != Color.Transparent; + bool gold = color == Color.Gold; - uint val = (uint)((BitConverter.ToUInt32(SAV.Data, ofsSymbols) & ~(0x7FFF << 4)) | (iSymbols & 0x7FFF) << 4); - BitConverter.GetBytes(val).CopyTo(SAV.Data, ofsSymbols); + var flagIndex = 0x860 + 0x64 + (i * 2); + SAV.SetEventFlag(flagIndex, silver); + SAV.SetEventFlag(flagIndex, gold); + } + SAV.SetEventFlag(0x860 + 0x72, CHK_ActivatePass.Checked); } private void BTN_Symbol_Click(object sender, EventArgs e) { - int index = Array.IndexOf(SymbolButtonA, sender); - if (index < 0) + var match = Array.Find(SymbolButtonA, z => z == sender); + if (match == null) return; - // 0 (none) | 1 (silver) | 2 (silver) | 3 (gold) - // bit rotation 00 -> 01 -> 11 -> 00 - if (Symbols[index] == 1) Symbols[index] = 3; - else Symbols[index] = (Symbols[index] + 1) & 3; - - SetFrontierSymbols(); + var color = match.BackColor; + color = color == Color.Transparent ? Color.Silver : color == Color.Silver ? Color.Gold : Color.Transparent; + match.BackColor = color; } #endregion diff --git a/PKHeX.WinForms/Subforms/Save Editors/SAV_MailBox.cs b/PKHeX.WinForms/Subforms/Save Editors/SAV_MailBox.cs index ebde77cb4..37a6033b2 100644 --- a/PKHeX.WinForms/Subforms/Save Editors/SAV_MailBox.cs +++ b/PKHeX.WinForms/Subforms/Save Editors/SAV_MailBox.cs @@ -70,7 +70,7 @@ public SAV_MailBox(SaveFile sav) for (int i = 0; i < m.Length; i++) { var ofs = sav3.GetMailOffset(i); - var data = sav.GetData(ofs, Mail3.SIZE); + var data = sav3.Large.Slice(ofs, Mail3.SIZE); m[i] = new Mail3(data, ofs, sav3.Japanese); } @@ -174,16 +174,6 @@ public SAV_MailBox(SaveFile sav) LB_PCBOX.SelectedIndex = 0; } - private readonly int[] HoennListMixed = { - 277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300, - 304,305,309,310,392,393,394,311,312,306,307,364,365,366,301,302,303,370,371,372,335,336,350,320,315,316, - 322,355,382,383,384,356,357,337,338,353,354,386,387,363,367,368,330,331,313,314, - 339,340,321,351,352,308,332,333,334,344,345,358,359,380,379,348,349,323,324, - 326,327,318,319,388,389,390,391,328,329,385,317,377,378,361,362,369,411,376,360, - 346,347,341,342,343,373,374,375,381,325,395,396,397,398,399,400, - 401,402,403,407,408,404,405,406,409,410 - }; - private void LoadList() { if (entry < PartyBoxCount) MakePartyList(); @@ -308,7 +298,7 @@ private void TempSave() } if (Gen == 3) { - mail.AppearPKM = v < 252 ? v : HoennListMixed[v - 252]; + mail.AppearPKM = SpeciesConverter.GetG3Species(v); return; } @@ -544,15 +534,7 @@ private void LoadMail() } if (Gen == 3) { - if (v < 252) - { - AppearPKMs[0].SelectedValue = v; - } - else - { - v = Array.IndexOf(HoennListMixed, v); - AppearPKMs[0].SelectedValue = v >= 0 ? (252 + v) : 0; - } + AppearPKMs[0].SelectedValue = SpeciesConverter.GetG4Species(v); editing = false; return; }