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.
This commit is contained in:
Kurt 2021-03-07 23:22:07 -08:00
parent cb7ee8a64e
commit 49541e6073
9 changed files with 370 additions and 397 deletions

View file

@ -70,7 +70,7 @@ namespace PKHeX.Core
return None; return None;
return new List<SlotInfoMisc> return new List<SlotInfoMisc>
{ {
new(sav.Data, 0, sav.GetBlockOffset(4) + 0xE18) {Type = StorageSlotType.Daycare } new(sav.Large, 0, 0x3C98) {Type = StorageSlotType.Daycare }
}; };
} }

View file

@ -19,7 +19,7 @@ namespace PKHeX.Core
Slot = slot; Slot = slot;
Offset = offset; Offset = offset;
PartyFormat = party; 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) public SlotInfoMisc(byte[] data, int slot, int offset, bool party = false)

View file

@ -18,24 +18,44 @@ namespace PKHeX.Core
public bool Korean => false; public bool Korean => false;
public bool IndeterminateGame => Version == GameVersion.Unknown; public bool IndeterminateGame => Version == GameVersion.Unknown;
/* SAV3 Structure: // Similar to future games, the Generation 3 Mainline save files are comprised of two separate objects:
* 0xE000 per save file // Object 1 - Small Block, containing misc configuration data & the Pokédex.
* 14 blocks @ 0x1000 each. // Object 2 - Large Block, containing everything else that isn't PC Storage system data.
* Blocks do not use all 0x1000 bytes allocated. // Object 3 - Storage Block, containing all the data for the PC storage system.
* Via: http://bulbapedia.bulbagarden.net/wiki/Save_data_structure_in_Generation_III
*/ // 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 SIZE_BLOCK = 0x1000;
private const int BLOCK_COUNT = 14; 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; 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 = 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, // 1 | Large Block Part 1
0xf80, // 2 | Large Block Part 2 0xf80, // 2 | Large Block Part 2
0xf80, // 3 | Large Block Part 3 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, // 5 | PC Block 0
0xf80, // 6 | PC Block 1 0xf80, // 6 | PC Block 1
0xf80, // 7 | PC Block 2 0xf80, // 7 | PC Block 2
@ -47,36 +67,11 @@ namespace PKHeX.Core
0x7d0 // D | PC Block 8 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; private PersonalTable _personal;
public override PersonalTable Personal => _personal; public override PersonalTable Personal => _personal;
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS; public override IReadOnlyList<ushort> 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 Version = version switch
{ {
@ -87,9 +82,7 @@ namespace PKHeX.Core
_personal = SaveUtil.GetG3Personal(Version); _personal = SaveUtil.GetG3Personal(Version);
Japanese = japanese; Japanese = japanese;
LoadBlocks(out BlockOrder, out BlockOfs); BlockOrder = Array.Empty<short>();
// spoof block offsets
BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray();
LegalKeyItems = Version switch LegalKeyItems = Version switch
{ {
@ -97,7 +90,7 @@ namespace PKHeX.Core
GameVersion.E => Legal.Pouch_Key_E, GameVersion.E => Legal.Pouch_Key_E,
_ => Legal.Pouch_Key_FRLG _ => Legal.Pouch_Key_FRLG
}; };
PokeDex = BlockOfs[0] + 0x18; PokeDex = 0x18;
SeenFlagOffsets = Array.Empty<int>(); SeenFlagOffsets = Array.Empty<int>();
Initialize(); Initialize();
@ -106,14 +99,20 @@ namespace PKHeX.Core
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data) public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
{ {
LoadBlocks(out BlockOrder, out BlockOfs); LoadBlocks(out BlockOrder);
Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Data, BlockOfs[0]);
// 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); _personal = SaveUtil.GetG3Personal(Version);
// Japanese games are limited to 5 character OT names; any unused characters are 0xFF. // 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 // 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. // 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 LegalKeyItems = Version switch
{ {
@ -122,12 +121,12 @@ namespace PKHeX.Core
_ => Legal.Pouch_Key_FRLG _ => Legal.Pouch_Key_FRLG
}; };
PokeDex = BlockOfs[0] + 0x18; PokeDex = 0x18;
SeenFlagOffsets = Version switch SeenFlagOffsets = Version switch
{ {
GameVersion.RS => new[] { PokeDex + 0x44, BlockOfs[1] + 0x938, BlockOfs[4] + 0xC0C }, GameVersion.RS => new[] { 0x938, 0x3A8C },
GameVersion.E => new[] { PokeDex + 0x44, BlockOfs[1] + 0x988, BlockOfs[4] + 0xCA4 }, GameVersion.E => new[] { 0x988, 0x3B24 },
_ => new[] { PokeDex + 0x44, BlockOfs[1] + 0x5F8, BlockOfs[4] + 0xB98 } _ => new[] { 0x5F8, 0x3A18 }
}; };
Initialize(); Initialize();
@ -135,55 +134,44 @@ namespace PKHeX.Core
private void Initialize() private void Initialize()
{ {
// Set up PC data buffer beyond end of save file. Box = 0;
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]);
}
switch (Version) switch (Version)
{ {
case GameVersion.RS: case GameVersion.RS:
OFS_PCItem = BlockOfs[1] + 0x0498; OFS_PCItem = 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560; OFS_PouchHeldItem = 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05B0; OFS_PouchKeyItem = 0x05B0;
OFS_PouchBalls = BlockOfs[1] + 0x0600; OFS_PouchBalls = 0x0600;
OFS_PouchTMHM = BlockOfs[1] + 0x0640; OFS_PouchTMHM = 0x0640;
OFS_PouchBerry = BlockOfs[1] + 0x0740; OFS_PouchBerry = 0x0740;
EventFlag = BlockOfs[2] + 0x2A0; EventFlag = 0x1220;
EventConst = EventFlag + (EventFlagMax / 8); EventConst = 0x1340;
OFS_Decorations = BlockOfs[3] + 0x7A0; OFS_Decorations = 0x26A0;
DaycareOffset = BlockOfs[4] + 0x11C; DaycareOffset = 0x2F9C;
break; break;
case GameVersion.E: case GameVersion.E:
OFS_PCItem = BlockOfs[1] + 0x0498; OFS_PCItem = 0x0498;
OFS_PouchHeldItem = BlockOfs[1] + 0x0560; OFS_PouchHeldItem = 0x0560;
OFS_PouchKeyItem = BlockOfs[1] + 0x05D8; OFS_PouchKeyItem = 0x05D8;
OFS_PouchBalls = BlockOfs[1] + 0x0650; OFS_PouchBalls = 0x0650;
OFS_PouchTMHM = BlockOfs[1] + 0x0690; OFS_PouchTMHM = 0x0690;
OFS_PouchBerry = BlockOfs[1] + 0x0790; OFS_PouchBerry = 0x0790;
EventFlag = BlockOfs[2] + 0x2F0; EventFlag = 0x1270;
EventConst = EventFlag + (EventFlagMax / 8); EventConst = 0x139C;
OFS_Decorations = BlockOfs[3] + 0x834; OFS_Decorations = 0x2734;
DaycareOffset = BlockOfs[4] + 0x1B0; DaycareOffset = 0x3030;
break; break;
case GameVersion.FRLG: case GameVersion.FRLG:
OFS_PCItem = BlockOfs[1] + 0x0298; OFS_PCItem = 0x0298;
OFS_PouchHeldItem = BlockOfs[1] + 0x0310; OFS_PouchHeldItem = 0x0310;
OFS_PouchKeyItem = BlockOfs[1] + 0x03B8; OFS_PouchKeyItem = 0x03B8;
OFS_PouchBalls = BlockOfs[1] + 0x0430; OFS_PouchBalls = 0x0430;
OFS_PouchTMHM = BlockOfs[1] + 0x0464; OFS_PouchTMHM = 0x0464;
OFS_PouchBerry = BlockOfs[1] + 0x054C; OFS_PouchBerry = 0x054C;
EventFlag = BlockOfs[1] + 0xEE0; EventFlag = 0xEE0;
EventConst = BlockOfs[2] + 0x80; EventConst = 0x1000;
DaycareOffset = BlockOfs[4] + 0x100; DaycareOffset = 0x2F80;
break; break;
default: default:
throw new ArgumentException(nameof(Version)); throw new ArgumentException(nameof(Version));
@ -195,7 +183,37 @@ namespace PKHeX.Core
SeenFlagOffsets = SeenFlagOffsets.Where(z => z >= 0).ToArray(); 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); var o1 = GetBlockOrder(0);
if (Data.Length > SaveUtil.SIZE_G3RAWHALF) if (Data.Length > SaveUtil.SIZE_G3RAWHALF)
@ -209,13 +227,6 @@ namespace PKHeX.Core
ActiveSAV = 0; ActiveSAV = 0;
blockOrder = o1; 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) private short[] GetBlockOrder(int ofs)
@ -239,9 +250,9 @@ namespace PKHeX.Core
return count1 > count2 ? 0 : 1; 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) switch (GameCode)
{ {
case 1: return GameVersion.FRLG; // fixed value case 1: return GameVersion.FRLG; // fixed value
@ -252,9 +263,9 @@ namespace PKHeX.Core
// 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00 // 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. // ^ byte pattern in Emerald saves, is all zero in Ruby/Sapphire as far as I can tell.
// Some saves have had data @ 0x550 // Some saves have had data @ 0x550
if (BitConverter.ToUInt64(data, block0Ofs + 0xEE0) != 0) if (BitConverter.ToUInt64(data, offset + 0xEE0) != 0)
return GameVersion.E; return GameVersion.E;
if (BitConverter.ToUInt64(data, block0Ofs + 0xEE8) != 0) if (BitConverter.ToUInt64(data, offset + 0xEE8) != 0)
return GameVersion.E; return GameVersion.E;
return GameVersion.RS; return GameVersion.RS;
} }
@ -263,25 +274,15 @@ namespace PKHeX.Core
protected override byte[] GetFinalData() protected override byte[] GetFinalData()
{ {
// Copy Box data back // Copy Box data back
for (short i = 5; i < BLOCK_COUNT; i++) SaveBlocks(Small, 0, 1);
{ SaveBlocks(Large, 1, 5);
int blockIndex = Array.IndexOf(BlockOrder, i); SaveBlocks(Storage, 5, BLOCK_COUNT);
if (blockIndex == -1) // block empty return base.GetFinalData();
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;
} }
private int ActiveSAV; private int ActiveSAV;
private int ABO => ActiveSAV*SIZE_BLOCK*0xE; private int ABO => ActiveSAV*SIZE_BLOCK*0xE;
private readonly short[] BlockOrder; private readonly short[] BlockOrder;
private readonly int[] BlockOfs;
public int GetBlockOffset(int block) => BlockOfs[block];
// Configuration // Configuration
protected override SaveFile CloneInternal() => new SAV3(Write(), Version); protected override SaveFile CloneInternal() => new SAV3(Write(), Version);
@ -406,61 +407,61 @@ namespace PKHeX.Core
public uint SecurityKey => Version switch public uint SecurityKey => Version switch
{ {
GameVersion.E => BitConverter.ToUInt32(Data, BlockOfs[0] + 0xAC), GameVersion.E => BitConverter.ToUInt32(Small, 0xAC),
GameVersion.FRLG => BitConverter.ToUInt32(Data, BlockOfs[0] + 0xF20), GameVersion.FRLG => BitConverter.ToUInt32(Small, 0xF20),
_ => 0u _ => 0u
}; };
public override string OT public override string OT
{ {
get => GetString(BlockOfs[0], 0x10); get => GetString(Small, 0, 0x10);
set set
{ {
int len = Japanese ? 5 : OTLength; 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 public override int Gender
{ {
get => Data[BlockOfs[0] + 8]; get => Small[8];
set => Data[BlockOfs[0] + 8] = (byte)value; set => Small[8] = (byte)value;
} }
public override int TID public override int TID
{ {
get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xA + 0); get => BitConverter.ToUInt16(Small, 0xA);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xA + 0); set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xA);
} }
public override int SID public override int SID
{ {
get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xC); get => BitConverter.ToUInt16(Small, 0xC);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xC); set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xC);
} }
public override int PlayedHours public override int PlayedHours
{ {
get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xE); get => BitConverter.ToUInt16(Small, 0xE);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xE); set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xE);
} }
public override int PlayedMinutes public override int PlayedMinutes
{ {
get => Data[BlockOfs[0] + 0x10]; get => Small[0x10];
set => Data[BlockOfs[0] + 0x10] = (byte)value; set => Small[0x10] = (byte)value;
} }
public override int PlayedSeconds public override int PlayedSeconds
{ {
get => Data[BlockOfs[0] + 0x11]; get => Small[0x11];
set => Data[BlockOfs[0] + 0x11] = (byte)value; set => Small[0x11] = (byte)value;
} }
public int PlayedFrames public int PlayedFrames
{ {
get => Data[BlockOfs[0] + 0x12]; get => Small[0x12];
set => Data[BlockOfs[0] + 0x12] = (byte)value; set => Small[0x12] = (byte)value;
} }
public override bool GetEventFlag(int flagNumber) public override bool GetEventFlag(int flagNumber)
@ -469,11 +470,6 @@ namespace PKHeX.Core
throw new ArgumentException($"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax})."); throw new ArgumentException($"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax}).");
var start = EventFlag; var start = EventFlag;
if (Version == GameVersion.FRLG && flagNumber >= 0x500)
{
flagNumber -= 0x500;
start = BlockOfs[2];
}
return GetFlag(start + (flagNumber >> 3), flagNumber & 7); return GetFlag(start + (flagNumber >> 3), flagNumber & 7);
} }
@ -483,16 +479,31 @@ namespace PKHeX.Core
throw new ArgumentException($"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax})."); throw new ArgumentException($"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax}).");
var start = EventFlag; var start = EventFlag;
if (Version == GameVersion.FRLG && flagNumber >= 0x500)
{
flagNumber -= 0x500;
start = BlockOfs[2];
}
SetFlag(start + (flagNumber >> 3), flagNumber & 7, value); SetFlag(start + (flagNumber >> 3), flagNumber & 7, value);
} }
public ushort GetEventConst(int index) => BitConverter.ToUInt16(Data, EventConst + (index * 2)); public override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex);
public void SetEventConst(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Data, EventConst + (index * 2)); 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 public int Badges
{ {
@ -535,8 +546,8 @@ namespace PKHeX.Core
switch (Version) switch (Version)
{ {
case GameVersion.RS: case GameVersion.RS:
case GameVersion.E: return BitConverter.ToUInt32(Data, BlockOfs[1] + 0x0490) ^ SecurityKey; case GameVersion.E: return BitConverter.ToUInt32(Large, 0x0490) ^ SecurityKey;
case GameVersion.FRLG: return BitConverter.ToUInt32(Data, BlockOfs[1] + 0x0290) ^ SecurityKey; case GameVersion.FRLG: return BitConverter.ToUInt32(Large, 0x0290) ^ SecurityKey;
default: return 0; default: return 0;
} }
} }
@ -545,8 +556,8 @@ namespace PKHeX.Core
switch (Version) switch (Version)
{ {
case GameVersion.RS: case GameVersion.RS:
case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, BlockOfs[1] + 0x0490); break; case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0490); break;
case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Data, BlockOfs[1] + 0x0290); break; case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0290); break;
} }
} }
} }
@ -558,8 +569,8 @@ namespace PKHeX.Core
switch (Version) switch (Version)
{ {
case GameVersion.RS: case GameVersion.RS:
case GameVersion.E: return (ushort)(BitConverter.ToUInt16(Data, BlockOfs[1] + 0x0494) ^ SecurityKey); case GameVersion.E: return (ushort)(BitConverter.ToUInt16(Large, 0x0494) ^ SecurityKey);
case GameVersion.FRLG: return (ushort)(BitConverter.ToUInt16(Data, BlockOfs[1] + 0x0294) ^ SecurityKey); case GameVersion.FRLG: return (ushort)(BitConverter.ToUInt16(Large, 0x0294) ^ SecurityKey);
default: return 0; default: return 0;
} }
} }
@ -570,31 +581,31 @@ namespace PKHeX.Core
switch (Version) switch (Version)
{ {
case GameVersion.RS: case GameVersion.RS:
case GameVersion.E: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Data, BlockOfs[1] + 0x0494); break; case GameVersion.E: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0494); break;
case GameVersion.FRLG: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Data, BlockOfs[1] + 0x0294); break; case GameVersion.FRLG: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0294); break;
} }
} }
} }
public uint BP public uint BP
{ {
get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xEB8); get => BitConverter.ToUInt16(Small, 0xEB8);
set set
{ {
if (value > 9999) if (value > 9999)
value = 9999; value = 9999;
BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xEB8); BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEB8);
} }
} }
public uint BPEarned public uint BPEarned
{ {
get => BitConverter.ToUInt16(Data, BlockOfs[0] + 0xEBA); get => BitConverter.ToUInt16(Small, 0xEBA);
set set
{ {
if (value > 65535) if (value > 65535)
value = 65535; value = 65535;
BitConverter.GetBytes((ushort)value).CopyTo(Data, BlockOfs[0] + 0xEBA); BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEBA);
} }
} }
@ -604,13 +615,13 @@ namespace PKHeX.Core
{ {
if (Version != GameVersion.FRLG) if (Version != GameVersion.FRLG)
return 0; return 0;
return BitConverter.ToUInt32(Data, BlockOfs[0] + 0xAF8) ^ SecurityKey; return BitConverter.ToUInt32(Small, 0xAF8) ^ SecurityKey;
} }
set set
{ {
if (Version != GameVersion.FRLG) if (Version != GameVersion.FRLG)
return; return;
SetData(BitConverter.GetBytes(value ^ SecurityKey), BlockOfs[0] + 0xAF8); SetData(Small, BitConverter.GetBytes(value ^ SecurityKey), 0xAF8);
} }
} }
@ -642,16 +653,16 @@ namespace PKHeX.Core
if (p.Type != InventoryType.PCItems) if (p.Type != InventoryType.PCItems)
((InventoryPouch3)p).SecurityKey = SecurityKey; ((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 private int DaycareSlotSize => RS ? SIZE_STORED : SIZE_STORED + 0x3C; // 0x38 mail + 4 exp
public override int DaycareSeedSize => E ? 8 : 4; // 32bit, 16bit public override int DaycareSeedSize => E ? 8 : 4; // 32bit, 16bit
public override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Data, GetDaycareEXPOffset(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(Data, 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(Data, GetDaycareSlotOffset(loc, 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 void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ }
public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize); public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize);
@ -669,8 +680,8 @@ namespace PKHeX.Core
public override string GetDaycareRNGSeed(int loc) public override string GetDaycareRNGSeed(int loc)
{ {
if (Version == GameVersion.E) if (Version == GameVersion.E)
return BitConverter.ToUInt32(Data, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter return BitConverter.ToUInt32(Large, 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.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter
} }
public override void SetDaycareRNGSeed(int loc, string seed) public override void SetDaycareRNGSeed(int loc, string seed)
@ -678,12 +689,12 @@ namespace PKHeX.Core
if (Version == GameVersion.E) // egg pid if (Version == GameVersion.E) // egg pid
{ {
var val = Util.GetHexValue(seed); var val = Util.GetHexValue(seed);
BitConverter.GetBytes(val).CopyTo(Data, GetDaycareSlotOffset(0, 2)); BitConverter.GetBytes(val).CopyTo(Large, GetDaycareSlotOffset(0, 2));
} }
// egg pid half // egg pid half
{ {
var val = (ushort)Util.GetHexValue(seed); var val = (ushort)Util.GetHexValue(seed);
BitConverter.GetBytes(val).CopyTo(Data, GetDaycareEXPOffset(2)); BitConverter.GetBytes(val).CopyTo(Large, GetDaycareEXPOffset(2));
} }
} }
@ -695,14 +706,14 @@ namespace PKHeX.Core
int ofs = 0x34; int ofs = 0x34;
if (GameVersion.FRLG != Version) if (GameVersion.FRLG != Version)
ofs += 0x200; ofs += 0x200;
return Data[BlockOfs[1] + ofs]; return Large[ofs];
} }
protected set protected set
{ {
int ofs = 0x34; int ofs = 0x34;
if (GameVersion.FRLG != Version) if (GameVersion.FRLG != Version)
ofs += 0x200; ofs += 0x200;
Data[BlockOfs[1] + ofs] = (byte)value; Large[ofs] = (byte)value;
} }
} }
@ -716,32 +727,50 @@ namespace PKHeX.Core
int ofs = 0x38; int ofs = 0x38;
if (GameVersion.FRLG != Version) if (GameVersion.FRLG != Version)
ofs += 0x200; ofs += 0x200;
return BlockOfs[1] + ofs + (SIZE_PARTY * slot); return ofs + (SIZE_PARTY * slot);
} }
public override int CurrentBox public override int CurrentBox
{ {
get => Data[Box]; get => Storage[0];
set => Data[Box] = (byte)value; 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) protected override int GetBoxWallpaperOffset(int box)
{ {
int offset = GetBoxOffset(BoxCount); int offset = GetBoxOffset(COUNT_BOX);
offset += (BoxCount * 0x9) + box; offset += (COUNT_BOX * COUNT_BOXNAME) + box;
return offset; return offset;
} }
public override string GetBoxName(int box) public override string GetBoxName(int box)
{ {
int offset = GetBoxOffset(BoxCount); int offset = GetBoxOffset(COUNT_BOX);
return StringConverter3.GetString3(Data, offset + (box * 9), 9, Japanese); return StringConverter3.GetString3(Storage, offset + (box * COUNT_BOXNAME), COUNT_BOXNAME, Japanese);
} }
public override void SetBoxName(int box, string value) public override void SetBoxName(int box, string value)
{ {
int offset = GetBoxOffset(BoxCount); int offset = GetBoxOffset(COUNT_BOX);
SetString(value, 8).CopyTo(Data, offset + (box * 9)); SetString(value, COUNT_BOXNAME - 1).CopyTo(Storage, offset + (box * COUNT_BOXNAME));
} }
protected override PKM GetPKM(byte[] data) protected override PKM GetPKM(byte[] data)
@ -754,7 +783,7 @@ namespace PKHeX.Core
return PokeCrypto.DecryptArray3(data); return PokeCrypto.DecryptArray3(data);
} }
// Pokédex /// <summary> Mirrors of the Seen Flags (inside the Large block) </summary>
private int[] SeenFlagOffsets; private int[] SeenFlagOffsets;
protected override void SetDex(PKM pkm) protected override void SetDex(PKM pkm)
@ -784,13 +813,11 @@ namespace PKHeX.Core
return false; return false;
if (Version == GameVersion.Invalid) if (Version == GameVersion.Invalid)
return false; return false;
if (BlockOfs.Any(z => z < 0))
return false;
return true; return true;
} }
public uint DexPIDUnown { get => BitConverter.ToUInt32(Data, PokeDex + 0x4); set => BitConverter.GetBytes(value).CopyTo(Data, PokeDex + 0x4); } public uint DexPIDUnown { get => BitConverter.ToUInt32(Small, PokeDex + 0x4); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x4); }
public uint DexPIDSpinda { get => BitConverter.ToUInt32(Data, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Data, PokeDex + 0x8); } public uint DexPIDSpinda { get => BitConverter.ToUInt32(Small, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x8); }
public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown); public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown);
public override bool GetCaught(int species) public override bool GetCaught(int species)
@ -798,7 +825,7 @@ namespace PKHeX.Core
int bit = species - 1; int bit = species - 1;
int ofs = bit >> 3; int ofs = bit >> 3;
int caughtOffset = PokeDex + 0x10; 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) public override void SetCaught(int species, bool caught)
@ -806,7 +833,7 @@ namespace PKHeX.Core
int bit = species - 1; int bit = species - 1;
int ofs = bit >> 3; int ofs = bit >> 3;
int caughtOffset = PokeDex + 0x10; int caughtOffset = PokeDex + 0x10;
SetFlag(caughtOffset + ofs, bit & 7, caught); FlagUtil.SetFlag(Small, caughtOffset + ofs, bit & 7, caught);
} }
public override bool GetSeen(int species) public override bool GetSeen(int species)
@ -814,7 +841,7 @@ namespace PKHeX.Core
int bit = species - 1; int bit = species - 1;
int ofs = bit >> 3; int ofs = bit >> 3;
int seenOffset = PokeDex + 0x44; 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) public override void SetSeen(int species, bool seen)
@ -822,32 +849,34 @@ namespace PKHeX.Core
int bit = species - 1; int bit = species - 1;
int ofs = bit >> 3; int ofs = bit >> 3;
int seenOffset = PokeDex + 0x44;
FlagUtil.SetFlag(Small, seenOffset + ofs, bit & 7, seen);
foreach (int o in SeenFlagOffsets) foreach (int o in SeenFlagOffsets)
SetFlag(o + ofs, bit & 7, seen); FlagUtil.SetFlag(Large, o + ofs, bit & 7, seen);
} }
public byte PokedexSort public byte PokedexSort
{ {
get => Data[PokeDex + 0x01]; get => Small[PokeDex + 0x01];
set => Data[PokeDex + 0x01] = value; set => Small[PokeDex + 0x01] = value;
} }
public byte PokedexMode public byte PokedexMode
{ {
get => Data[PokeDex + 0x01]; get => Small[PokeDex + 0x01];
set => Data[PokeDex + 0x01] = value; set => Small[PokeDex + 0x01] = value;
} }
public byte PokedexNationalMagicRSE public byte PokedexNationalMagicRSE
{ {
get => Data[PokeDex + 0x02]; get => Small[PokeDex + 0x02];
set => Data[PokeDex + 0x02] = value; set => Small[PokeDex + 0x02] = value;
} }
public byte PokedexNationalMagicFRLG public byte PokedexNationalMagicFRLG
{ {
get => Data[PokeDex + 0x03]; get => Small[PokeDex + 0x03];
set => Data[PokeDex + 0x03] = value; set => Small[PokeDex + 0x03] = value;
} }
private const int PokedexNationalUnlockRSE = 0xDA; private const int PokedexNationalUnlockRSE = 0xDA;
@ -859,8 +888,6 @@ namespace PKHeX.Core
{ {
get get
{ {
if (BlockOfs.Any(z => z < 0))
return false;
return Version switch // only check natdex status in Block0 return Version switch // only check natdex status in Block0
{ {
// enable nat dex option magic value // enable nat dex option magic value
@ -871,9 +898,6 @@ namespace PKHeX.Core
} }
set set
{ {
if (BlockOfs.Any(z => z < 0))
return;
PokedexMode = value ? 1 : 0; // mode PokedexMode = value ? 1 : 0; // mode
switch (Version) switch (Version)
{ {
@ -912,9 +936,9 @@ namespace PKHeX.Core
// Offset and checksum code based from // Offset and checksum code based from
// https://github.com/suloku/wc-tool by Suloku // https://github.com/suloku/wc-tool by Suloku
private const int SIZE_EBERRY = 0x530; 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; } private bool IsEBerryChecksumValid { get; set; }
public string EBerryName public string EBerryName
@ -923,7 +947,7 @@ namespace PKHeX.Core
{ {
if (!GameVersion.RS.Contains(Version) || !IsEBerryChecksumValid) if (!GameVersion.RS.Contains(Version) || !IsEBerryChecksumValid)
return string.Empty; 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 @@ namespace PKHeX.Core
if (!GameVersion.RS.Contains(Version)) if (!GameVersion.RS.Contains(Version))
return; 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 // 8 bytes are 0x00 for chk calculation
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
@ -950,15 +974,13 @@ namespace PKHeX.Core
{ {
if (FRLG) if (FRLG)
throw new ArgumentException(nameof(ClockInitial)); throw new ArgumentException(nameof(ClockInitial));
int block0 = GetBlockOffset(0); return new RTC3(GetData(Small, 0x98, RTC3.Size));
return new RTC3(GetData(block0 + 0x98, RTC3.Size));
} }
set set
{ {
if (FRLG) if (FRLG)
return; return;
int block0 = GetBlockOffset(0); SetData(Small, value.Data, 0x98);
SetData(value.Data, block0 + 0x98);
} }
} }
@ -968,15 +990,13 @@ namespace PKHeX.Core
{ {
if (FRLG) if (FRLG)
throw new ArgumentException(nameof(ClockElapsed)); throw new ArgumentException(nameof(ClockElapsed));
int block0 = GetBlockOffset(0); return new RTC3(GetData(Small, 0xA0, RTC3.Size));
return new RTC3(GetData(block0 + 0xA0, RTC3.Size));
} }
set set
{ {
if (FRLG) if (FRLG)
return; return;
int block0 = GetBlockOffset(0); SetData(Small, value.Data, 0xA0);
SetData(value.Data, block0 + 0xA0);
} }
} }
@ -987,9 +1007,9 @@ namespace PKHeX.Core
var ofs = PokeBlockOffset; var ofs = PokeBlockOffset;
if (ofs < 0) if (ofs < 0)
throw new ArgumentException($"Game does not support {nameof(PokeBlocks)}."); 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 private int PokeBlockOffset
@ -997,52 +1017,38 @@ namespace PKHeX.Core
get get
{ {
if (Version == GameVersion.E) if (Version == GameVersion.E)
return BlockOfs[1] + 0x848; return 0x848;
if (Version == GameVersion.RS) if (Version == GameVersion.RS)
return BlockOfs[1] + 0x7F8; return 0x7F8;
return -1; return -1;
} }
} }
public int GetMailOffset(int index) public int GetMailOffset(int index)
{ {
GetMailBlockOffset(Version, ref index, out int block, out int offset); var offset = Version switch
return (index * Mail3.SIZE) + GetBlockOffset(block) + offset; {
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; GameVersion.RS => 0x312F,
if (game == GameVersion.E) GameVersion.E => 0x31C7,
{ _ => throw new IndexOutOfRangeException(),
offset = 0xCE0; };
}
else if (GameVersion.RS.Contains(game))
{
offset = 0xC4C;
}
else // FRLG
{
if (index >= 12)
{
block = 4;
offset = 0;
index -= 12;
}
else
{
offset = 0xDD0;
}
}
}
public bool HasReceivedWishmkrJirachi public bool HasReceivedWishmkrJirachi
{ {
get => GameVersion.RS.Contains(Version) && GetFlag(BlockOfs[4] + 0x2B1, 0); get => !GameVersion.FRLG.Contains(Version) && GetFlag(ExternalEventFlags + 2, 0);
set set
{ {
if (GameVersion.RS.Contains(Version)) if (!GameVersion.FRLG.Contains(Version))
SetFlag(BlockOfs[4] + 0x2B1, 0, value); SetFlag(ExternalEventFlags + 2, 0, value);
} }
} }
@ -1060,13 +1066,13 @@ namespace PKHeX.Core
{ {
if (Version == GameVersion.FRLG) if (Version == GameVersion.FRLG)
throw new Exception(); throw new Exception();
return Data.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>(); return Large.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
} }
set set
{ {
if (Version == GameVersion.FRLG) if (Version == GameVersion.FRLG)
throw new Exception(); throw new Exception();
SetData(value.ToBytes(), OFS_Decorations); SetData(Large, value.ToBytes(), OFS_Decorations);
} }
} }
} }

View file

@ -7,30 +7,25 @@ namespace PKHeX.Core
{ {
private readonly SAV3 SAV; private readonly SAV3 SAV;
public uint GetRecord(int record) => BitConverter.ToUInt32(SAV.Data, GetRecordOffset(record)) ^ GetKey(SAV); public uint GetRecord(int record) => BitConverter.ToUInt32(SAV.Large, GetRecordOffset(record)) ^ GetKey(SAV);
public void SetRecord(int record, uint value) => SAV.SetData(BitConverter.GetBytes(value ^ GetKey(SAV)), GetRecordOffset(record)); public void SetRecord(int record, uint value) => SAV.SetData(SAV.Large, BitConverter.GetBytes(value ^ GetKey(SAV)), GetRecordOffset(record));
private int GetRecordOffset(int record) private int GetRecordOffset(int record)
{ {
var baseOffset = GetOffset(SAV.Version); var baseOffset = GetOffset(SAV.Version);
var offset = baseOffset + (4 * record); var offset = baseOffset + (4 * record);
return offset;
SAV3.GetLargeBlockOffset(offset, out var chunk, out var ofs);
return SAV.GetBlockOffset(chunk) + ofs;
} }
public Record3(SAV3 sav) => SAV = sav; 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.RS => 0x1540,
GameVersion.FRLG => 0x1200,
GameVersion.E => 0x159C, GameVersion.E => 0x159C,
GameVersion.FRLG => 0x1200,
_ => throw new ArgumentException(nameof(ver)) _ => throw new ArgumentException(nameof(ver))
}; };
}
public static uint GetKey(SAV3 sav) public static uint GetKey(SAV3 sav)
{ {

View file

@ -4,62 +4,62 @@ namespace PKHeX.Core
{ {
public sealed class Roamer3 : IContestStats, IContestStatsMutable public sealed class Roamer3 : IContestStats, IContestStatsMutable
{ {
private readonly SaveFile SAV;
private readonly int Offset; private readonly int Offset;
public bool IsGlitched { get; } public bool IsGlitched { get; }
private readonly byte[] Data;
private readonly SAV3 SAV;
public Roamer3(SAV3 sav) public Roamer3(SAV3 sav)
{ {
SAV = sav; Data = (SAV = sav).Large;
Offset = sav.GetBlockOffset(4); Offset = sav.Version switch
if (GameVersion.FRLG.Contains(SAV.Version)) {
Offset += 0x250; GameVersion.RS => 0x3144,
else if (SAV.Version == GameVersion.E) GameVersion.E => 0x31DC,
Offset += 0x35C; _ => 0x30D0, // FRLG
else // RS };
Offset += 0x2C4; IsGlitched = sav.Version != GameVersion.E;
IsGlitched = SAV.Version != GameVersion.E;
} }
private uint IV32 private uint IV32
{ {
get => BitConverter.ToUInt32(SAV.Data, Offset); get => BitConverter.ToUInt32(Data, Offset);
set => SAV.SetData(BitConverter.GetBytes(value), Offset); set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset);
} }
public uint PID public uint PID
{ {
get => BitConverter.ToUInt32(SAV.Data, Offset + 4); get => BitConverter.ToUInt32(Data, Offset + 4);
set => SAV.SetData(BitConverter.GetBytes(value), Offset + 4); set => SAV.SetData(Data, BitConverter.GetBytes(value), Offset + 4);
} }
public int Species public int Species
{ {
get => SpeciesConverter.GetG4Species(BitConverter.ToInt16(SAV.Data, Offset + 8)); get => SpeciesConverter.GetG4Species(BitConverter.ToInt16(Data, Offset + 8));
set => SAV.SetData(BitConverter.GetBytes((ushort)SpeciesConverter.GetG3Species(value)), Offset + 8); set => SAV.SetData(Data, BitConverter.GetBytes((ushort)SpeciesConverter.GetG3Species(value)), Offset + 8);
} }
public int HP_Current public int HP_Current
{ {
get => BitConverter.ToInt16(SAV.Data, Offset + 10); get => BitConverter.ToInt16(Data, Offset + 10);
set => SAV.SetData(BitConverter.GetBytes((short)value), Offset + 10); set => SAV.SetData(Data, BitConverter.GetBytes((short)value), Offset + 10);
} }
public int CurrentLevel public int CurrentLevel
{ {
get => SAV.Data[Offset + 12]; get => Data[Offset + 12];
set => SAV.Data[Offset + 12] = (byte)value; 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_Cool { get => Data[Offset + 0x0E]; set => Data[Offset + 0x0E] = value; }
public byte CNT_Beauty { get => SAV.Data[Offset + 0x0F]; set => SAV.Data[Offset + 0x0F] = value; } public byte CNT_Beauty { get => Data[Offset + 0x0F]; set => Data[Offset + 0x0F] = value; }
public byte CNT_Cute { get => SAV.Data[Offset + 0x10]; set => SAV.Data[Offset + 0x10] = value; } public byte CNT_Cute { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; }
public byte CNT_Smart { get => SAV.Data[Offset + 0x11]; set => SAV.Data[Offset + 0x11] = value; } public byte CNT_Smart { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = value; }
public byte CNT_Tough { get => SAV.Data[Offset + 0x12]; set => SAV.Data[Offset + 0x12] = value; } public byte CNT_Tough { get => Data[Offset + 0x12]; set => Data[Offset + 0x12] = value; }
public byte CNT_Sheen { get => 0; set { } } 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 // Derived Properties
private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); }

View file

@ -28,6 +28,7 @@ namespace PKHeX.Core
public override ushort GetMessage(int index1, int index2) => BitConverter.ToUInt16(Data, ((index1 * 3) + index2) * 2); 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 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 public override string AuthorName
{ {

View file

@ -43,7 +43,7 @@
this.CB_TCM1 = new System.Windows.Forms.ComboBox(); this.CB_TCM1 = new System.Windows.Forms.ComboBox();
this.NUD_Coins = new System.Windows.Forms.NumericUpDown(); this.NUD_Coins = new System.Windows.Forms.NumericUpDown();
this.L_Coins = new System.Windows.Forms.Label(); 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.L_TrainerName = new System.Windows.Forms.Label();
this.NUD_BP = new System.Windows.Forms.NumericUpDown(); this.NUD_BP = new System.Windows.Forms.NumericUpDown();
this.L_BP = new System.Windows.Forms.Label(); this.L_BP = new System.Windows.Forms.Label();
@ -177,7 +177,7 @@
this.TAB_Main.Controls.Add(this.GB_TCM); this.TAB_Main.Controls.Add(this.GB_TCM);
this.TAB_Main.Controls.Add(this.NUD_Coins); this.TAB_Main.Controls.Add(this.NUD_Coins);
this.TAB_Main.Controls.Add(this.L_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.L_TrainerName);
this.TAB_Main.Controls.Add(this.NUD_BP); this.TAB_Main.Controls.Add(this.NUD_BP);
this.TAB_Main.Controls.Add(this.L_BP); this.TAB_Main.Controls.Add(this.L_BP);
@ -310,16 +310,16 @@
this.L_Coins.Text = "Coins:"; this.L_Coins.Text = "Coins:";
this.L_Coins.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 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_RivalName.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_RivalName.Location = new System.Drawing.Point(120, 93);
this.TB_OTName.MaxLength = 7; this.TB_RivalName.MaxLength = 7;
this.TB_OTName.Name = "TB_OTName"; this.TB_RivalName.Name = "TB_RivalName";
this.TB_OTName.Size = new System.Drawing.Size(63, 20); this.TB_RivalName.Size = new System.Drawing.Size(63, 20);
this.TB_OTName.TabIndex = 31; this.TB_RivalName.TabIndex = 31;
this.TB_OTName.Text = "WWWWWWW"; this.TB_RivalName.Text = "WWWWWWW";
this.TB_OTName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.TB_RivalName.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
// //
// L_TrainerName // L_TrainerName
// //
@ -1112,7 +1112,7 @@
private System.Windows.Forms.Label L_B5Score; private System.Windows.Forms.Label L_B5Score;
private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label4; 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.Label L_TrainerName;
private System.Windows.Forms.NumericUpDown NUD_BP; private System.Windows.Forms.NumericUpDown NUD_BP;
private System.Windows.Forms.Label L_BP; private System.Windows.Forms.Label L_BP;

View file

@ -37,31 +37,22 @@ namespace PKHeX.WinForms
if (SAV.FRLG) 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 }; ComboBox[] cba = { CB_TCM1, CB_TCM2, CB_TCM3, CB_TCM4, CB_TCM5, CB_TCM6 };
int[] HoennListMixed = { var legal = GameInfo.SpeciesDataSource.Where(v => v.Value <= SAV.MaxSpeciesID);
277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300, var speciesList = legal.Select(v => new ComboItem(v.Text, SpeciesConverter.GetG3Species(v.Value))).ToList();
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;
for (int i = 0; i < cba.Length; i++) for (int i = 0; i < cba.Length; i++)
{ {
cba[i].Items.Clear(); cba[i].Items.Clear();
cba[i].InitializeBinding(); cba[i].InitializeBinding();
cba[i].DataSource = new BindingSource(speciesList, null); 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 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); NUD_Coins.Value = Math.Min(NUD_Coins.Maximum, SAV.Coin);
} }
@ -76,11 +67,10 @@ namespace PKHeX.WinForms
SaveBattleFrontier(); SaveBattleFrontier();
if (SAV.FRLG) 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 }; 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++) 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) if (!SAV.RS)
@ -102,40 +92,40 @@ namespace PKHeX.WinForms
switch (SAV.Version) switch (SAV.Version)
{ {
case GameVersion.E: case GameVersion.E:
JUMPS_IN_ROW = SAV.GetBlockOffset(0) + 0x1fc; JUMPS_IN_ROW = 0x1fc;
JUMPS_SCORE = SAV.GetBlockOffset(0) + 0x208; JUMPS_SCORE = 0x208;
JUMPS_5_IN_ROW = SAV.GetBlockOffset(0) + 0x200; JUMPS_5_IN_ROW = 0x200;
BERRIES_IN_ROW = SAV.GetBlockOffset(0) + 0x210; BERRIES_IN_ROW = 0x210;
BERRIES_SCORE = SAV.GetBlockOffset(0) + 0x20c; BERRIES_SCORE = 0x20c;
BERRIES_5_IN_ROW = SAV.GetBlockOffset(0) + 0x212; BERRIES_5_IN_ROW = 0x212;
break; break;
case GameVersion.FRLG: case GameVersion.FRLG:
JUMPS_IN_ROW = SAV.GetBlockOffset(0) + 0xB00; JUMPS_IN_ROW = 0xB00;
JUMPS_SCORE = SAV.GetBlockOffset(0) + 0xB0C; JUMPS_SCORE = 0xB0C;
JUMPS_5_IN_ROW = SAV.GetBlockOffset(0) + 0xB04; JUMPS_5_IN_ROW = 0xB04;
BERRIES_IN_ROW = SAV.GetBlockOffset(0) + 0xB14; BERRIES_IN_ROW = 0xB14;
BERRIES_SCORE = SAV.GetBlockOffset(0) + 0xB10; BERRIES_SCORE = 0xB10;
BERRIES_5_IN_ROW = SAV.GetBlockOffset(0) + 0xB16; BERRIES_5_IN_ROW = 0xB16;
break; break;
} }
TB_J1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Data, JUMPS_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.Data, JUMPS_SCORE)).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.Data, JUMPS_5_IN_ROW)).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.Data, BERRIES_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.Data, BERRIES_SCORE)).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.Data, BERRIES_5_IN_ROW)).ToString(); TB_B3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_5_IN_ROW)).ToString();
} }
private void SaveJoyful() private void SaveJoyful()
{ {
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J1.Text)).CopyTo(SAV.Data, JUMPS_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.Data, JUMPS_SCORE); BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J2.Text)).CopyTo(SAV.Small, JUMPS_SCORE);
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J3.Text)).CopyTo(SAV.Data, JUMPS_5_IN_ROW); 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.Data, BERRIES_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.Data, BERRIES_SCORE); BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B2.Text)).CopyTo(SAV.Small, BERRIES_SCORE);
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B3.Text)).CopyTo(SAV.Data, BERRIES_5_IN_ROW); BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B3.Text)).CopyTo(SAV.Small, BERRIES_5_IN_ROW);
} }
#endregion #endregion
@ -234,9 +224,6 @@ namespace PKHeX.WinForms
#endregion #endregion
#region BattleFrontier #region BattleFrontier
private int[] Symbols = null!;
private int ofsSymbols;
private Color[] SymbolColorA = null!;
private Button[] SymbolButtonA = null!; private Button[] SymbolButtonA = null!;
private bool editingcont; private bool editingcont;
private bool editingval; private bool editingval;
@ -322,15 +309,16 @@ namespace PKHeX.WinForms
SetValToSav = Array.IndexOf(BFV[BFF[Facility][0]], SetValToSav); SetValToSav = Array.IndexOf(BFV[BFF[Facility][0]], SetValToSav);
if (SetValToSav < 0) if (SetValToSav < 0)
return; return;
if (val > 9999) val = 9999; if (val > 9999)
BitConverter.GetBytes(val).CopyTo(SAV.Data, SAV.GetBlockOffset(0) + BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi)); val = 9999;
BitConverter.GetBytes(val).CopyTo(SAV.Small, BFF[Facility][2 + SetValToSav] + (4 * BattleType) + (2 * RBi));
return; return;
} }
if (SetValToSav == -1) if (SetValToSav == -1)
{ {
int p = BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi; int p = BFF[Facility][2 + BFV[BFF[Facility][0]].Length + BattleType] + RBi;
int offset = SAV.GetBlockOffset(0) + 0xCDC; const int offset = 0xCDC;
BitConverter.GetBytes((BitConverter.ToUInt32(SAV.Data, offset) & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p)).CopyTo(SAV.Data, offset); BitConverter.GetBytes((BitConverter.ToUInt32(SAV.Small, offset) & (uint)~(1 << p)) | (uint)((CHK_Continue.Checked ? 1 : 0) << p)).CopyTo(SAV.Small, offset);
return; return;
} }
if (!SetSavToVal) if (!SetSavToVal)
@ -339,11 +327,12 @@ namespace PKHeX.WinForms
editingval = true; editingval = true;
for (int i = 0; i < BFV[BFF[Facility][0]].Length; i++) 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)); int vali = BitConverter.ToUInt16(SAV.Small, BFF[Facility][2 + i] + (4 * BattleType) + (2 * RBi));
if (vali > 9999) vali = 9999; if (vali > 9999)
vali = 9999;
StatNUDA[BFV[BFF[Facility][0]][i]].Value = vali; 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; editingval = false;
} }
@ -395,14 +384,8 @@ namespace PKHeX.WinForms
StatNUDA = new[] { NUD_Stat0, NUD_Stat1, NUD_Stat2, NUD_Stat3 }; StatNUDA = new[] { NUD_Stat0, NUD_Stat1, NUD_Stat2, NUD_Stat3 };
StatLabelA = new[] { L_Stat0, L_Stat1, L_Stat2, L_Stat3 }; StatLabelA = new[] { L_Stat0, L_Stat1, L_Stat2, L_Stat3 };
StatRBA = new[] { RB_Stats3_01, RB_Stats3_02 }; 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 }; SymbolButtonA = new[] { BTN_SymbolA, BTN_SymbolT, BTN_SymbolS, BTN_SymbolG, BTN_SymbolK, BTN_SymbolL, BTN_SymbolB };
ofsSymbols = SAV.GetBlockOffset(2) + 0x408; CHK_ActivatePass.Checked = SAV.GetEventFlag(0x860 + 0x72);
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;
SetFrontierSymbols(); SetFrontierSymbols();
CB_Stats1.Items.Clear(); CB_Stats1.Items.Clear();
@ -416,33 +399,39 @@ namespace PKHeX.WinForms
private void SetFrontierSymbols() private void SetFrontierSymbols()
{ {
for (int i = 0; i < SymbolButtonA.Length; i++) 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() private void SaveBattleFrontier()
{ {
uint iSymbols = 0;
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
iSymbols |= (uint)((Symbols[i] & 3) << i * 2); {
if (CHK_ActivatePass.Checked) var color = SymbolButtonA[i].BackColor;
iSymbols |= 1 << 14; bool silver = color != Color.Transparent;
bool gold = color == Color.Gold;
uint val = (uint)((BitConverter.ToUInt32(SAV.Data, ofsSymbols) & ~(0x7FFF << 4)) | (iSymbols & 0x7FFF) << 4); var flagIndex = 0x860 + 0x64 + (i * 2);
BitConverter.GetBytes(val).CopyTo(SAV.Data, ofsSymbols); SAV.SetEventFlag(flagIndex, silver);
SAV.SetEventFlag(flagIndex, gold);
}
SAV.SetEventFlag(0x860 + 0x72, CHK_ActivatePass.Checked);
} }
private void BTN_Symbol_Click(object sender, EventArgs e) private void BTN_Symbol_Click(object sender, EventArgs e)
{ {
int index = Array.IndexOf(SymbolButtonA, sender); var match = Array.Find(SymbolButtonA, z => z == sender);
if (index < 0) if (match == null)
return; return;
// 0 (none) | 1 (silver) | 2 (silver) | 3 (gold) var color = match.BackColor;
// bit rotation 00 -> 01 -> 11 -> 00 color = color == Color.Transparent ? Color.Silver : color == Color.Silver ? Color.Gold : Color.Transparent;
if (Symbols[index] == 1) Symbols[index] = 3; match.BackColor = color;
else Symbols[index] = (Symbols[index] + 1) & 3;
SetFrontierSymbols();
} }
#endregion #endregion

View file

@ -70,7 +70,7 @@ namespace PKHeX.WinForms
for (int i = 0; i < m.Length; i++) for (int i = 0; i < m.Length; i++)
{ {
var ofs = sav3.GetMailOffset(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); m[i] = new Mail3(data, ofs, sav3.Japanese);
} }
@ -174,16 +174,6 @@ namespace PKHeX.WinForms
LB_PCBOX.SelectedIndex = 0; 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() private void LoadList()
{ {
if (entry < PartyBoxCount) MakePartyList(); if (entry < PartyBoxCount) MakePartyList();
@ -308,7 +298,7 @@ namespace PKHeX.WinForms
} }
if (Gen == 3) if (Gen == 3)
{ {
mail.AppearPKM = v < 252 ? v : HoennListMixed[v - 252]; mail.AppearPKM = SpeciesConverter.GetG3Species(v);
return; return;
} }
@ -544,15 +534,7 @@ namespace PKHeX.WinForms
} }
if (Gen == 3) if (Gen == 3)
{ {
if (v < 252) AppearPKMs[0].SelectedValue = SpeciesConverter.GetG4Species(v);
{
AppearPKMs[0].SelectedValue = v;
}
else
{
v = Array.IndexOf(HoennListMixed, v);
AppearPKMs[0].SelectedValue = v >= 0 ? (252 + v) : 0;
}
editing = false; editing = false;
return; return;
} }