mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
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:
parent
cb7ee8a64e
commit
49541e6073
9 changed files with 370 additions and 397 deletions
|
@ -70,7 +70,7 @@ namespace PKHeX.Core
|
|||
return None;
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.Data, 0, sav.GetBlockOffset(4) + 0xE18) {Type = StorageSlotType.Daycare }
|
||||
new(sav.Large, 0, 0x3C98) {Type = StorageSlotType.Daycare }
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace PKHeX.Core
|
|||
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)
|
||||
|
|
|
@ -18,24 +18,44 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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<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
|
||||
{
|
||||
|
@ -87,9 +82,7 @@ namespace PKHeX.Core
|
|||
_personal = SaveUtil.GetG3Personal(Version);
|
||||
Japanese = japanese;
|
||||
|
||||
LoadBlocks(out BlockOrder, out BlockOfs);
|
||||
// spoof block offsets
|
||||
BlockOfs = Enumerable.Range(0, BLOCK_COUNT).ToArray();
|
||||
BlockOrder = Array.Empty<short>();
|
||||
|
||||
LegalKeyItems = Version switch
|
||||
{
|
||||
|
@ -97,7 +90,7 @@ namespace PKHeX.Core
|
|||
GameVersion.E => Legal.Pouch_Key_E,
|
||||
_ => Legal.Pouch_Key_FRLG
|
||||
};
|
||||
PokeDex = BlockOfs[0] + 0x18;
|
||||
PokeDex = 0x18;
|
||||
SeenFlagOffsets = Array.Empty<int>();
|
||||
|
||||
Initialize();
|
||||
|
@ -106,14 +99,20 @@ namespace PKHeX.Core
|
|||
|
||||
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 @@ namespace PKHeX.Core
|
|||
_ => 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 @@ namespace PKHeX.Core
|
|||
|
||||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
// 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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
|
||||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
{
|
||||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
return PokeCrypto.DecryptArray3(data);
|
||||
}
|
||||
|
||||
// Pokédex
|
||||
/// <summary> Mirrors of the Seen Flags (inside the Large block) </summary>
|
||||
private int[] SeenFlagOffsets;
|
||||
|
||||
protected override void SetDex(PKM pkm)
|
||||
|
@ -784,13 +813,11 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
{
|
||||
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 @@ namespace PKHeX.Core
|
|||
}
|
||||
set
|
||||
{
|
||||
if (BlockOfs.Any(z => z < 0))
|
||||
return;
|
||||
|
||||
PokedexMode = value ? 1 : 0; // mode
|
||||
switch (Version)
|
||||
{
|
||||
|
@ -912,9 +936,9 @@ namespace PKHeX.Core
|
|||
// 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 @@ namespace PKHeX.Core
|
|||
{
|
||||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
{
|
||||
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 @@ namespace PKHeX.Core
|
|||
{
|
||||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
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 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (Version == GameVersion.FRLG)
|
||||
throw new Exception();
|
||||
return Data.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
|
||||
return Large.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Version == GameVersion.FRLG)
|
||||
throw new Exception();
|
||||
SetData(value.ToBytes(), OFS_Decorations);
|
||||
SetData(Large, value.ToBytes(), OFS_Decorations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,30 +7,25 @@ namespace PKHeX.Core
|
|||
{
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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)); }
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace PKHeX.Core
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
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 @@
|
|||
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 @@
|
|||
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 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;
|
||||
|
|
|
@ -37,31 +37,22 @@ namespace PKHeX.WinForms
|
|||
|
||||
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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
#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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
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
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
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 @@ namespace PKHeX.WinForms
|
|||
}
|
||||
if (Gen == 3)
|
||||
{
|
||||
mail.AppearPKM = v < 252 ? v : HoennListMixed[v - 252];
|
||||
mail.AppearPKM = SpeciesConverter.GetG3Species(v);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -544,15 +534,7 @@ namespace PKHeX.WinForms
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue