mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-17 05:48:44 +00:00
Refactoring
pull out BlockInfo logic, standardize common block checking reduces array allocations
This commit is contained in:
parent
26ca111630
commit
d20f14504e
10 changed files with 444 additions and 543 deletions
89
PKHeX.Core/Saves/Blocks/BlockInfo.cs
Normal file
89
PKHeX.Core/Saves/Blocks/BlockInfo.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public abstract class BlockInfo
|
||||
{
|
||||
// General
|
||||
public uint ID;
|
||||
public int Offset;
|
||||
public int Length;
|
||||
|
||||
public string Summary => $"{ID:00}: {Offset:X5}-{Offset + Length:X5}, {Length:X5}";
|
||||
|
||||
protected abstract bool ChecksumValid(byte[] data);
|
||||
protected abstract void SetChecksum(byte[] data);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the currently written checksum values are valid.
|
||||
/// </summary>
|
||||
/// <param name="blocks">Block info objects used for offset/length</param>
|
||||
/// <param name="data">Complete data array</param>
|
||||
/// <returns>True if checksums are valid, false if anything is invalid.</returns>
|
||||
public static bool GetChecksumsValid(IEnumerable<BlockInfo> blocks, byte[] data)
|
||||
{
|
||||
foreach (var b in blocks)
|
||||
{
|
||||
if (b.Length + b.Offset > data.Length)
|
||||
return false;
|
||||
|
||||
if (!b.ChecksumValid(data))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies checksums to the <see cref="data"/> object based on the input blocks.
|
||||
/// </summary>
|
||||
/// <param name="blocks">Block info objects used for offset/length</param>
|
||||
/// <param name="data">Complete data array</param>
|
||||
public static void SetChecksums(IEnumerable<BlockInfo> blocks, byte[] data)
|
||||
{
|
||||
foreach (var b in blocks)
|
||||
b.SetChecksum(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information pertaining to the checksum data for diagnostic purposes.
|
||||
/// </summary>
|
||||
/// <param name="blocks">Block info objects used for offset/length</param>
|
||||
/// <param name="data">Complete data array</param>
|
||||
/// <returns>Multi-line string with <see cref="Summary"/> data.</returns>
|
||||
public static string GetChecksumInfo(IReadOnlyList<BlockInfo> blocks, byte[] data)
|
||||
{
|
||||
var invalid = GetInvalidBlockCount(blocks, data, out var list);
|
||||
list.Add($"SAV: {blocks.Count - invalid}/{blocks.Count}");
|
||||
return string.Join(Environment.NewLine, list);
|
||||
}
|
||||
|
||||
private static int GetInvalidBlockCount(IReadOnlyList<BlockInfo> blocks, byte[] data, out List<string> list)
|
||||
{
|
||||
int invalid = 0;
|
||||
list = new List<string>();
|
||||
for (int i = 0; i < blocks.Count; i++)
|
||||
{
|
||||
if (blocks[i].Length + blocks[i].Offset > data.Length)
|
||||
{
|
||||
list.Add($"Block {i} Invalid Offset/Length.");
|
||||
return invalid;
|
||||
}
|
||||
|
||||
if (blocks[i].ChecksumValid(data))
|
||||
continue;
|
||||
|
||||
invalid++;
|
||||
list.Add($"Invalid: {i:X2} @ Region {blocks[i].Offset:X5}");
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class Extensions
|
||||
{
|
||||
public static bool GetChecksumsValid(this IEnumerable<BlockInfo> blocks, byte[] data) => BlockInfo.GetChecksumsValid(blocks, data);
|
||||
public static void SetChecksums(this IEnumerable<BlockInfo> blocks, byte[] data) => BlockInfo.SetChecksums(blocks, data);
|
||||
public static string GetChecksumInfo(this IReadOnlyList<BlockInfo> blocks, byte[] data) => BlockInfo.GetChecksumInfo(blocks, data);
|
||||
}
|
||||
}
|
69
PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs
Normal file
69
PKHeX.Core/Saves/Blocks/BlockInfo3DS.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class BlockInfo3DS : BlockInfo
|
||||
{
|
||||
public ushort Checksum;
|
||||
private int BlockInfoOffset;
|
||||
private readonly Func<byte[], int, int, ushort> CheckFunc;
|
||||
|
||||
private BlockInfo3DS(Func<byte[], int, int, ushort> func) => CheckFunc = func;
|
||||
private int ChecksumOffset => BlockInfoOffset + 6 + (int)ID * 8;
|
||||
private ushort GetChecksum(byte[] data) => CheckFunc(data, Offset, Length);
|
||||
|
||||
protected override bool ChecksumValid(byte[] data)
|
||||
{
|
||||
ushort chk = GetChecksum(data);
|
||||
var old = BitConverter.ToUInt16(data, ChecksumOffset);
|
||||
return chk == old;
|
||||
}
|
||||
protected override void SetChecksum(byte[] data)
|
||||
{
|
||||
ushort chk = GetChecksum(data);
|
||||
BitConverter.GetBytes(chk).CopyTo(data, ChecksumOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="BlockInfo"/> table for the input <see cref="data"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">Complete data array</param>
|
||||
/// <param name="blockInfoOffset">Offset the <see cref="BlockInfo"/> starts at.</param>
|
||||
/// <param name="CheckFunc">Checksum method for validating each block.</param>
|
||||
public static BlockInfo[] GetBlockInfoData(byte[] data, out int blockInfoOffset, Func<byte[], int, int, ushort> CheckFunc)
|
||||
{
|
||||
blockInfoOffset = data.Length - 0x200 + 0x10;
|
||||
if (BitConverter.ToUInt32(data, blockInfoOffset) != SaveUtil.BEEF)
|
||||
blockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
||||
int count = (data.Length - blockInfoOffset - 0x8) / 8;
|
||||
blockInfoOffset += 4;
|
||||
|
||||
var Blocks = new BlockInfo[count];
|
||||
int CurrentPosition = 0;
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
Blocks[i] = new BlockInfo3DS(CheckFunc)
|
||||
{
|
||||
Offset = CurrentPosition,
|
||||
Length = BitConverter.ToInt32(data, blockInfoOffset + 0 + 8 * i),
|
||||
ID = BitConverter.ToUInt16(data, blockInfoOffset + 4 + 8 * i),
|
||||
Checksum = BitConverter.ToUInt16(data, blockInfoOffset + 6 + 8 * i),
|
||||
|
||||
BlockInfoOffset = blockInfoOffset
|
||||
};
|
||||
|
||||
// Expand out to nearest 0x200
|
||||
var remainder = Blocks[i].Length & 0x1FF;
|
||||
CurrentPosition += remainder == 0 ? Blocks[i].Length : Blocks[i].Length + 0x200 - remainder;
|
||||
|
||||
if (Blocks[i].ID != 0 || i == 0)
|
||||
continue;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
// Fix Final Array Lengths
|
||||
Array.Resize(ref Blocks, count);
|
||||
return Blocks;
|
||||
}
|
||||
}
|
||||
}
|
188
PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs
Normal file
188
PKHeX.Core/Saves/Blocks/BlockInfoNDS.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class BlockInfoNDS : BlockInfo
|
||||
{
|
||||
// Gen4/5
|
||||
private readonly int ChecksumOffset;
|
||||
private readonly int ChecksumMirror;
|
||||
private BlockInfoNDS(int offset, int length, int chkOffset, int chkMirror)
|
||||
{
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
ChecksumOffset = chkOffset;
|
||||
ChecksumMirror = chkMirror;
|
||||
}
|
||||
|
||||
private ushort GetChecksum(byte[] data) => SaveUtil.CRC16_CCITT(data, Offset, Length);
|
||||
protected override bool ChecksumValid(byte[] data)
|
||||
{
|
||||
ushort chk = GetChecksum(data);
|
||||
if (chk != BitConverter.ToUInt16(data, ChecksumOffset))
|
||||
return false;
|
||||
if (chk != BitConverter.ToUInt16(data, ChecksumMirror))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
protected override void SetChecksum(byte[] data)
|
||||
{
|
||||
ushort chk = GetChecksum(data);
|
||||
var bytes = BitConverter.GetBytes(chk);
|
||||
bytes.CopyTo(data, ChecksumOffset);
|
||||
bytes.CopyTo(data, ChecksumMirror);
|
||||
}
|
||||
|
||||
// Offset, Length, chkOffset, ChkMirror
|
||||
public static readonly BlockInfoNDS[] BlocksBW =
|
||||
{
|
||||
new BlockInfoNDS(0x00000, 0x03E0, 0x003E2, 0x23F00), // Box Names
|
||||
new BlockInfoNDS(0x00400, 0x0FF0, 0x013F2, 0x23F02), // Box 1
|
||||
new BlockInfoNDS(0x01400, 0x0FF0, 0x023F2, 0x23F04), // Box 2
|
||||
new BlockInfoNDS(0x02400, 0x0FF0, 0x033F2, 0x23F06), // Box 3
|
||||
new BlockInfoNDS(0x03400, 0x0FF0, 0x043F2, 0x23F08), // Box 4
|
||||
new BlockInfoNDS(0x04400, 0x0FF0, 0x053F2, 0x23F0A), // Box 5
|
||||
new BlockInfoNDS(0x05400, 0x0FF0, 0x063F2, 0x23F0C), // Box 6
|
||||
new BlockInfoNDS(0x06400, 0x0FF0, 0x073F2, 0x23F0E), // Box 7
|
||||
new BlockInfoNDS(0x07400, 0x0FF0, 0x083F2, 0x23F10), // Box 8
|
||||
new BlockInfoNDS(0x08400, 0x0FF0, 0x093F2, 0x23F12), // Box 9
|
||||
new BlockInfoNDS(0x09400, 0x0FF0, 0x0A3F2, 0x23F14), // Box 10
|
||||
new BlockInfoNDS(0x0A400, 0x0FF0, 0x0B3F2, 0x23F16), // Box 11
|
||||
new BlockInfoNDS(0x0B400, 0x0FF0, 0x0C3F2, 0x23F18), // Box 12
|
||||
new BlockInfoNDS(0x0C400, 0x0FF0, 0x0D3F2, 0x23F1A), // Box 13
|
||||
new BlockInfoNDS(0x0D400, 0x0FF0, 0x0E3F2, 0x23F1C), // Box 14
|
||||
new BlockInfoNDS(0x0E400, 0x0FF0, 0x0F3F2, 0x23F1E), // Box 15
|
||||
new BlockInfoNDS(0x0F400, 0x0FF0, 0x103F2, 0x23F20), // Box 16
|
||||
new BlockInfoNDS(0x10400, 0x0FF0, 0x113F2, 0x23F22), // Box 17
|
||||
new BlockInfoNDS(0x11400, 0x0FF0, 0x123F2, 0x23F24), // Box 18
|
||||
new BlockInfoNDS(0x12400, 0x0FF0, 0x133F2, 0x23F26), // Box 19
|
||||
new BlockInfoNDS(0x13400, 0x0FF0, 0x143F2, 0x23F28), // Box 20
|
||||
new BlockInfoNDS(0x14400, 0x0FF0, 0x153F2, 0x23F2A), // Box 21
|
||||
new BlockInfoNDS(0x15400, 0x0FF0, 0x163F2, 0x23F2C), // Box 22
|
||||
new BlockInfoNDS(0x16400, 0x0FF0, 0x173F2, 0x23F2E), // Box 23
|
||||
new BlockInfoNDS(0x17400, 0x0FF0, 0x183F2, 0x23F30), // Box 24
|
||||
new BlockInfoNDS(0x18400, 0x09C0, 0x18DC2, 0x23F32), // Inventory
|
||||
new BlockInfoNDS(0x18E00, 0x0534, 0x19336, 0x23F34), // Party Pokemon
|
||||
new BlockInfoNDS(0x19400, 0x0068, 0x1946A, 0x23F36), // Trainer Data
|
||||
new BlockInfoNDS(0x19500, 0x009C, 0x1959E, 0x23F38), // Trainer Position
|
||||
new BlockInfoNDS(0x19600, 0x1338, 0x1A93A, 0x23F3A), // Unity Tower and survey stuff
|
||||
new BlockInfoNDS(0x1AA00, 0x07C4, 0x1B1C6, 0x23F3C), // Pal Pad Player Data
|
||||
new BlockInfoNDS(0x1B200, 0x0D54, 0x1BF56, 0x23F3E), // Pal Pad Friend Data
|
||||
new BlockInfoNDS(0x1C000, 0x002C, 0x1C02E, 0x23F40), // Skin Info
|
||||
new BlockInfoNDS(0x1C100, 0x0658, 0x1C75A, 0x23F42), // ??? Gym badge data
|
||||
new BlockInfoNDS(0x1C800, 0x0A94, 0x1D296, 0x23F44), // Mystery Gift
|
||||
new BlockInfoNDS(0x1D300, 0x01AC, 0x1D4AE, 0x23F46), // Dream World Stuff (Catalog)
|
||||
new BlockInfoNDS(0x1D500, 0x03EC, 0x1D8EE, 0x23F48), // Chatter
|
||||
new BlockInfoNDS(0x1D900, 0x005C, 0x1D95E, 0x23F4A), // Adventure Info
|
||||
new BlockInfoNDS(0x1DA00, 0x01E0, 0x1DBE2, 0x23F4C), // Trainer Card Records
|
||||
new BlockInfoNDS(0x1DC00, 0x00A8, 0x1DCAA, 0x23F4E), // ???
|
||||
new BlockInfoNDS(0x1DD00, 0x0460, 0x1E162, 0x23F50), // (40d)
|
||||
new BlockInfoNDS(0x1E200, 0x1400, 0x1F602, 0x23F52), // ???
|
||||
new BlockInfoNDS(0x1F700, 0x02A4, 0x1F9A6, 0x23F54), // Contains flags and references for downloaded data (Musical)
|
||||
new BlockInfoNDS(0x1FA00, 0x02DC, 0x1FCDE, 0x23F56), // ???
|
||||
new BlockInfoNDS(0x1FD00, 0x034C, 0x2004E, 0x23F58), // ???
|
||||
new BlockInfoNDS(0x20100, 0x03EC, 0x204EE, 0x23F5A), // ???
|
||||
new BlockInfoNDS(0x20500, 0x00F8, 0x205FA, 0x23F5C), // ???
|
||||
new BlockInfoNDS(0x20600, 0x02FC, 0x208FE, 0x23F5E), // Tournament Block
|
||||
new BlockInfoNDS(0x20900, 0x0094, 0x20996, 0x23F60), // ???
|
||||
new BlockInfoNDS(0x20A00, 0x035C, 0x20D5E, 0x23F62), // Battle Box Block
|
||||
new BlockInfoNDS(0x20E00, 0x01CC, 0x20FCE, 0x23F64), // Daycare Block
|
||||
new BlockInfoNDS(0x21000, 0x0168, 0x2116A, 0x23F66), // Strength Boulder Status Block
|
||||
new BlockInfoNDS(0x21200, 0x00EC, 0x212EE, 0x23F68), // Badge Flags, Money, Trainer Sayings
|
||||
new BlockInfoNDS(0x21300, 0x01B0, 0x214B2, 0x23F6A), // Entralink (Level & Powers etc)
|
||||
new BlockInfoNDS(0x21500, 0x001C, 0x2151E, 0x23F6C), // ???
|
||||
new BlockInfoNDS(0x21600, 0x04D4, 0x21AD6, 0x23F6E), // Pokedex
|
||||
new BlockInfoNDS(0x21B00, 0x0034, 0x21B36, 0x23F70), // Swarm and other overworld info - 2C - swarm, 2D - repel steps, 2E repel type
|
||||
new BlockInfoNDS(0x21C00, 0x003C, 0x21C3E, 0x23F72), // ???
|
||||
new BlockInfoNDS(0x21D00, 0x01AC, 0x21EAE, 0x23F74), // Battle Subway
|
||||
new BlockInfoNDS(0x21F00, 0x0B90, 0x22A92, 0x23F76), // ???
|
||||
new BlockInfoNDS(0x22B00, 0x009C, 0x22B9E, 0x23F78), // Online Records
|
||||
new BlockInfoNDS(0x22C00, 0x0850, 0x23452, 0x23F7A), // Entralink Forest pokémon data
|
||||
new BlockInfoNDS(0x23500, 0x0028, 0x2352A, 0x23F7C), // ???
|
||||
new BlockInfoNDS(0x23600, 0x0284, 0x23886, 0x23F7E), // ???
|
||||
new BlockInfoNDS(0x23900, 0x0010, 0x23912, 0x23F80), // ???
|
||||
new BlockInfoNDS(0x23A00, 0x005C, 0x23A5E, 0x23F82), // ???
|
||||
new BlockInfoNDS(0x23B00, 0x016C, 0x23C6E, 0x23F84), // ???
|
||||
new BlockInfoNDS(0x23D00, 0x0040, 0x23D42, 0x23F86), // ???
|
||||
new BlockInfoNDS(0x23E00, 0x00FC, 0x23EFE, 0x23F88), // ???
|
||||
new BlockInfoNDS(0x23F00, 0x008C, 0x23F9A, 0x23F9A), // Checksums */
|
||||
};
|
||||
public static readonly BlockInfoNDS[] BlocksB2W2 =
|
||||
{
|
||||
new BlockInfoNDS(0x00000, 0x03e0, 0x003E2, 0x25F00), // Box Names
|
||||
new BlockInfoNDS(0x00400, 0x0ff0, 0x013F2, 0x25F02), // Box 1
|
||||
new BlockInfoNDS(0x01400, 0x0ff0, 0x023F2, 0x25F04), // Box 2
|
||||
new BlockInfoNDS(0x02400, 0x0ff0, 0x033F2, 0x25F06), // Box 3
|
||||
new BlockInfoNDS(0x03400, 0x0ff0, 0x043F2, 0x25F08), // Box 4
|
||||
new BlockInfoNDS(0x04400, 0x0ff0, 0x053F2, 0x25F0A), // Box 5
|
||||
new BlockInfoNDS(0x05400, 0x0ff0, 0x063F2, 0x25F0C), // Box 6
|
||||
new BlockInfoNDS(0x06400, 0x0ff0, 0x073F2, 0x25F0E), // Box 7
|
||||
new BlockInfoNDS(0x07400, 0x0ff0, 0x083F2, 0x25F10), // Box 8
|
||||
new BlockInfoNDS(0x08400, 0x0ff0, 0x093F2, 0x25F12), // Box 9
|
||||
new BlockInfoNDS(0x09400, 0x0ff0, 0x0A3F2, 0x25F14), // Box 10
|
||||
new BlockInfoNDS(0x0A400, 0x0ff0, 0x0B3F2, 0x25F16), // Box 11
|
||||
new BlockInfoNDS(0x0B400, 0x0ff0, 0x0C3F2, 0x25F18), // Box 12
|
||||
new BlockInfoNDS(0x0C400, 0x0ff0, 0x0D3F2, 0x25F1A), // Box 13
|
||||
new BlockInfoNDS(0x0D400, 0x0ff0, 0x0E3F2, 0x25F1C), // Box 14
|
||||
new BlockInfoNDS(0x0E400, 0x0ff0, 0x0F3F2, 0x25F1E), // Box 15
|
||||
new BlockInfoNDS(0x0F400, 0x0ff0, 0x103F2, 0x25F20), // Box 16
|
||||
new BlockInfoNDS(0x10400, 0x0ff0, 0x113F2, 0x25F22), // Box 17
|
||||
new BlockInfoNDS(0x11400, 0x0ff0, 0x123F2, 0x25F24), // Box 18
|
||||
new BlockInfoNDS(0x12400, 0x0ff0, 0x133F2, 0x25F26), // Box 19
|
||||
new BlockInfoNDS(0x13400, 0x0ff0, 0x143F2, 0x25F28), // Box 20
|
||||
new BlockInfoNDS(0x14400, 0x0ff0, 0x153F2, 0x25F2A), // Box 21
|
||||
new BlockInfoNDS(0x15400, 0x0ff0, 0x163F2, 0x25F2C), // Box 22
|
||||
new BlockInfoNDS(0x16400, 0x0ff0, 0x173F2, 0x25F2E), // Box 23
|
||||
new BlockInfoNDS(0x17400, 0x0ff0, 0x183F2, 0x25F30), // Box 24
|
||||
new BlockInfoNDS(0x18400, 0x09ec, 0x18DEE, 0x25F32), // Inventory
|
||||
new BlockInfoNDS(0x18E00, 0x0534, 0x19336, 0x25F34), // Party Pokemon
|
||||
new BlockInfoNDS(0x19400, 0x00b0, 0x194B2, 0x25F36), // Trainer Data
|
||||
new BlockInfoNDS(0x19500, 0x00a8, 0x195AA, 0x25F38), // Trainer Position
|
||||
new BlockInfoNDS(0x19600, 0x1338, 0x1A93A, 0x25F3A), // Unity Tower and survey stuff
|
||||
new BlockInfoNDS(0x1AA00, 0x07c4, 0x1B1C6, 0x25F3C), // Pal Pad Player Data (30d)
|
||||
new BlockInfoNDS(0x1B200, 0x0d54, 0x1BF56, 0x25F3E), // Pal Pad Friend Data
|
||||
new BlockInfoNDS(0x1C000, 0x0094, 0x1C096, 0x25F40), // Skin Info
|
||||
new BlockInfoNDS(0x1C100, 0x0658, 0x1C75A, 0x25F42), // Card Signature Block & ????
|
||||
new BlockInfoNDS(0x1C800, 0x0a94, 0x1D296, 0x25F44), // Mystery Gift
|
||||
new BlockInfoNDS(0x1D300, 0x01ac, 0x1D4AE, 0x25F46), // Dream World Stuff (Catalog)
|
||||
new BlockInfoNDS(0x1D500, 0x03ec, 0x1D8EE, 0x25F48), // Chatter
|
||||
new BlockInfoNDS(0x1D900, 0x005c, 0x1D95E, 0x25F4A), // Adventure data
|
||||
new BlockInfoNDS(0x1DA00, 0x01e0, 0x1DBE2, 0x25F4C), // Trainer Card Records
|
||||
new BlockInfoNDS(0x1DC00, 0x00a8, 0x1DCAA, 0x25F4E), // ???
|
||||
new BlockInfoNDS(0x1DD00, 0x0460, 0x1E162, 0x25F50), // (40d)
|
||||
new BlockInfoNDS(0x1E200, 0x1400, 0x1F602, 0x25F52), // ???
|
||||
new BlockInfoNDS(0x1F700, 0x02a4, 0x1F9A6, 0x25F54), // Contains flags and references for downloaded data (Musical)
|
||||
new BlockInfoNDS(0x1FA00, 0x00e0, 0x1FAE2, 0x25F56), // Fused Reshiram/Zekrom Storage
|
||||
new BlockInfoNDS(0x1FB00, 0x034c, 0x1FE4E, 0x25F58), // ???
|
||||
new BlockInfoNDS(0x1FF00, 0x04e0, 0x203E2, 0x25F5A), // Const Data Block and Event Flag Block (0x35E is the split)
|
||||
new BlockInfoNDS(0x20400, 0x00f8, 0x204FA, 0x25F5C), // ???
|
||||
new BlockInfoNDS(0x20500, 0x02fc, 0x207FE, 0x25F5E), // Tournament Block
|
||||
new BlockInfoNDS(0x20800, 0x0094, 0x20896, 0x25F60), // ???
|
||||
new BlockInfoNDS(0x20900, 0x035c, 0x20C5E, 0x25F62), // Battle Box Block
|
||||
new BlockInfoNDS(0x20D00, 0x01d4, 0x20ED6, 0x25F64), // Daycare Block (50d)
|
||||
new BlockInfoNDS(0x20F00, 0x01e0, 0x210E2, 0x25F66), // Strength Boulder Status Block
|
||||
new BlockInfoNDS(0x21100, 0x00f0, 0x211F2, 0x25F68), // Badge Flags, Money, Trainer Sayings
|
||||
new BlockInfoNDS(0x21200, 0x01b4, 0x213B6, 0x25F6A), // Entralink (Level & Powers etc)
|
||||
new BlockInfoNDS(0x21400, 0x04dc, 0x218DE, 0x25F6C), // Pokedex
|
||||
new BlockInfoNDS(0x21900, 0x0034, 0x21936, 0x25F6E), // Swarm and other overworld info - 2C - swarm, 2D - repel steps, 2E repel type
|
||||
new BlockInfoNDS(0x21A00, 0x003c, 0x21A3E, 0x25F70), // ???
|
||||
new BlockInfoNDS(0x21B00, 0x01ac, 0x21CAE, 0x25F72), // Battle Subway
|
||||
new BlockInfoNDS(0x21D00, 0x0b90, 0x22892, 0x25F74), // ???
|
||||
new BlockInfoNDS(0x22900, 0x00ac, 0x229AE, 0x25F76), // Online Records
|
||||
new BlockInfoNDS(0x22A00, 0x0850, 0x23252, 0x25F78), // Entralink Forest pokémon data (60d)
|
||||
new BlockInfoNDS(0x23300, 0x0284, 0x23586, 0x25F7A), // ???
|
||||
new BlockInfoNDS(0x23600, 0x0010, 0x23612, 0x25F7C), // ???
|
||||
new BlockInfoNDS(0x23700, 0x00a8, 0x237AA, 0x25F7E), // PWT related data
|
||||
new BlockInfoNDS(0x23800, 0x016c, 0x2396E, 0x25F80), // ???
|
||||
new BlockInfoNDS(0x23A00, 0x0080, 0x23A82, 0x25F82), // ???
|
||||
new BlockInfoNDS(0x23B00, 0x00fc, 0x23BFE, 0x25F84), // Hollow/Rival Block
|
||||
new BlockInfoNDS(0x23C00, 0x16a8, 0x252AA, 0x25F86), // Join Avenue Block
|
||||
new BlockInfoNDS(0x25300, 0x0498, 0x2579A, 0x25F88), // Medal data
|
||||
new BlockInfoNDS(0x25800, 0x0060, 0x25862, 0x25F8A), // Key-related data
|
||||
new BlockInfoNDS(0x25900, 0x00fc, 0x259FE, 0x25F8C), // (70d)
|
||||
new BlockInfoNDS(0x25A00, 0x03e4, 0x25DE6, 0x25F8E), // ???
|
||||
new BlockInfoNDS(0x25E00, 0x00f0, 0x25EF2, 0x25F90), // ???
|
||||
new BlockInfoNDS(0x25F00, 0x0094, 0x25FA2, 0x25FA2), // Checksum Block (73d)
|
||||
};
|
||||
}
|
||||
}
|
51
PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs
Normal file
51
PKHeX.Core/Saves/Blocks/BlockInfoRSBOX.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class BlockInfoRSBOX : BlockInfo
|
||||
{
|
||||
public readonly uint SaveCount;
|
||||
public readonly uint Checksum;
|
||||
|
||||
public BlockInfoRSBOX(byte[] data, int offset)
|
||||
{
|
||||
Offset = offset;
|
||||
Length = 0x1FFC;
|
||||
|
||||
// Values stored in Big Endian format
|
||||
Checksum = BigEndian.ToUInt32(data, Offset + 0);
|
||||
ID = BigEndian.ToUInt32(data, Offset + 4);
|
||||
SaveCount = BigEndian.ToUInt32(data, Offset + 8);
|
||||
}
|
||||
|
||||
protected override bool ChecksumValid(byte[] data)
|
||||
{
|
||||
var chk = GetChecksum(data);
|
||||
var old = BigEndian.ToUInt32(data, Offset);
|
||||
return chk == old;
|
||||
}
|
||||
|
||||
protected override void SetChecksum(byte[] data)
|
||||
{
|
||||
var chk = GetChecksum(data, Offset, Length);
|
||||
data[0] = (byte)(chk >> 24);
|
||||
data[1] = (byte)(chk >> 16);
|
||||
data[2] = (byte)(chk >> 8);
|
||||
data[3] = (byte)(chk >> 0);
|
||||
}
|
||||
|
||||
private uint GetChecksum(byte[] data)
|
||||
{
|
||||
int start = Offset + 4;
|
||||
int length = start + Length - 4;
|
||||
|
||||
return GetChecksum(data, start, length);
|
||||
}
|
||||
|
||||
private static uint GetChecksum(byte[] data, int start, int length)
|
||||
{
|
||||
ushort chk = 0; // initial value
|
||||
for (int j = start; j < length; j += 2)
|
||||
chk += BigEndian.ToUInt16(data, j);
|
||||
return (uint)(chk << 16 | (0xF004 - chk));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,26 +31,27 @@ namespace PKHeX.Core
|
|||
if (SaveUtil.GetIsG3BOXSAV(Data) != GameVersion.RSBOX)
|
||||
return;
|
||||
|
||||
Blocks = new RSBOX_Block[2*BLOCK_COUNT];
|
||||
Blocks = new BlockInfo[2*BLOCK_COUNT];
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
int offset = BLOCK_SIZE + i* BLOCK_SIZE;
|
||||
Blocks[i] = new RSBOX_Block(GetData(offset, BLOCK_SIZE), offset);
|
||||
Blocks[i] = new BlockInfoRSBOX(Data, offset);
|
||||
}
|
||||
|
||||
// Detect active save
|
||||
int[] SaveCounts = Blocks.Select(block => (int)block.SaveCount).ToArray();
|
||||
int[] SaveCounts = Blocks.OfType<BlockInfoRSBOX>().Select(block => (int)block.SaveCount).ToArray();
|
||||
SaveCount = SaveCounts.Max();
|
||||
int ActiveSAV = Array.IndexOf(SaveCounts, SaveCount) / BLOCK_COUNT;
|
||||
Blocks = Blocks.Skip(ActiveSAV*BLOCK_COUNT).Take(BLOCK_COUNT).OrderBy(b => b.BlockNumber).ToArray();
|
||||
Blocks = Blocks.Skip(ActiveSAV*BLOCK_COUNT).Take(BLOCK_COUNT).OrderBy(b => b.ID).ToArray();
|
||||
|
||||
// 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 block to the allocated location
|
||||
foreach (RSBOX_Block b in Blocks)
|
||||
Array.Copy(b.Data, 0xC, Data, (int)(Box + b.BlockNumber*(BLOCK_SIZE - 0x10)), b.Data.Length - 0x10);
|
||||
const int copySize = BLOCK_SIZE - 0x10;
|
||||
foreach (var b in Blocks)
|
||||
Array.Copy(Data, b.Offset + 0xC, Data, (int)(Box + b.ID*copySize), copySize);
|
||||
|
||||
Personal = PersonalTable.RS;
|
||||
HeldItems = Legal.HeldItems_RS;
|
||||
|
@ -59,22 +60,20 @@ namespace PKHeX.Core
|
|||
ClearBoxes();
|
||||
}
|
||||
|
||||
private readonly RSBOX_Block[] Blocks;
|
||||
private readonly BlockInfo[] Blocks;
|
||||
private readonly int SaveCount;
|
||||
private const int BLOCK_COUNT = 23;
|
||||
private const int BLOCK_SIZE = 0x2000;
|
||||
private const int SIZE_RESERVED = BLOCK_COUNT * BLOCK_SIZE; // unpacked box data
|
||||
public override byte[] Write(bool DSV, bool GCI)
|
||||
{
|
||||
// Copy Box data back to block
|
||||
foreach (RSBOX_Block b in Blocks)
|
||||
Array.Copy(Data, (int)(Box + b.BlockNumber * (BLOCK_SIZE - 0x10)), b.Data, 0xC, b.Data.Length - 0x10);
|
||||
// Copy Box data back
|
||||
const int copySize = BLOCK_SIZE - 0x10;
|
||||
foreach (var b in Blocks)
|
||||
Array.Copy(Data, (int)(Box + b.ID * copySize), Data, b.Offset + 0xC, copySize);
|
||||
|
||||
SetChecksums();
|
||||
|
||||
// Set Data Back
|
||||
foreach (RSBOX_Block b in Blocks)
|
||||
b.Data.CopyTo(Data, b.Offset);
|
||||
byte[] newFile = GetData(0, Data.Length - SIZE_RESERVED);
|
||||
|
||||
// Return the gci if Memory Card is not being exported
|
||||
|
@ -117,23 +116,9 @@ namespace PKHeX.Core
|
|||
public override bool HasParty => false;
|
||||
|
||||
// Checksums
|
||||
protected override void SetChecksums()
|
||||
{
|
||||
foreach (RSBOX_Block b in Blocks)
|
||||
b.SetChecksums();
|
||||
}
|
||||
public override bool ChecksumsValid
|
||||
{
|
||||
get { return Blocks.All(t => t.ChecksumsValid); }
|
||||
}
|
||||
public override string ChecksumInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Join(Environment.NewLine,
|
||||
Blocks.Where(b => !b.ChecksumsValid).Select(b => $"Block {b.BlockNumber:00} invalid"));
|
||||
}
|
||||
}
|
||||
protected override void SetChecksums() => Blocks.SetChecksums(Data);
|
||||
public override bool ChecksumsValid => Blocks.GetChecksumsValid(Data);
|
||||
public override string ChecksumInfo => Blocks.GetChecksumInfo(Data);
|
||||
|
||||
// Trainer Info
|
||||
public override GameVersion Version { get => GameVersion.RSBOX; protected set { } }
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -95,7 +93,7 @@ namespace PKHeX.Core
|
|||
break;
|
||||
}
|
||||
HeldItems = Legal.HeldItems_BW;
|
||||
GetBlockInfo();
|
||||
Blocks = Version == GameVersion.BW ? BlockInfoNDS.BlocksBW : BlockInfoNDS.BlocksB2W2;
|
||||
|
||||
if (!Exportable)
|
||||
ClearBoxes();
|
||||
|
@ -126,239 +124,11 @@ namespace PKHeX.Core
|
|||
public override int MaxGameID => Legal.MaxGameID_5; // B2
|
||||
|
||||
// Blocks & Offsets
|
||||
private BlockInfo[] Blocks;
|
||||
private void GetBlockInfo()
|
||||
{
|
||||
// Can be slick with just a list of block lengths, but oh well, precomputed.
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.BW:
|
||||
Blocks = new[]
|
||||
{
|
||||
new BlockInfo(0x00000, 0x03E0, 0x003E2, 0x23F00), // Box Names
|
||||
new BlockInfo(0x00400, 0x0FF0, 0x013F2, 0x23F02), // Box 1
|
||||
new BlockInfo(0x01400, 0x0FF0, 0x023F2, 0x23F04), // Box 2
|
||||
new BlockInfo(0x02400, 0x0FF0, 0x033F2, 0x23F06), // Box 3
|
||||
new BlockInfo(0x03400, 0x0FF0, 0x043F2, 0x23F08), // Box 4
|
||||
new BlockInfo(0x04400, 0x0FF0, 0x053F2, 0x23F0A), // Box 5
|
||||
new BlockInfo(0x05400, 0x0FF0, 0x063F2, 0x23F0C), // Box 6
|
||||
new BlockInfo(0x06400, 0x0FF0, 0x073F2, 0x23F0E), // Box 7
|
||||
new BlockInfo(0x07400, 0x0FF0, 0x083F2, 0x23F10), // Box 8
|
||||
new BlockInfo(0x08400, 0x0FF0, 0x093F2, 0x23F12), // Box 9
|
||||
new BlockInfo(0x09400, 0x0FF0, 0x0A3F2, 0x23F14), // Box 10
|
||||
new BlockInfo(0x0A400, 0x0FF0, 0x0B3F2, 0x23F16), // Box 11
|
||||
new BlockInfo(0x0B400, 0x0FF0, 0x0C3F2, 0x23F18), // Box 12
|
||||
new BlockInfo(0x0C400, 0x0FF0, 0x0D3F2, 0x23F1A), // Box 13
|
||||
new BlockInfo(0x0D400, 0x0FF0, 0x0E3F2, 0x23F1C), // Box 14
|
||||
new BlockInfo(0x0E400, 0x0FF0, 0x0F3F2, 0x23F1E), // Box 15
|
||||
new BlockInfo(0x0F400, 0x0FF0, 0x103F2, 0x23F20), // Box 16
|
||||
new BlockInfo(0x10400, 0x0FF0, 0x113F2, 0x23F22), // Box 17
|
||||
new BlockInfo(0x11400, 0x0FF0, 0x123F2, 0x23F24), // Box 18
|
||||
new BlockInfo(0x12400, 0x0FF0, 0x133F2, 0x23F26), // Box 19
|
||||
new BlockInfo(0x13400, 0x0FF0, 0x143F2, 0x23F28), // Box 20
|
||||
new BlockInfo(0x14400, 0x0FF0, 0x153F2, 0x23F2A), // Box 21
|
||||
new BlockInfo(0x15400, 0x0FF0, 0x163F2, 0x23F2C), // Box 22
|
||||
new BlockInfo(0x16400, 0x0FF0, 0x173F2, 0x23F2E), // Box 23
|
||||
new BlockInfo(0x17400, 0x0FF0, 0x183F2, 0x23F30), // Box 24
|
||||
new BlockInfo(0x18400, 0x09C0, 0x18DC2, 0x23F32), // Inventory
|
||||
new BlockInfo(0x18E00, 0x0534, 0x19336, 0x23F34), // Party Pokemon
|
||||
new BlockInfo(0x19400, 0x0068, 0x1946A, 0x23F36), // Trainer Data
|
||||
new BlockInfo(0x19500, 0x009C, 0x1959E, 0x23F38), // Trainer Position
|
||||
new BlockInfo(0x19600, 0x1338, 0x1A93A, 0x23F3A), // Unity Tower and survey stuff
|
||||
new BlockInfo(0x1AA00, 0x07C4, 0x1B1C6, 0x23F3C), // Pal Pad Player Data
|
||||
new BlockInfo(0x1B200, 0x0D54, 0x1BF56, 0x23F3E), // Pal Pad Friend Data
|
||||
new BlockInfo(0x1C000, 0x002C, 0x1C02E, 0x23F40), // Skin Info
|
||||
new BlockInfo(0x1C100, 0x0658, 0x1C75A, 0x23F42), // ??? Gym badge data
|
||||
new BlockInfo(0x1C800, 0x0A94, 0x1D296, 0x23F44), // Mystery Gift
|
||||
new BlockInfo(0x1D300, 0x01AC, 0x1D4AE, 0x23F46), // Dream World Stuff (Catalog)
|
||||
new BlockInfo(0x1D500, 0x03EC, 0x1D8EE, 0x23F48), // Chatter
|
||||
new BlockInfo(0x1D900, 0x005C, 0x1D95E, 0x23F4A), // Adventure Info
|
||||
new BlockInfo(0x1DA00, 0x01E0, 0x1DBE2, 0x23F4C), // Trainer Card Records
|
||||
new BlockInfo(0x1DC00, 0x00A8, 0x1DCAA, 0x23F4E), // ???
|
||||
new BlockInfo(0x1DD00, 0x0460, 0x1E162, 0x23F50), // (40d)
|
||||
new BlockInfo(0x1E200, 0x1400, 0x1F602, 0x23F52), // ???
|
||||
new BlockInfo(0x1F700, 0x02A4, 0x1F9A6, 0x23F54), // Contains flags and references for downloaded data (Musical)
|
||||
new BlockInfo(0x1FA00, 0x02DC, 0x1FCDE, 0x23F56), // ???
|
||||
new BlockInfo(0x1FD00, 0x034C, 0x2004E, 0x23F58), // ???
|
||||
new BlockInfo(0x20100, 0x03EC, 0x204EE, 0x23F5A), // ???
|
||||
new BlockInfo(0x20500, 0x00F8, 0x205FA, 0x23F5C), // ???
|
||||
new BlockInfo(0x20600, 0x02FC, 0x208FE, 0x23F5E), // Tournament Block
|
||||
new BlockInfo(0x20900, 0x0094, 0x20996, 0x23F60), // ???
|
||||
new BlockInfo(0x20A00, 0x035C, 0x20D5E, 0x23F62), // Battle Box Block
|
||||
new BlockInfo(0x20E00, 0x01CC, 0x20FCE, 0x23F64), // Daycare Block
|
||||
new BlockInfo(0x21000, 0x0168, 0x2116A, 0x23F66), // Strength Boulder Status Block
|
||||
new BlockInfo(0x21200, 0x00EC, 0x212EE, 0x23F68), // Badge Flags, Money, Trainer Sayings
|
||||
new BlockInfo(0x21300, 0x01B0, 0x214B2, 0x23F6A), // Entralink (Level & Powers etc)
|
||||
new BlockInfo(0x21500, 0x001C, 0x2151E, 0x23F6C), // ???
|
||||
new BlockInfo(0x21600, 0x04D4, 0x21AD6, 0x23F6E), // Pokedex
|
||||
new BlockInfo(0x21B00, 0x0034, 0x21B36, 0x23F70), // Swarm and other overworld info - 2C - swarm, 2D - repel steps, 2E repel type
|
||||
new BlockInfo(0x21C00, 0x003C, 0x21C3E, 0x23F72), // ???
|
||||
new BlockInfo(0x21D00, 0x01AC, 0x21EAE, 0x23F74), // Battle Subway
|
||||
new BlockInfo(0x21F00, 0x0B90, 0x22A92, 0x23F76), // ???
|
||||
new BlockInfo(0x22B00, 0x009C, 0x22B9E, 0x23F78), // Online Records
|
||||
new BlockInfo(0x22C00, 0x0850, 0x23452, 0x23F7A), // Entralink Forest pokémon data
|
||||
new BlockInfo(0x23500, 0x0028, 0x2352A, 0x23F7C), // ???
|
||||
new BlockInfo(0x23600, 0x0284, 0x23886, 0x23F7E), // ???
|
||||
new BlockInfo(0x23900, 0x0010, 0x23912, 0x23F80), // ???
|
||||
new BlockInfo(0x23A00, 0x005C, 0x23A5E, 0x23F82), // ???
|
||||
new BlockInfo(0x23B00, 0x016C, 0x23C6E, 0x23F84), // ???
|
||||
new BlockInfo(0x23D00, 0x0040, 0x23D42, 0x23F86), // ???
|
||||
new BlockInfo(0x23E00, 0x00FC, 0x23EFE, 0x23F88), // ???
|
||||
new BlockInfo(0x23F00, 0x008C, 0x23F9A, 0x23F9A), // Checksums */
|
||||
};
|
||||
break;
|
||||
case GameVersion.B2W2:
|
||||
Blocks = new[]
|
||||
{
|
||||
// Offset, Length, CHKOfst, ChkMirror
|
||||
new BlockInfo(0x00000, 0x03e0, 0x003E2, 0x25F00), // Box Names
|
||||
new BlockInfo(0x00400, 0x0ff0, 0x013F2, 0x25F02), // Box 1
|
||||
new BlockInfo(0x01400, 0x0ff0, 0x023F2, 0x25F04), // Box 2
|
||||
new BlockInfo(0x02400, 0x0ff0, 0x033F2, 0x25F06), // Box 3
|
||||
new BlockInfo(0x03400, 0x0ff0, 0x043F2, 0x25F08), // Box 4
|
||||
new BlockInfo(0x04400, 0x0ff0, 0x053F2, 0x25F0A), // Box 5
|
||||
new BlockInfo(0x05400, 0x0ff0, 0x063F2, 0x25F0C), // Box 6
|
||||
new BlockInfo(0x06400, 0x0ff0, 0x073F2, 0x25F0E), // Box 7
|
||||
new BlockInfo(0x07400, 0x0ff0, 0x083F2, 0x25F10), // Box 8
|
||||
new BlockInfo(0x08400, 0x0ff0, 0x093F2, 0x25F12), // Box 9
|
||||
new BlockInfo(0x09400, 0x0ff0, 0x0A3F2, 0x25F14), // Box 10
|
||||
new BlockInfo(0x0A400, 0x0ff0, 0x0B3F2, 0x25F16), // Box 11
|
||||
new BlockInfo(0x0B400, 0x0ff0, 0x0C3F2, 0x25F18), // Box 12
|
||||
new BlockInfo(0x0C400, 0x0ff0, 0x0D3F2, 0x25F1A), // Box 13
|
||||
new BlockInfo(0x0D400, 0x0ff0, 0x0E3F2, 0x25F1C), // Box 14
|
||||
new BlockInfo(0x0E400, 0x0ff0, 0x0F3F2, 0x25F1E), // Box 15
|
||||
new BlockInfo(0x0F400, 0x0ff0, 0x103F2, 0x25F20), // Box 16
|
||||
new BlockInfo(0x10400, 0x0ff0, 0x113F2, 0x25F22), // Box 17
|
||||
new BlockInfo(0x11400, 0x0ff0, 0x123F2, 0x25F24), // Box 18
|
||||
new BlockInfo(0x12400, 0x0ff0, 0x133F2, 0x25F26), // Box 19
|
||||
new BlockInfo(0x13400, 0x0ff0, 0x143F2, 0x25F28), // Box 20
|
||||
new BlockInfo(0x14400, 0x0ff0, 0x153F2, 0x25F2A), // Box 21
|
||||
new BlockInfo(0x15400, 0x0ff0, 0x163F2, 0x25F2C), // Box 22
|
||||
new BlockInfo(0x16400, 0x0ff0, 0x173F2, 0x25F2E), // Box 23
|
||||
new BlockInfo(0x17400, 0x0ff0, 0x183F2, 0x25F30), // Box 24
|
||||
new BlockInfo(0x18400, 0x09ec, 0x18DEE, 0x25F32), // Inventory
|
||||
new BlockInfo(0x18E00, 0x0534, 0x19336, 0x25F34), // Party Pokemon
|
||||
new BlockInfo(0x19400, 0x00b0, 0x194B2, 0x25F36), // Trainer Data
|
||||
new BlockInfo(0x19500, 0x00a8, 0x195AA, 0x25F38), // Trainer Position
|
||||
new BlockInfo(0x19600, 0x1338, 0x1A93A, 0x25F3A), // Unity Tower and survey stuff
|
||||
new BlockInfo(0x1AA00, 0x07c4, 0x1B1C6, 0x25F3C), // Pal Pad Player Data (30d)
|
||||
new BlockInfo(0x1B200, 0x0d54, 0x1BF56, 0x25F3E), // Pal Pad Friend Data
|
||||
new BlockInfo(0x1C000, 0x0094, 0x1C096, 0x25F40), // Skin Info
|
||||
new BlockInfo(0x1C100, 0x0658, 0x1C75A, 0x25F42), // Card Signature Block & ????
|
||||
new BlockInfo(0x1C800, 0x0a94, 0x1D296, 0x25F44), // Mystery Gift
|
||||
new BlockInfo(0x1D300, 0x01ac, 0x1D4AE, 0x25F46), // Dream World Stuff (Catalog)
|
||||
new BlockInfo(0x1D500, 0x03ec, 0x1D8EE, 0x25F48), // Chatter
|
||||
new BlockInfo(0x1D900, 0x005c, 0x1D95E, 0x25F4A), // Adventure data
|
||||
new BlockInfo(0x1DA00, 0x01e0, 0x1DBE2, 0x25F4C), // Trainer Card Records
|
||||
new BlockInfo(0x1DC00, 0x00a8, 0x1DCAA, 0x25F4E), // ???
|
||||
new BlockInfo(0x1DD00, 0x0460, 0x1E162, 0x25F50), // (40d)
|
||||
new BlockInfo(0x1E200, 0x1400, 0x1F602, 0x25F52), // ???
|
||||
new BlockInfo(0x1F700, 0x02a4, 0x1F9A6, 0x25F54), // Contains flags and references for downloaded data (Musical)
|
||||
new BlockInfo(0x1FA00, 0x00e0, 0x1FAE2, 0x25F56), // Fused Reshiram/Zekrom Storage
|
||||
new BlockInfo(0x1FB00, 0x034c, 0x1FE4E, 0x25F58), // ???
|
||||
new BlockInfo(0x1FF00, 0x04e0, 0x203E2, 0x25F5A), // Const Data Block and Event Flag Block (0x35E is the split)
|
||||
new BlockInfo(0x20400, 0x00f8, 0x204FA, 0x25F5C), // ???
|
||||
new BlockInfo(0x20500, 0x02fc, 0x207FE, 0x25F5E), // Tournament Block
|
||||
new BlockInfo(0x20800, 0x0094, 0x20896, 0x25F60), // ???
|
||||
new BlockInfo(0x20900, 0x035c, 0x20C5E, 0x25F62), // Battle Box Block
|
||||
new BlockInfo(0x20D00, 0x01d4, 0x20ED6, 0x25F64), // Daycare Block (50d)
|
||||
new BlockInfo(0x20F00, 0x01e0, 0x210E2, 0x25F66), // Strength Boulder Status Block
|
||||
new BlockInfo(0x21100, 0x00f0, 0x211F2, 0x25F68), // Badge Flags, Money, Trainer Sayings
|
||||
new BlockInfo(0x21200, 0x01b4, 0x213B6, 0x25F6A), // Entralink (Level & Powers etc)
|
||||
new BlockInfo(0x21400, 0x04dc, 0x218DE, 0x25F6C), // Pokedex
|
||||
new BlockInfo(0x21900, 0x0034, 0x21936, 0x25F6E), // Swarm and other overworld info - 2C - swarm, 2D - repel steps, 2E repel type
|
||||
new BlockInfo(0x21A00, 0x003c, 0x21A3E, 0x25F70), // ???
|
||||
new BlockInfo(0x21B00, 0x01ac, 0x21CAE, 0x25F72), // Battle Subway
|
||||
new BlockInfo(0x21D00, 0x0b90, 0x22892, 0x25F74), // ???
|
||||
new BlockInfo(0x22900, 0x00ac, 0x229AE, 0x25F76), // Online Records
|
||||
new BlockInfo(0x22A00, 0x0850, 0x23252, 0x25F78), // Entralink Forest pokémon data (60d)
|
||||
new BlockInfo(0x23300, 0x0284, 0x23586, 0x25F7A), // ???
|
||||
new BlockInfo(0x23600, 0x0010, 0x23612, 0x25F7C), // ???
|
||||
new BlockInfo(0x23700, 0x00a8, 0x237AA, 0x25F7E), // PWT related data
|
||||
new BlockInfo(0x23800, 0x016c, 0x2396E, 0x25F80), // ???
|
||||
new BlockInfo(0x23A00, 0x0080, 0x23A82, 0x25F82), // ???
|
||||
new BlockInfo(0x23B00, 0x00fc, 0x23BFE, 0x25F84), // Hollow/Rival Block
|
||||
new BlockInfo(0x23C00, 0x16a8, 0x252AA, 0x25F86), // Join Avenue Block
|
||||
new BlockInfo(0x25300, 0x0498, 0x2579A, 0x25F88), // Medal data
|
||||
new BlockInfo(0x25800, 0x0060, 0x25862, 0x25F8A), // Key-related data
|
||||
new BlockInfo(0x25900, 0x00fc, 0x259FE, 0x25F8C), // (70d)
|
||||
new BlockInfo(0x25A00, 0x03e4, 0x25DE6, 0x25F8E), // ???
|
||||
new BlockInfo(0x25E00, 0x00f0, 0x25EF2, 0x25F90), // ???
|
||||
new BlockInfo(0x25F00, 0x0094, 0x25FA2, 0x25FA2), // Checksum Block (73d)
|
||||
};
|
||||
break;
|
||||
default:
|
||||
Blocks = new BlockInfo[] { };
|
||||
break;
|
||||
}
|
||||
}
|
||||
protected override void SetChecksums()
|
||||
{
|
||||
// Check for invalid block lengths
|
||||
if (Blocks.Length < 3) // arbitrary...
|
||||
{
|
||||
Debug.WriteLine("Not enough blocks ({0}), aborting SetChecksums", Blocks.Length);
|
||||
return;
|
||||
}
|
||||
// Apply checksums
|
||||
foreach (BlockInfo b in Blocks)
|
||||
{
|
||||
byte[] array = Data.Skip(b.Offset).Take(b.Length).ToArray();
|
||||
ushort chk = SaveUtil.CRC16_CCITT(array);
|
||||
BitConverter.GetBytes(chk).CopyTo(Data, b.ChecksumOffset);
|
||||
BitConverter.GetBytes(chk).CopyTo(Data, b.ChecksumMirror);
|
||||
}
|
||||
}
|
||||
public override bool ChecksumsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check for invalid block lengths
|
||||
if (Blocks.Length < 3) // arbitrary...
|
||||
{
|
||||
Debug.WriteLine("Not enough blocks ({0}), aborting SetChecksums", Blocks.Length);
|
||||
return false;
|
||||
}
|
||||
private readonly BlockInfoNDS[] Blocks;
|
||||
protected override void SetChecksums() => Blocks.SetChecksums(Data);
|
||||
public override bool ChecksumsValid => Blocks.GetChecksumsValid(Data);
|
||||
public override string ChecksumInfo => Blocks.GetChecksumInfo(Data);
|
||||
|
||||
foreach (BlockInfo b in Blocks)
|
||||
{
|
||||
byte[] array = Data.Skip(b.Offset).Take(b.Length).ToArray();
|
||||
ushort chk = SaveUtil.CRC16_CCITT(array);
|
||||
if (chk != BitConverter.ToUInt16(Data, b.ChecksumOffset))
|
||||
return false;
|
||||
if (chk != BitConverter.ToUInt16(Data, b.ChecksumMirror))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override string ChecksumInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check for invalid block lengths
|
||||
if (Blocks.Length < 3) // arbitrary...
|
||||
{
|
||||
Debug.WriteLine("Not enough blocks ({0}), aborting SetChecksums", Blocks.Length);
|
||||
return "Not a valid save to check.";
|
||||
}
|
||||
|
||||
var list = new List<string>();
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
BlockInfo b = Blocks[i];
|
||||
byte[] array = Data.Skip(b.Offset).Take(b.Length).ToArray();
|
||||
ushort chk = SaveUtil.CRC16_CCITT(array);
|
||||
if (chk != BitConverter.ToUInt16(Data, b.ChecksumOffset))
|
||||
list.Add($"Block {i} has been modified.");
|
||||
else if (chk != BitConverter.ToUInt16(Data, b.ChecksumMirror))
|
||||
list.Add($"Block {i} mirror does not match.");
|
||||
}
|
||||
list.Add($"SAV: {Blocks.Length - list.Count}/{Blocks.Length}");
|
||||
return string.Join(Environment.NewLine, list);
|
||||
}
|
||||
}
|
||||
|
||||
private const int wcSeed = 0x1D290;
|
||||
|
||||
public readonly int CGearInfoOffset, CGearDataOffset;
|
||||
|
@ -487,7 +257,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
uint seed = BitConverter.ToUInt32(Data, wcSeed);
|
||||
MysteryGiftAlbum Info = new MysteryGiftAlbum { Seed = seed };
|
||||
byte[] wcData = Data.Skip(WondercardData).Take(0xA90).ToArray(); // Encrypted, Decrypt
|
||||
byte[] wcData = GetData(WondercardData, 0xA90); // Encrypted, Decrypt
|
||||
for (int i = 0; i < wcData.Length; i += 2)
|
||||
BitConverter.GetBytes((ushort)(BitConverter.ToUInt16(wcData, i) ^ PKX.LCRNG(ref seed) >> 16)).CopyTo(wcData, i);
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -22,7 +20,7 @@ namespace PKHeX.Core
|
|||
Exportable = !Data.All(z => z == 0);
|
||||
|
||||
// Load Info
|
||||
GetBlockInfo();
|
||||
Blocks = BlockInfo3DS.GetBlockInfoData(Data, out BlockInfoOffset, SaveUtil.CRC16_CCITT);
|
||||
GetSAVOffsets();
|
||||
|
||||
HeldItems = ORAS ? Legal.HeldItem_AO : Legal.HeldItem_XY;
|
||||
|
@ -60,93 +58,12 @@ namespace PKHeX.Core
|
|||
public override bool HasGeolocation => true;
|
||||
|
||||
// Blocks & Offsets
|
||||
private int BlockInfoOffset;
|
||||
private BlockInfo[] Blocks;
|
||||
private void GetBlockInfo()
|
||||
{
|
||||
BlockInfoOffset = Data.Length - 0x200 + 0x10;
|
||||
if (BitConverter.ToUInt32(Data, BlockInfoOffset) != SaveUtil.BEEF)
|
||||
BlockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
||||
int count = (Data.Length - BlockInfoOffset - 0x8) / 8;
|
||||
BlockInfoOffset += 4;
|
||||
private readonly int BlockInfoOffset;
|
||||
private readonly BlockInfo[] Blocks;
|
||||
protected override void SetChecksums() => Blocks.SetChecksums(Data);
|
||||
public override bool ChecksumsValid => Blocks.GetChecksumsValid(Data);
|
||||
public override string ChecksumInfo => Blocks.GetChecksumInfo(Data);
|
||||
|
||||
Blocks = new BlockInfo[count];
|
||||
int CurrentPosition = 0;
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
Blocks[i] = new BlockInfo
|
||||
{
|
||||
Offset = CurrentPosition,
|
||||
Length = BitConverter.ToInt32(Data, BlockInfoOffset + 0 + 8 * i),
|
||||
ID = BitConverter.ToUInt16(Data, BlockInfoOffset + 4 + 8 * i),
|
||||
Checksum = BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + 8 * i)
|
||||
};
|
||||
|
||||
// Expand out to nearest 0x200
|
||||
CurrentPosition += Blocks[i].Length % 0x200 == 0 ? Blocks[i].Length : 0x200 - Blocks[i].Length % 0x200 + Blocks[i].Length;
|
||||
|
||||
if ((Blocks[i].ID != 0) || i == 0) continue;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
// Fix Final Array Lengths
|
||||
Array.Resize(ref Blocks, count);
|
||||
}
|
||||
protected override void SetChecksums()
|
||||
{
|
||||
// Check for invalid block lengths
|
||||
if (Blocks.Length < 3) // arbitrary...
|
||||
{
|
||||
Debug.WriteLine("Not enough blocks ({0}), aborting SetChecksums", Blocks.Length);
|
||||
return;
|
||||
}
|
||||
// Apply checksums
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
{ Debug.WriteLine("Block {0} has invalid offset/length value.", i); return; }
|
||||
byte[] array = new byte[Blocks[i].Length];
|
||||
Array.Copy(Data, Blocks[i].Offset, array, 0, array.Length);
|
||||
BitConverter.GetBytes(SaveUtil.CRC16_CCITT(array)).CopyTo(Data, BlockInfoOffset + 6 + i * 8);
|
||||
}
|
||||
}
|
||||
public override bool ChecksumsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
return false;
|
||||
byte[] array = new byte[Blocks[i].Length];
|
||||
Array.Copy(Data, Blocks[i].Offset, array, 0, array.Length);
|
||||
if (SaveUtil.CRC16_CCITT(array) != BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + i * 8))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override string ChecksumInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
var list = new List<string>();
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
return $"Block {i} Invalid Offset/Length.";
|
||||
byte[] array = new byte[Blocks[i].Length];
|
||||
Array.Copy(Data, Blocks[i].Offset, array, 0, array.Length);
|
||||
if (SaveUtil.CRC16_CCITT(array) == BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + i * 8))
|
||||
continue;
|
||||
|
||||
list.Add($"Invalid: {i:X2} @ Region {Blocks[i].Offset:X5}");
|
||||
}
|
||||
// Return Outputs
|
||||
list.Add($"SAV: {Blocks.Length - list.Count}/{Blocks.Length}");
|
||||
return string.Join(Environment.NewLine, list);
|
||||
}
|
||||
}
|
||||
public override ulong? Secure1
|
||||
{
|
||||
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14);
|
||||
|
@ -1065,10 +982,10 @@ namespace PKHeX.Core
|
|||
public override string MiscSaveChecks()
|
||||
{
|
||||
string r = "";
|
||||
byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < Data.Length / 0x200; i++)
|
||||
{
|
||||
if (!FFFF.SequenceEqual(Data.Skip(i * 0x200).Take(0x200))) continue;
|
||||
if (Data.Skip(i * 0x200).Take(0x200).Any(z => z != 0xFF))
|
||||
continue;
|
||||
r = $"0x200 chunk @ 0x{i*0x200:X5} is FF'd."
|
||||
+ Environment.NewLine + "Cyber will screw up (as of August 31st 2014)." + Environment.NewLine + Environment.NewLine;
|
||||
|
||||
|
@ -1083,11 +1000,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
return r;
|
||||
}
|
||||
public override string MiscSaveInfo()
|
||||
{
|
||||
return string.Join(Environment.NewLine,
|
||||
Blocks.Select(b => $"{b.ID:00}: {b.Offset:X5}-{b.Offset + b.Length:X5}, {b.Length:X5}"));
|
||||
}
|
||||
public override string MiscSaveInfo() => string.Join(Environment.NewLine, Blocks.Select(b => b.Summary));
|
||||
|
||||
public override string GetString(int Offset, int Count) => StringConverter.GetString6(Data, Offset, Count);
|
||||
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
|
||||
|
|
|
@ -27,7 +27,9 @@ namespace PKHeX.Core
|
|||
Exportable = !Data.All(z => z == 0);
|
||||
|
||||
// Load Info
|
||||
GetBlockInfo();
|
||||
Blocks = BlockInfo3DS.GetBlockInfoData(Data, out BlockInfoOffset, SaveUtil.CRC16);
|
||||
if (Exportable)
|
||||
CanReadChecksums();
|
||||
GetSAVOffsets();
|
||||
|
||||
HeldItems = USUM ? Legal.HeldItems_USUM : Legal.HeldItems_SM;
|
||||
|
@ -79,44 +81,12 @@ namespace PKHeX.Core
|
|||
public override bool HasGeolocation => true;
|
||||
|
||||
// Blocks & Offsets
|
||||
private int BlockInfoOffset;
|
||||
private BlockInfo[] Blocks;
|
||||
private void GetBlockInfo()
|
||||
{
|
||||
BlockInfoOffset = Data.Length - 0x200 + 0x10;
|
||||
if (BitConverter.ToUInt32(Data, BlockInfoOffset) != SaveUtil.BEEF)
|
||||
BlockInfoOffset -= 0x200; // No savegames have more than 0x3D blocks, maybe in the future?
|
||||
int count = (Data.Length - BlockInfoOffset - 0x8) / 8;
|
||||
BlockInfoOffset += 4;
|
||||
|
||||
Blocks = new BlockInfo[count];
|
||||
int CurrentPosition = 0;
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
Blocks[i] = new BlockInfo
|
||||
{
|
||||
Offset = CurrentPosition,
|
||||
Length = BitConverter.ToInt32(Data, BlockInfoOffset + 0 + 8 * i),
|
||||
ID = BitConverter.ToUInt16(Data, BlockInfoOffset + 4 + 8 * i),
|
||||
Checksum = BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + 8 * i)
|
||||
};
|
||||
|
||||
// Expand out to nearest 0x200
|
||||
CurrentPosition += Blocks[i].Length % 0x200 == 0 ? Blocks[i].Length : 0x200 - Blocks[i].Length % 0x200 + Blocks[i].Length;
|
||||
|
||||
if ((Blocks[i].ID != 0) || i == 0) continue;
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
// Fix Final Array Lengths
|
||||
Array.Resize(ref Blocks, count);
|
||||
|
||||
if (Exportable)
|
||||
CanReadChecksums();
|
||||
}
|
||||
|
||||
private readonly int BlockInfoOffset;
|
||||
private readonly BlockInfo[] Blocks;
|
||||
private bool IsMemeCryptoApplied = true;
|
||||
private const int MemeCryptoBlock = 36;
|
||||
public override bool ChecksumsValid => CanReadChecksums() && Blocks.GetChecksumsValid(Data);
|
||||
public override string ChecksumInfo => CanReadChecksums() ? Blocks.GetChecksumInfo(Data) : string.Empty;
|
||||
private bool CanReadChecksums()
|
||||
{
|
||||
if (Blocks.Length <= MemeCryptoBlock)
|
||||
|
@ -132,61 +102,11 @@ namespace PKHeX.Core
|
|||
{
|
||||
if (!CanReadChecksums())
|
||||
return;
|
||||
// Apply checksums
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
{ Debug.WriteLine($"Block {i} has invalid offset/length value."); return; }
|
||||
|
||||
ushort chk = SaveUtil.CRC16(Data, Blocks[i].Offset, Blocks[i].Length);
|
||||
BitConverter.GetBytes(chk).CopyTo(Data, BlockInfoOffset + 6 + i * 8);
|
||||
}
|
||||
|
||||
Blocks.SetChecksums(Data);
|
||||
Data = SaveUtil.Resign7(Data);
|
||||
IsMemeCryptoApplied = true;
|
||||
}
|
||||
public override bool ChecksumsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanReadChecksums())
|
||||
return false;
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
return false;
|
||||
ushort chk = SaveUtil.CRC16(Data, Blocks[i].Offset, Blocks[i].Length);
|
||||
if (chk != BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + i * 8))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override string ChecksumInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanReadChecksums())
|
||||
return string.Empty;
|
||||
|
||||
int invalid = 0;
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < Blocks.Length; i++)
|
||||
{
|
||||
if (Blocks[i].Length + Blocks[i].Offset > Data.Length)
|
||||
return $"Block {i} Invalid Offset/Length.";
|
||||
ushort chk = SaveUtil.CRC16(Data, Blocks[i].Offset, Blocks[i].Length);
|
||||
if (chk == BitConverter.ToUInt16(Data, BlockInfoOffset + 6 + i * 8))
|
||||
continue;
|
||||
|
||||
invalid++;
|
||||
sb.AppendLine($"Invalid: {i:X2} @ Region {Blocks[i].Offset:X5}");
|
||||
}
|
||||
// Return Outputs
|
||||
sb.AppendLine($"SAV: {Blocks.Length - invalid}/{Blocks.Length}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
public override ulong? Secure1
|
||||
{
|
||||
get => BitConverter.ToUInt64(Data, BlockInfoOffset - 0x14);
|
||||
|
@ -1502,10 +1422,10 @@ namespace PKHeX.Core
|
|||
var r = new StringBuilder();
|
||||
|
||||
// FFFF checks
|
||||
byte[] FFFF = Enumerable.Repeat((byte)0xFF, 0x200).ToArray();
|
||||
for (int i = 0; i < Data.Length / 0x200; i++)
|
||||
{
|
||||
if (!FFFF.SequenceEqual(Data.Skip(i * 0x200).Take(0x200))) continue;
|
||||
if (Data.Skip(i * 0x200).Take(0x200).Any(z => z != 0xFF))
|
||||
continue;
|
||||
r.AppendLine($"0x200 chunk @ 0x{i*0x200:X5} is FF'd.");
|
||||
r.AppendLine("Cyber will screw up (as of August 31st 2014).");
|
||||
r.AppendLine();
|
||||
|
@ -1521,11 +1441,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
return r.ToString();
|
||||
}
|
||||
public override string MiscSaveInfo()
|
||||
{
|
||||
return string.Join(Environment.NewLine,
|
||||
Blocks.Select(b => $"{b.ID:00}: {b.Offset:X5}-{b.Offset + b.Length:X5}, {b.Length:X5}"));
|
||||
}
|
||||
public override string MiscSaveInfo() => string.Join(Environment.NewLine, Blocks.Select(b => b.Summary));
|
||||
public bool MegaUnlocked
|
||||
{
|
||||
get => (Data[TrainerCard + 0x78] & 0x01) != 0;
|
||||
|
|
|
@ -652,24 +652,25 @@ namespace PKHeX.Core
|
|||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
|
||||
};
|
||||
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array. Used in Gen7 save files.</summary>
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <param name="initial">Initial value for checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16(byte[] data, int start, int length, ushort initial = 0)
|
||||
public static ushort CRC16(byte[] data, int start, int length, ushort initial)
|
||||
{
|
||||
ushort chk = (ushort)~initial;
|
||||
for (var i = start; i < start + length; i++)
|
||||
chk = (ushort) (crc16[(data[i] ^ chk) & 0xFF] ^ chk >> 8);
|
||||
return (ushort)~chk;
|
||||
}
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array. Used in Gen7 save files.</summary>
|
||||
/// <summary>Calculates the 16bit checksum over an input byte array.</summary>
|
||||
/// <param name="data">Input byte array</param>
|
||||
/// <param name="initial">Initial value for checksum</param>
|
||||
/// <param name="start">Offset to start checksum at</param>
|
||||
/// <param name="length">Length of array to checksum</param>
|
||||
/// <returns>Checksum</returns>
|
||||
public static ushort CRC16(byte[] data, ushort initial = 0) => CRC16(data, 0, data.Length, initial);
|
||||
public static ushort CRC16(byte[] data, int start, int length) => CRC16(data, start, length, 0);
|
||||
public static byte[] Resign7(byte[] sav7)
|
||||
{
|
||||
return MemeCrypto.Resign7(sav7);
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class BlockInfo
|
||||
{
|
||||
// General
|
||||
public int Offset;
|
||||
public int Length;
|
||||
|
||||
// Gen6
|
||||
public ushort ID;
|
||||
public ushort Checksum;
|
||||
public BlockInfo() { }
|
||||
|
||||
// Gen4/5
|
||||
public readonly int ChecksumOffset;
|
||||
public readonly int ChecksumMirror;
|
||||
public BlockInfo(int offset, int length, int chkOffset, int chkMirror)
|
||||
{
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
ChecksumOffset = chkOffset;
|
||||
ChecksumMirror = chkMirror;
|
||||
}
|
||||
}
|
||||
|
||||
public class RSBOX_Block
|
||||
{
|
||||
private ushort CHK_0;
|
||||
private ushort CHK_1;
|
||||
|
||||
public readonly uint BlockNumber;
|
||||
public readonly uint SaveCount;
|
||||
public readonly byte[] Data;
|
||||
|
||||
public readonly int Offset;
|
||||
|
||||
public RSBOX_Block(byte[] data, int offset)
|
||||
{
|
||||
Data = (byte[])data.Clone();
|
||||
Offset = offset;
|
||||
// Values stored in Big Endian format
|
||||
CHK_0 = (ushort)((Data[0x0] << 8) | (Data[0x1] << 0));
|
||||
CHK_1 = (ushort)((Data[0x2] << 8) | (Data[0x3] << 0));
|
||||
BlockNumber = (uint)((Data[0x4] << 8) | (Data[0x5] << 8) | (Data[0x6] << 8) | (Data[0x7] << 0));
|
||||
SaveCount = (uint)((Data[0x8] << 8) | (Data[0x9] << 8) | (Data[0xA] << 8) | (Data[0xB] << 0));
|
||||
}
|
||||
|
||||
public bool ChecksumsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort[] chks = GetChecksum(Data);
|
||||
return chks[0] == CHK_0 && chks[1] == CHK_1;
|
||||
}
|
||||
}
|
||||
public void SetChecksums()
|
||||
{
|
||||
ushort[] chks = GetChecksum(Data);
|
||||
CHK_0 = chks[0];
|
||||
CHK_1 = chks[1];
|
||||
Data[0] = (byte)(CHK_0 >> 8);
|
||||
Data[1] = (byte)(CHK_0 & 0xFF);
|
||||
Data[2] = (byte)(CHK_1 >> 8);
|
||||
Data[3] = (byte)(CHK_1 & 0xFF);
|
||||
}
|
||||
|
||||
private static ushort[] GetChecksum(byte[] data)
|
||||
{
|
||||
int chk = 0; // initial value
|
||||
for (int j = 0x4; j < 0x1FFC; j += 2)
|
||||
{
|
||||
chk += data[j] << 8;
|
||||
chk += data[j + 1];
|
||||
}
|
||||
ushort chk0 = (ushort)chk;
|
||||
ushort chk1 = (ushort)(0xF004 - chk0);
|
||||
|
||||
return new[] { chk0, chk1 };
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue