mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
Relocate xd/batrev encryption
update some xmldoc too
This commit is contained in:
parent
f212ba29c9
commit
e614aa9ac8
4 changed files with 115 additions and 140 deletions
|
@ -61,7 +61,7 @@ namespace PKHeX.Core
|
|||
keys[i] = BigEndian.ToUInt16(slot, 8 + (i * 2));
|
||||
|
||||
// Decrypt Slot
|
||||
Data = SaveUtil.DecryptGC(slot, 0x00010, 0x27FD8, keys);
|
||||
Data = GCSaveUtil.Decrypt(slot, 0x00010, 0x27FD8, keys);
|
||||
}
|
||||
|
||||
// Get Offset Info
|
||||
|
@ -137,7 +137,7 @@ namespace PKHeX.Core
|
|||
ushort[] keys = new ushort[4];
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(Data, 8 + (i * 2));
|
||||
byte[] newSAV = SaveUtil.EncryptGC(Data, 0x10, 0x27FD8, keys);
|
||||
byte[] newSAV = GCSaveUtil.Encrypt(Data, 0x10, 0x27FD8, keys);
|
||||
|
||||
// Put save slot back in original save data
|
||||
byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[])BAK.Clone();
|
||||
|
|
|
@ -233,24 +233,11 @@ namespace PKHeX.Core
|
|||
public static byte[] DecryptPBRSaveData(byte[] input)
|
||||
{
|
||||
byte[] output = new byte[input.Length];
|
||||
ushort[] keys = new ushort[4];
|
||||
for (int base_ofs = 0; base_ofs < SaveUtil.SIZE_G4BR; base_ofs += 0x1C0000)
|
||||
for (int i = 0; i < SaveUtil.SIZE_G4BR; i += 0x1C0000)
|
||||
{
|
||||
Array.Copy(input, base_ofs, output, base_ofs, 8);
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(input, base_ofs + (i * 2));
|
||||
|
||||
for (int ofs = base_ofs + 8; ofs < base_ofs + 0x1C0000; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
ushort val = BigEndian.ToUInt16(input, ofs + (i * 2));
|
||||
val -= keys[i];
|
||||
output[ofs + (i * 2)] = (byte)(val >> 8);
|
||||
output[ofs + (i * 2) + 1] = (byte)val;
|
||||
}
|
||||
SaveUtil.AdvanceGCKeys(keys);
|
||||
}
|
||||
var keys = GetKeys(input, i);
|
||||
Array.Copy(input, i, output, i, 8);
|
||||
GCSaveUtil.Decrypt(input, i + 8, i + 0x1C0000, keys, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
@ -258,28 +245,23 @@ namespace PKHeX.Core
|
|||
private static byte[] EncryptPBRSaveData(byte[] input)
|
||||
{
|
||||
byte[] output = new byte[input.Length];
|
||||
ushort[] keys = new ushort[4];
|
||||
for (int base_ofs = 0; base_ofs < SaveUtil.SIZE_G4BR; base_ofs += 0x1C0000)
|
||||
for (int i = 0; i < SaveUtil.SIZE_G4BR; i += 0x1C0000)
|
||||
{
|
||||
Array.Copy(input, base_ofs, output, base_ofs, 8);
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(input, base_ofs + (i * 2));
|
||||
|
||||
for (int ofs = base_ofs + 8; ofs < base_ofs + 0x1C0000; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
ushort val = BigEndian.ToUInt16(input, ofs + (i * 2));
|
||||
val += keys[i];
|
||||
output[ofs + (i * 2)] = (byte)(val >> 8);
|
||||
output[ofs + (i * 2) + 1] = (byte)val;
|
||||
}
|
||||
SaveUtil.AdvanceGCKeys(keys);
|
||||
}
|
||||
var keys = GetKeys(input, i);
|
||||
Array.Copy(input, i, output, i, 8);
|
||||
GCSaveUtil.Encrypt(input, i + 8, i + 0x1C0000, keys, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private static ushort[] GetKeys(byte[] input, int ofs)
|
||||
{
|
||||
ushort[] keys = new ushort[4];
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(input, ofs + (i * 2));
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static bool VerifyChecksum(byte[] input, int offset, int len, int checksum_offset)
|
||||
{
|
||||
uint[] storedChecksums = new uint[16];
|
||||
|
|
74
PKHeX.Core/Saves/Util/GCSaveUtil.cs
Normal file
74
PKHeX.Core/Saves/Util/GCSaveUtil.cs
Normal file
|
@ -0,0 +1,74 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Logic for <see cref="GameVersion.XD"/> and <see cref="GameVersion.BATREV"/> encryption.
|
||||
/// </summary>
|
||||
public static class GCSaveUtil
|
||||
{
|
||||
public static byte[] Decrypt(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
var output = (byte[])input.Clone();
|
||||
Decrypt(input, start, end, keys, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void Decrypt(byte[] input, int start, int end, ushort[] keys, byte[] output)
|
||||
{
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var index = ofs + (i * 2);
|
||||
ushort val = BigEndian.ToUInt16(input, index);
|
||||
val -= keys[i];
|
||||
output[index] = (byte)(val >> 8);
|
||||
output[index + 1] = (byte)val;
|
||||
}
|
||||
|
||||
AdvanceKeys(keys);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
var output = (byte[])input.Clone();
|
||||
Encrypt(input, start, end, keys, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void Encrypt(byte[] input, int start, int end, ushort[] keys, byte[] output)
|
||||
{
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var index = ofs + (i * 2);
|
||||
ushort val = BigEndian.ToUInt16(input, index);
|
||||
val += keys[i];
|
||||
output[index] = (byte)(val >> 8);
|
||||
output[index + 1] = (byte)val;
|
||||
}
|
||||
|
||||
AdvanceKeys(keys);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AdvanceKeys(ushort[] keys)
|
||||
{
|
||||
keys[0] += 0x43;
|
||||
keys[1] += 0x29;
|
||||
keys[2] += 0x17;
|
||||
keys[3] += 0x13;
|
||||
|
||||
var _0 = (ushort)((keys[0] >> 00 & 0xf) | (keys[1] << 4 & 0xf0) | (keys[2] << 8 & 0xf00) | (keys[3] << 12 & 0xf000));
|
||||
var _1 = (ushort)((keys[0] >> 04 & 0xf) | (keys[1] << 0 & 0xf0) | (keys[2] << 4 & 0xf00) | (keys[3] << 08 & 0xf000));
|
||||
var _2 = (ushort)((keys[0] >> 08 & 0xf) | (keys[1] >> 4 & 0xf0) | (keys[2] >> 0 & 0xf00) | (keys[3] << 04 & 0xf000));
|
||||
var _3 = (ushort)((keys[0] >> 12 & 0xf) | (keys[1] >> 8 & 0xf0) | (keys[2] >> 4 & 0xf00) | (keys[3] << 00 & 0xf000));
|
||||
|
||||
keys[0] = _0;
|
||||
keys[1] = _1;
|
||||
keys[2] = _2;
|
||||
keys[3] = _3;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,8 +74,8 @@ namespace PKHeX.Core
|
|||
internal static readonly string[] HEADER_XD = { "GXXJ","GXXE","GXXP" }; // NTSC-J, NTSC-U, PAL
|
||||
internal static readonly string[] HEADER_RSBOX = { "GPXJ","GPXE","GPXP" }; // NTSC-J, NTSC-U, PAL
|
||||
|
||||
/// <summary>Determines the generation of the given save data.</summary>
|
||||
/// <param name="data">Save data of which to determine the generation</param>
|
||||
/// <summary>Determines the type of the provided save data.</summary>
|
||||
/// <param name="data">Save data of which to determine the origins of</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
private static GameVersion GetSAVGeneration(byte[] data)
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a Gen2 Pokémon List is Invalid
|
||||
/// Determines if a Gen1/2 Pokémon List is Invalid
|
||||
/// </summary>
|
||||
/// <param name="data">Save data</param>
|
||||
/// <param name="offset">Offset the list starts at</param>
|
||||
|
@ -130,7 +130,7 @@ namespace PKHeX.Core
|
|||
return num_entries <= listCount && data[offset + 1 + num_entries] == 0xFF;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 1st gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen1 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG1SAV(byte[] data)
|
||||
|
@ -146,23 +146,23 @@ namespace PKHeX.Core
|
|||
return GameVersion.RBY;
|
||||
}
|
||||
|
||||
/// <summary>Determines if 1st gen save is non-japanese</summary>
|
||||
/// <summary>Checks to see if the data belongs to an International Gen1 save</summary>
|
||||
/// <param name="data">Save data of which to determine the region</param>
|
||||
/// <returns>True if a valid non-japanese save, False otherwise.</returns>
|
||||
/// <returns>True if a valid International save, False otherwise.</returns>
|
||||
private static bool GetIsG1SAVU(byte[] data)
|
||||
{
|
||||
return IsG12ListValid(data, 0x2F2C, 20) && IsG12ListValid(data, 0x30C0, 20);
|
||||
}
|
||||
|
||||
/// <summary>Determines if 1st gen save is japanese</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Japanese Gen1 save</summary>
|
||||
/// <param name="data">Save data of which to determine the region</param>
|
||||
/// <returns>True if a valid japanese save, False otherwise.</returns>
|
||||
/// <returns>True if a valid Japanese save, False otherwise.</returns>
|
||||
internal static bool GetIsG1SAVJ(byte[] data)
|
||||
{
|
||||
return IsG12ListValid(data, 0x2ED5, 30) && IsG12ListValid(data, 0x302D, 30);
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 2nd gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen2 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG2SAV(byte[] data)
|
||||
|
@ -170,7 +170,7 @@ namespace PKHeX.Core
|
|||
if (!SIZES_2.Contains(data.Length))
|
||||
return GameVersion.Invalid;
|
||||
|
||||
// Check if it's not an american save or a japanese save
|
||||
// Check if it's not an International, Japanese, or Korean save file
|
||||
GameVersion result;
|
||||
if ((result = GetIsG2SAVU(data)) != GameVersion.Invalid)
|
||||
return result;
|
||||
|
@ -181,9 +181,9 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines if 2nd gen save is non-japanese</summary>
|
||||
/// <summary>Checks to see if the data belongs to an International (not Japanese or Korean) Gen2 save</summary>
|
||||
/// <param name="data">Save data of which to determine the region</param>
|
||||
/// <returns>True if a valid international save, False otherwise.</returns>
|
||||
/// <returns>True if a valid International save, False otherwise.</returns>
|
||||
private static GameVersion GetIsG2SAVU(byte[] data)
|
||||
{
|
||||
if (IsG12ListValid(data, 0x288A, 20) && IsG12ListValid(data, 0x2D6C, 20))
|
||||
|
@ -193,7 +193,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines if 2nd gen save is japanese</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Japanese Gen2 save</summary>
|
||||
/// <param name="data">Save data of which to determine the region</param>
|
||||
/// <returns>True if a valid Japanese save, False otherwise.</returns>
|
||||
internal static GameVersion GetIsG2SAVJ(byte[] data)
|
||||
|
@ -207,7 +207,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines if 2nd gen save is Korean</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Korean Gen2 save</summary>
|
||||
/// <param name="data">Save data of which to determine the region</param>
|
||||
/// <returns>True if a valid Korean save, False otherwise.</returns>
|
||||
internal static GameVersion GetIsG2SAVK(byte[] data)
|
||||
|
@ -217,7 +217,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 3rd gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen3 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG3SAV(byte[] data)
|
||||
|
@ -253,7 +253,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 3rd gen Box RS</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen3 Box RS save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG3BOXSAV(byte[] data)
|
||||
|
@ -278,7 +278,7 @@ namespace PKHeX.Core
|
|||
return CHK_A == chkA && CHK_B == chkB ? GameVersion.RSBOX : GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 3rd gen Colosseum</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Colosseum save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG3COLOSAV(byte[] data)
|
||||
|
@ -297,7 +297,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.COLO;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 3rd gen XD</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen3 XD save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG3XDSAV(byte[] data)
|
||||
|
@ -316,7 +316,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.XD;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 4th gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen4 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG4SAV(byte[] data)
|
||||
|
@ -350,7 +350,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 4th gen Battle Revolution</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen4 Battle Revolution save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG4BRSAV(byte[] data)
|
||||
|
@ -362,7 +362,7 @@ namespace PKHeX.Core
|
|||
return SAV4BR.IsChecksumsValid(sav) ? GameVersion.BATREV : GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 5th gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen5 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
internal static GameVersion GetIsG5SAV(byte[] data)
|
||||
|
@ -382,7 +382,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.Invalid;
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 6th gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen6 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
private static GameVersion GetIsG6SAV(byte[] data)
|
||||
|
@ -400,7 +400,7 @@ namespace PKHeX.Core
|
|||
return GameVersion.ORASDEMO; // least likely
|
||||
}
|
||||
|
||||
/// <summary>Determines the type of 7th gen save</summary>
|
||||
/// <summary>Checks to see if the data belongs to a Gen7 save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
private static GameVersion GetIsG7SAV(byte[] data)
|
||||
|
@ -722,87 +722,6 @@ namespace PKHeX.Core
|
|||
bool IsGameMatchHeader(IEnumerable<string> headers, byte[] data) => headers.Contains(Encoding.ASCII.GetString(data, 0, 4));
|
||||
}
|
||||
|
||||
public static byte[] DecryptGC(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
byte[] output = (byte[])input.Clone();
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var index = ofs + (i * 2);
|
||||
ushort val = BigEndian.ToUInt16(input, index);
|
||||
val -= keys[i];
|
||||
output[index] = (byte)(val >> 8);
|
||||
output[index + 1] = (byte)val;
|
||||
}
|
||||
AdvanceGCKeys(keys);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static byte[] EncryptGC(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
byte[] output = (byte[])input.Clone();
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var index = ofs + (i * 2);
|
||||
ushort val = BigEndian.ToUInt16(input, index);
|
||||
val += keys[i];
|
||||
output[index] = (byte)(val >> 8);
|
||||
output[index + 1] = (byte)val;
|
||||
}
|
||||
AdvanceGCKeys(keys);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void AdvanceGCKeys(ushort[] keys)
|
||||
{
|
||||
keys[0] += 0x43;
|
||||
keys[1] += 0x29;
|
||||
keys[2] += 0x17;
|
||||
keys[3] += 0x13;
|
||||
|
||||
var _0 = (ushort)((keys[0] >> 00 & 0xf) | (keys[1] << 4 & 0xf0) | (keys[2] << 8 & 0xf00) | (keys[3] << 12 & 0xf000));
|
||||
var _1 = (ushort)((keys[0] >> 04 & 0xf) | (keys[1] << 0 & 0xf0) | (keys[2] << 4 & 0xf00) | (keys[3] << 08 & 0xf000));
|
||||
var _2 = (ushort)((keys[0] >> 08 & 0xf) | (keys[1] >> 4 & 0xf0) | (keys[2] >> 0 & 0xf00) | (keys[3] << 04 & 0xf000));
|
||||
var _3 = (ushort)((keys[0] >> 12 & 0xf) | (keys[1] >> 8 & 0xf0) | (keys[2] >> 4 & 0xf00) | (keys[3] << 00 & 0xf000));
|
||||
|
||||
keys[0] = _0;
|
||||
keys[1] = _1;
|
||||
keys[2] = _2;
|
||||
keys[3] = _3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a 16bit TID/SID tuple for a given G7TID.
|
||||
/// </summary>
|
||||
/// <param name="G7TID">Desired G7TID</param>
|
||||
/// <param name="minimizeSID">Optional param to yield minimum SID.</param>
|
||||
/// <returns>16bit TID/SID tuple</returns>
|
||||
public static Tuple<ushort, ushort> GetTIDSID(uint G7TID, bool minimizeSID = false)
|
||||
{
|
||||
// 32 bit number = 4294 967295
|
||||
// lowest 6 digits G7TID
|
||||
|
||||
// Bare minimum 32bit value to get ID, yields min SID
|
||||
uint val = G7TID;
|
||||
if (!minimizeSID) // randomize SID
|
||||
{
|
||||
uint s7 = 4294;
|
||||
if (val > 967295)
|
||||
s7--;
|
||||
s7 = (uint)Util.Rand.Next((int)s7);
|
||||
val += s7 * 1000000;
|
||||
}
|
||||
var TID = (ushort)(val & 0xFFFF);
|
||||
var SID = (ushort)(val >> 16);
|
||||
|
||||
return new Tuple<ushort, ushort>(TID, SID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force loads the provided <see cref="sav"/> to the requested <see cref="ver"/>.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue