misc simplifications

recognize 01 01 01 00 xd saves
simplify some detection to be easier to follow, less lines is nice too
This commit is contained in:
Kurt 2017-09-27 23:22:41 -07:00
parent cd88313fad
commit 14697f47d3
2 changed files with 62 additions and 105 deletions

View file

@ -84,6 +84,18 @@ namespace PKHeX.Core
return GameVersion.Invalid; return GameVersion.Invalid;
} }
/// <summary>
/// Determines if a Gen2 Pokémon List is Invalid
/// </summary>
/// <param name="data">Save data</param>
/// <param name="offset">Offset the list starts at</param>
/// <param name="listCount">Max count of Pokémon in the list</param>
/// <returns>True if a valid list, False otherwise</returns>
private static bool IsG12ListValid(byte[] data, int offset, int listCount)
{
byte num_entries = data[offset];
return num_entries <= listCount && data[offset + 1 + num_entries] == 0xFF;
}
/// <summary>Determines the type of 1st gen save</summary> /// <summary>Determines the type of 1st gen save</summary>
/// <param name="data">Save data of which to determine the type</param> /// <param name="data">Save data of which to determine the type</param>
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns> /// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
@ -104,26 +116,14 @@ namespace PKHeX.Core
/// <returns>True if a valid non-japanese save, False otherwise.</returns> /// <returns>True if a valid non-japanese save, False otherwise.</returns>
private static bool GetIsG1SAVU(byte[] data) private static bool GetIsG1SAVU(byte[] data)
{ {
foreach (int ofs in new[] { 0x2F2C, 0x30C0 }) return IsG12ListValid(data, 0x2F2C, 20) && IsG12ListValid(data, 0x30C0, 20);
{
byte num_entries = data[ofs];
if (num_entries > 20 || data[ofs + 1 + num_entries] != 0xFF)
return false;
}
return true;
} }
/// <summary>Determines if 1st gen save is japanese</summary> /// <summary>Determines if 1st gen save is japanese</summary>
/// <param name="data">Save data of which to determine the region</param> /// <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) internal static bool GetIsG1SAVJ(byte[] data)
{ {
foreach (int ofs in new[] { 0x2ED5, 0x302D }) return IsG12ListValid(data, 0x2ED5, 30) && IsG12ListValid(data, 0x302D, 30);
{
byte num_entries = data[ofs];
if (num_entries > 30 || data[ofs + 1 + num_entries] != 0xFF)
return false;
}
return true;
} }
/// <summary>Determines the type of 2nd gen save</summary> /// <summary>Determines the type of 2nd gen save</summary>
/// <param name="data">Save data of which to determine the type</param> /// <param name="data">Save data of which to determine the type</param>
@ -145,66 +145,36 @@ namespace PKHeX.Core
} }
/// <summary>Determines if 2nd gen save is non-japanese</summary> /// <summary>Determines if 2nd gen save is non-japanese</summary>
/// <param name="data">Save data of which to determine the region</param> /// <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 GameVersion GetIsG2SAVU(byte[] data) private static GameVersion GetIsG2SAVU(byte[] data)
{ {
bool gs = true; if (IsG12ListValid(data, 0x288A, 30) && IsG12ListValid(data, 0x2D6C, 30))
bool c = true;
foreach (int ofs in new[] { 0x288A, 0x2D6C })
{
byte num_entries = data[ofs];
if (num_entries > 20 || data[ofs + 1 + num_entries] != 0xFF)
gs = false;
}
foreach (int ofs in new[] { 0x2865, 0x2D10 })
{
byte num_entries = data[ofs];
if (num_entries > 20 || data[ofs + 1 + num_entries] != 0xFF)
c = false;
}
if (gs)
return GameVersion.GS; return GameVersion.GS;
if (c) if (IsG12ListValid(data, 0x2865, 30) && IsG12ListValid(data, 0x2D10, 30))
return GameVersion.C; return GameVersion.C;
return GameVersion.Invalid; return GameVersion.Invalid;
} }
/// <summary>Determines if 2nd gen save is japanese</summary> /// <summary>Determines if 2nd gen save is japanese</summary>
/// <param name="data">Save data of which to determine the region</param> /// <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 GameVersion GetIsG2SAVJ(byte[] data) internal static GameVersion GetIsG2SAVJ(byte[] data)
{ {
bool gs = true; if (!IsG12ListValid(data, 0x2D10, 20))
bool c = true; return GameVersion.Invalid;
foreach (int ofs in new[] { 0x283E, 0x2D10 }) if (IsG12ListValid(data, 0x283E, 20))
{
byte num_entries = data[ofs];
if (num_entries > 30 || data[ofs + 1 + num_entries] != 0xFF)
gs = false;
}
foreach (int ofs in new[] { 0x281A, 0x2D10 })
{
byte num_entries = data[ofs];
if (num_entries > 30 || data[ofs + 1 + num_entries] != 0xFF)
c = false;
}
if (gs)
return GameVersion.GS; return GameVersion.GS;
if (c) if (IsG12ListValid(data, 0x281A, 20))
return GameVersion.C; return GameVersion.C;
return GameVersion.Invalid; return GameVersion.Invalid;
} }
/// <summary>Determines if 2nd gen save is Korean</summary> /// <summary>Determines if 2nd gen save is Korean</summary>
/// <param name="data">Save data of which to determine the region</param> /// <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 Korean save, False otherwise.</returns>
internal static GameVersion GetIsG2SAVK(byte[] data) internal static GameVersion GetIsG2SAVK(byte[] data)
{ {
foreach (int ofs in new[] { 0x28CC, 0x2DAE }) if (IsG12ListValid(data, 0x2DAE, 30) && IsG12ListValid(data, 0x28CC, 30))
{ return GameVersion.GS;
byte num_entries = data[ofs]; return GameVersion.Invalid;
if (num_entries > 20 || data[ofs + 1 + num_entries] != 0xFF)
return GameVersion.Invalid;
}
return GameVersion.GS;
} }
/// <summary>Determines the type of 3rd gen save</summary> /// <summary>Determines the type of 3rd gen save</summary>
/// <param name="data">Save data of which to determine the type</param> /// <param name="data">Save data of which to determine the type</param>
@ -260,18 +230,16 @@ namespace PKHeX.Core
byte[] sav = data; byte[] sav = data;
// Verify first checksum // Verify first checksum
uint chk = 0; // initial value ushort chk = 0; // initial value
var ofs = data.Length - SIZE_G3BOX + 0x2000; var ofs = data.Length - SIZE_G3BOX + 0x2000;
for (int j = 0x4; j < 0x1FFC; j += 2) for (int i = 0x4; i < 0x1FFC; i += 2)
{ chk += BigEndian.ToUInt16(sav, ofs + i);
chk += (ushort)(sav[ofs + j] << 8);
chk += sav[ofs + j + 1]; ushort chkA = chk;
}
ushort chkA = (ushort)chk;
ushort chkB = (ushort)(0xF004 - chkA); ushort chkB = (ushort)(0xF004 - chkA);
ushort CHK_A = (ushort)((sav[ofs + 0] << 8) | sav[ofs + 1]); ushort CHK_A = BigEndian.ToUInt16(sav, ofs + 0);
ushort CHK_B = (ushort)((sav[ofs + 2] << 8) | sav[ofs + 3]); ushort CHK_B = BigEndian.ToUInt16(sav, ofs + 2);
return CHK_A == chkA && CHK_B == chkB ? GameVersion.RSBOX : GameVersion.Invalid; return CHK_A == chkA && CHK_B == chkB ? GameVersion.RSBOX : GameVersion.Invalid;
} }
@ -284,12 +252,11 @@ namespace PKHeX.Core
return GameVersion.Invalid; return GameVersion.Invalid;
// Check the intro bytes for each save slot // Check the intro bytes for each save slot
byte[] slotintroColo = {0x01, 0x01, 0x00, 0x00};
int offset = data.Length - SIZE_G3COLO; int offset = data.Length - SIZE_G3COLO;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
var ident = data.Skip(0x6000 + offset + 0x1E000*i).Take(4); var ofs = 0x6000 + offset + 0x1E000 * i;
if (!ident.SequenceEqual(slotintroColo)) if (BitConverter.ToUInt32(data, ofs) != 0x00000101)
return GameVersion.Invalid; return GameVersion.Invalid;
} }
return GameVersion.COLO; return GameVersion.COLO;
@ -299,21 +266,18 @@ namespace PKHeX.Core
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns> /// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
internal static GameVersion GetIsG3XDSAV(byte[] data) internal static GameVersion GetIsG3XDSAV(byte[] data)
{ {
if (!new[] { SIZE_G3XD, SIZE_G3XDGCI }.Contains(data.Length)) if (!new[] { SIZE_G3XD, SIZE_G3XDGCI }.Contains(data.Length))
return GameVersion.Invalid; return GameVersion.Invalid;
// Check the intro bytes for each save slot // Check the intro bytes for each save slot
byte[] slotintroXD = { 0x01, 0x01, 0x01, 0x00 };
int offset = data.Length - SIZE_G3XD; int offset = data.Length - SIZE_G3XD;
// For XD savegames inside a memory card only the first sequence is equal to slotintroXD
bool valid = false;
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
var ident = data.Skip(0x6000 + offset + 0x28000 * i).Take(4); var ofs = 0x6000 + offset + 0x28000 * i;
if (ident.SequenceEqual(slotintroXD)) if ((BitConverter.ToUInt32(data, ofs) & 0xFFFE_FFFF) != 0x00000101)
valid = true; return GameVersion.Invalid;
} }
return valid ? GameVersion.XD : GameVersion.Invalid; return GameVersion.XD;
} }
/// <summary>Determines the type of 4th gen save</summary> /// <summary>Determines the type of 4th gen save</summary>
/// <param name="data">Save data of which to determine the type</param> /// <param name="data">Save data of which to determine the type</param>
@ -690,46 +654,39 @@ namespace PKHeX.Core
uint val = 0; uint val = 0;
for (int i = 0; i < data.Length; i += 4) for (int i = 0; i < data.Length; i += 4)
val += BitConverter.ToUInt32(data, i); val += BitConverter.ToUInt32(data, i);
return (ushort)((val & 0xFFFF) + (val >> 16)); return (ushort)(val + (val >> 16));
} }
private static void CheckHeaderFooter(ref byte[] input, ref byte[] header, ref byte[] footer) private static void CheckHeaderFooter(ref byte[] input, ref byte[] header, ref byte[] footer)
{ {
if (input.Length > SIZE_G4RAW) // DeSmuME Gen4/5 DSV if (input.Length > SIZE_G4RAW) // DeSmuME Gen4/5 DSV
{ {
bool dsv = FOOTER_DSV.SequenceEqual(input.Skip(input.Length - FOOTER_DSV.Length)); if (!FOOTER_DSV.SequenceEqual(input.Skip(input.Length - FOOTER_DSV.Length)))
if (dsv) return;
{ footer = input.Skip(SIZE_G4RAW).ToArray();
footer = input.Skip(SIZE_G4RAW).ToArray(); input = input.Take(SIZE_G4RAW).ToArray();
input = input.Take(SIZE_G4RAW).ToArray();
}
} }
if (input.Length == SIZE_G3BOXGCI) else if (input.Length == SIZE_G3BOXGCI)
{ {
string game = Encoding.ASCII.GetString(input, 0, 4); if (!IsGameMatchHeader(HEADER_RSBOX, input))
if (HEADER_RSBOX.Any(id => id == game)) // gci return; // not gci
{ header = input.Take(SIZE_G3BOXGCI - SIZE_G3BOX).ToArray();
header = input.Take(SIZE_G3BOXGCI - SIZE_G3BOX).ToArray(); input = input.Skip(header.Length).ToArray();
input = input.Skip(header.Length).ToArray();
}
} }
if (input.Length == SIZE_G3COLOGCI) else if (input.Length == SIZE_G3COLOGCI)
{ {
string game = Encoding.ASCII.GetString(input, 0, 4); if (!IsGameMatchHeader(HEADER_COLO, input))
if (HEADER_COLO.Any(id => id == game)) // gci return; // not gci
{ header = input.Take(SIZE_G3COLOGCI - SIZE_G3COLO).ToArray();
header = input.Take(SIZE_G3COLOGCI - SIZE_G3COLO).ToArray(); input = input.Skip(header.Length).ToArray();
input = input.Skip(header.Length).ToArray();
}
} }
if (input.Length == SIZE_G3XDGCI) else if (input.Length == SIZE_G3XDGCI)
{ {
string game = Encoding.ASCII.GetString(input, 0, 4); if (!IsGameMatchHeader(HEADER_XD, input))
if (HEADER_XD.Any(id => id == game)) // gci return; // not gci
{ header = input.Take(SIZE_G3XDGCI - SIZE_G3XD).ToArray();
header = input.Take(SIZE_G3XDGCI - SIZE_G3XD).ToArray(); input = input.Skip(header.Length).ToArray();
input = input.Skip(header.Length).ToArray();
}
} }
bool IsGameMatchHeader(IEnumerable<string> headers, byte[] data) => headers.Contains(Encoding.ASCII.GetString(data, 0, 4));
} }
public static int GetDexFormIndexBW(int species, int formct) public static int GetDexFormIndexBW(int species, int formct)

View file

@ -171,7 +171,7 @@ namespace PKHeX.WinForms
try try
{ {
// Not using a string builder because something's very wrong, and we don't want to make things worse // Not using a string builder because something's very wrong, and we don't want to make things worse
var message = (originalException?.ToString() ?? "null first exception") + "\n\n" + errorHandlingException.ToString(); var message = (originalException?.ToString() ?? "null first exception") + "\n\n" + errorHandlingException;
File.WriteAllText("PKHeX_Error_Report " + DateTime.Now.ToString("YYYYMMDDhhmmss") + ".txt", message); File.WriteAllText("PKHeX_Error_Report " + DateTime.Now.ToString("YYYYMMDDhhmmss") + ".txt", message);
} }
catch (Exception) catch (Exception)