diff --git a/PKHeX.Core/Saves/SAV1Stadium.cs b/PKHeX.Core/Saves/SAV1Stadium.cs
index 806069aa1..c18575ba5 100644
--- a/PKHeX.Core/Saves/SAV1Stadium.cs
+++ b/PKHeX.Core/Saves/SAV1Stadium.cs
@@ -3,16 +3,13 @@
namespace PKHeX.Core
{
- public sealed class SAV1Stadium : SaveFile, ILangDeviantSave
+ ///
+ /// Pokémon Stadium (Pokémon Stadium 2 in Japan)
+ ///
+ public sealed class SAV1Stadium : SAV_STADIUM
{
- protected override string BAKText => $"{OT} ({Version})";
- public override string Filter => "SAV File|*.sav|All Files|*.*";
- public override string Extension => ".sav";
-
- public int SaveRevision => Japanese ? 0 : 1;
- public string SaveRevisionString => Japanese ? "J" : "U";
- public bool Japanese { get; }
- public bool Korean => false;
+ public override int SaveRevision => Japanese ? 0 : 1;
+ public override string SaveRevisionString => Japanese ? "J" : "U";
public override PersonalTable Personal => PersonalTable.Y;
public override int MaxEV => ushort.MaxValue;
@@ -21,18 +18,7 @@ public sealed class SAV1Stadium : SaveFile, ILangDeviantSave
public override SaveFile Clone() => new SAV1Stadium((byte[])Data.Clone(), Japanese);
- public override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
public override int Generation => 1;
-
- public override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese);
-
- public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
- {
- if (PadToSize == 0)
- PadToSize = maxLength + 1;
- return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, PadWith);
- }
-
private int StringLength => Japanese ? StringLengthJ : StringLengthU;
private const int StringLengthJ = 6;
private const int StringLengthU = 11;
@@ -45,53 +31,64 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0,
public override int MaxSpeciesID => Legal.MaxSpeciesID_1;
public override int MaxAbilityID => Legal.MaxAbilityID_1;
public override int MaxItemID => Legal.MaxItemID_1;
- public override int MaxBallID => 0; // unused
- public override int MaxGameID => 99; // unused
- public override int MaxMoney => 999999;
- public override int MaxCoins => 9999;
- public override int GetPartyOffset(int slot) => -1;
+ public override Type PKMType => typeof(PK1);
+ public override PKM BlankPKM => new PK1(Japanese);
+ private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLengthJ); // 0x2D
+ private const int SIZE_PK1U = PokeCrypto.SIZE_1STORED + (2 * StringLengthU); // 0x37
+ protected override int SIZE_STORED => Japanese ? SIZE_PK1J : SIZE_PK1U;
+ protected override int SIZE_PARTY => Japanese ? SIZE_PK1J : SIZE_PK1U;
- private readonly bool IsPairSwapped;
+ private int ListHeaderSize => Japanese ? 0x0C : 0x10;
+ private const int ListFooterSize = 6; // POKE + 2byte checksum
- protected override byte[] GetFinalData()
+ private const int TeamCountU = 10;
+ private const int TeamCountJ = 12;
+ private const int TeamCountTypeU = 9; // team-types 1 & 2 are unused
+ private const int TeamCountTypeJ = 9;
+ protected override int TeamCount => Japanese ? TeamCountJ * TeamCountTypeJ : TeamCountU * TeamCountTypeU;
+ private const int TeamSizeJ = 0x0C + (SIZE_PK1J * 6) + ListFooterSize; // 0x120
+ private const int TeamSizeU = 0x10 + (SIZE_PK1U * 6) + ListFooterSize; // 0x160
+
+ private const uint FOOTER_MAGIC = 0x454B4F50; // POKE
+
+ private int BoxSize => Japanese ? BoxSizeJ : BoxSizeU;
+ //private int ListHeaderSizeBox => Japanese ? 0x0C : 0x10;
+ private const int BoxSizeJ = 0x0C + (SIZE_PK1J * 30) + ListFooterSize; // 0x558
+ private const int BoxSizeU = 0x10 + (SIZE_PK1U * 20) + 6 + ListFooterSize; // 0x468 (6 bytes alignment)
+ public override int GetBoxOffset(int box) => Box + ListHeaderSize + (box * BoxSize);
+
+ public SAV1Stadium(byte[] data) : this(data, IsStadiumJ(data)) { }
+
+ public SAV1Stadium(byte[] data, bool japanese) : base(data, japanese, StadiumUtil.IsMagicPresentSwap(data, japanese ? TeamSizeJ : TeamSizeU, FOOTER_MAGIC))
{
- var result = base.GetFinalData();
- if (IsPairSwapped)
- BigEndian.SwapBytes32(result = (byte[])result.Clone());
- return result;
+ Box = 0xC000;
}
- public override bool ChecksumsValid => GetBoxChecksumsValid();
- protected override void SetChecksums() => SetBoxChecksums();
-
- private bool GetBoxChecksumsValid()
+ public SAV1Stadium(bool japanese = false) : base(japanese, SaveUtil.SIZE_G1STAD)
{
- for (int i = 0; i < BoxCount; i++)
- {
- var boxOfs = GetBoxOffset(i) - ListHeaderSize;
- var size = BoxSize - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- var actual = BigEndian.ToUInt16(Data, boxOfs + size);
- if (chk != actual)
- return false;
- }
- return true;
+ Box = 0xC000;
+ ClearBoxes();
}
- private void SetBoxChecksums()
+ protected override bool GetIsBoxChecksumValid(int i)
{
- for (int i = 0; i < BoxCount; i++)
- {
- SetBoxMetadata(i);
- var boxOfs = GetBoxOffset(i) - ListHeaderSize;
- var size = BoxSize - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
- }
+ var boxOfs = GetBoxOffset(i) - ListHeaderSize;
+ var size = BoxSize - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ var actual = BigEndian.ToUInt16(Data, boxOfs + size);
+ return chk == actual;
}
- private void SetBoxMetadata(int i)
+ protected override void SetBoxChecksum(int i)
+ {
+ var boxOfs = GetBoxOffset(i) - ListHeaderSize;
+ var size = BoxSize - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
+ }
+
+ protected override void SetBoxMetadata(int i)
{
var bdata = GetBoxOffset(i);
@@ -108,58 +105,15 @@ private void SetBoxMetadata(int i)
Data[bdata - 1] = (byte)count;
}
- public override Type PKMType => typeof(PK1);
-
protected override PKM GetPKM(byte[] data)
{
int len = StringLength;
var nick = data.Slice(PokeCrypto.SIZE_1STORED, len);
var ot = data.Slice(PokeCrypto.SIZE_1STORED + len, len);
data = data.Slice(0, PokeCrypto.SIZE_1STORED);
- return new PK1(data, Japanese) {OT_Trash = ot, Nickname_Trash = nick};
+ return new PK1(data, Japanese) { OT_Trash = ot, Nickname_Trash = nick };
}
- protected override byte[] DecryptPKM(byte[] data) => data;
-
- public override PKM BlankPKM => new PK1(Japanese);
- private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLengthJ); // 0x2D
- private const int SIZE_PK1U = PokeCrypto.SIZE_1STORED + (2 * StringLengthU); // 0x37
- protected override int SIZE_STORED => Japanese ? SIZE_PK1J : SIZE_PK1U;
- protected override int SIZE_PARTY => Japanese ? SIZE_PK1J : SIZE_PK1U;
-
- public SAV1Stadium(byte[] data) : this(data, IsStadiumJ(data)) { }
-
- public SAV1Stadium(byte[] data, bool japanese) : base(data)
- {
- Japanese = japanese;
- var swap = StadiumUtil.IsMagicPresentSwap(data, TeamSize, MAGIC_POKE);
- if (swap)
- {
- BigEndian.SwapBytes32(Data);
- IsPairSwapped = true;
- }
- Box = 0xC000;
- }
-
- public SAV1Stadium(bool japanese = false) : base(SaveUtil.SIZE_G1STAD)
- {
- Japanese = japanese;
- Box = 0xC000;
- ClearBoxes();
- }
-
- private int ListHeaderSize => Japanese ? 0x0C : 0x10;
- private const int ListFooterSize = 6; // POKE + 2byte checksum
-
- private const int TeamCountU = 10;
- private const int TeamCountJ = 12;
- private const int TeamCountTypeU = 9; // team-types 1 & 2 are unused
- private const int TeamCountTypeJ = 9;
- private int TeamCount => Japanese ? TeamCountJ * TeamCountTypeJ : TeamCountU * TeamCountTypeU;
- private int TeamSize => Japanese ? TeamSizeJ : TeamSizeU;
- private const int TeamSizeJ = 0x0C + (SIZE_PK1J * 6) + ListFooterSize; // 0x120
- private const int TeamSizeU = 0x10 + (SIZE_PK1U * 6) + ListFooterSize; // 0x160
-
public int GetTeamOffset(int team) => Japanese ? GetTeamOffsetJ(team) : GetTeamOffsetU(team);
private int GetTeamOffsetJ(int team)
@@ -239,11 +193,9 @@ private string GetTeamTypeName(int type)
};
}
- public SlotGroup[] GetRegisteredTeams()
+ public override SlotGroup[] GetRegisteredTeams()
{
- var result = new SlotGroup[TeamCount];
- for (int i = 0; i < result.Length; i++)
- result[i] = GetTeam(i);
+ var result = base.GetRegisteredTeams();
if (Japanese)
return result;
@@ -254,7 +206,7 @@ public SlotGroup[] GetRegisteredTeams()
return noUnused;
}
- public SlotGroup GetTeam(int team)
+ public override SlotGroup GetTeam(int team)
{
if ((uint)team >= TeamCount)
throw new ArgumentOutOfRangeException(nameof(team));
@@ -270,14 +222,6 @@ public SlotGroup GetTeam(int team)
return new SlotGroup(name, members);
}
- private int BoxSize => Japanese ? BoxSizeJ : BoxSizeU;
- private int ListHeaderSizeBox => Japanese ? 0x0C : 0x10;
- private const int BoxSizeJ = 0x0C + (SIZE_PK1J * 30) + ListFooterSize; // 0x558
- private const int BoxSizeU = 0x10 + (SIZE_PK1U * 20) + 6 + ListFooterSize; // 0x468 (6 bytes alignment)
- public override int GetBoxOffset(int box) => Box + ListHeaderSize + (box * BoxSize);
- public override string GetBoxName(int box) => $"Box {box + 1}";
- public override void SetBoxName(int box, string value) { }
-
public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset)
{
// pkm that have never been boxed have yet to save the 'current level' for box indication
@@ -294,21 +238,19 @@ public override void WriteBoxSlot(PKM pkm, byte[] data, int offset)
base.WriteBoxSlot(pkm, Data, offset);
}
- private const uint MAGIC_POKE = 0x454B4F50;
-
- public static bool IsStadiumU(byte[] data)
+ public static bool IsStadium(byte[] data)
{
if (data.Length != SaveUtil.SIZE_G1STAD)
return false;
- return StadiumUtil.IsMagicPresentEither(data, TeamSizeU, MAGIC_POKE);
+ if (IsStadiumU(data))
+ return true;
+ if (IsStadiumJ(data))
+ return true;
+ return false;
}
- public static bool IsStadiumJ(byte[] data)
- {
- if (data.Length != SaveUtil.SIZE_G1STAD)
- return false;
- return StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, MAGIC_POKE);
- }
+ private static bool IsStadiumU(byte[] data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeU, FOOTER_MAGIC);
+ private static bool IsStadiumJ(byte[] data) => StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, FOOTER_MAGIC);
}
public enum Stadium1TeamType
diff --git a/PKHeX.Core/Saves/SAV1StadiumJ.cs b/PKHeX.Core/Saves/SAV1StadiumJ.cs
index e60c8285e..6eed45edf 100644
--- a/PKHeX.Core/Saves/SAV1StadiumJ.cs
+++ b/PKHeX.Core/Saves/SAV1StadiumJ.cs
@@ -6,17 +6,11 @@ namespace PKHeX.Core
///
/// Pocket Monsters Stadium
///
- public class SAV1StadiumJ : SaveFile, ILangDeviantSave
+ public sealed class SAV1StadiumJ : SAV_STADIUM
{
- protected override string BAKText => $"{OT} ({Version})";
- public override string Filter => "SAV File|*.sav|All Files|*.*";
- public override string Extension => ".sav";
-
// Required since PK1 logic comparing a save file assumes the save file can be U/J
- public int SaveRevision => 0;
- public string SaveRevisionString => "0"; // so we're different from Japanese SAV1Stadium naming...
- public bool Japanese => true;
- public bool Korean => false;
+ public override int SaveRevision => 0;
+ public override string SaveRevisionString => "0"; // so we're different from Japanese SAV1Stadium naming...
public override PersonalTable Personal => PersonalTable.Y;
public override int MaxEV => ushort.MaxValue;
@@ -25,18 +19,7 @@ public class SAV1StadiumJ : SaveFile, ILangDeviantSave
public override SaveFile Clone() => new SAV1StadiumJ((byte[])Data.Clone());
- public override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
public override int Generation => 1;
-
- public override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, true);
-
- public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
- {
- if (PadToSize == 0)
- PadToSize = maxLength + 1;
- return StringConverter12.SetString1(value, maxLength, true, PadToSize, PadWith);
- }
-
private const int StringLength = 6; // Japanese Only
public override int OTLength => StringLength;
public override int NickLength => StringLength;
@@ -47,52 +30,53 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0,
public override int MaxSpeciesID => Legal.MaxSpeciesID_1;
public override int MaxAbilityID => Legal.MaxAbilityID_1;
public override int MaxItemID => Legal.MaxItemID_1;
- public override int MaxBallID => 0; // unused
- public override int MaxGameID => 99; // unused
- public override int MaxMoney => 999999;
- public override int MaxCoins => 9999;
-
- public override int GetPartyOffset(int slot) => -1;
-
- private readonly bool IsPairSwapped;
-
- protected override byte[] GetFinalData()
- {
- var result = base.GetFinalData();
- if (IsPairSwapped)
- BigEndian.SwapBytes32(result = (byte[])result.Clone());
- return result;
- }
-
- public override bool ChecksumsValid => GetBoxChecksumsValid();
- protected override void SetChecksums() => SetBoxChecksums();
-
- private bool GetBoxChecksumsValid()
- {
- for (int i = 0; i < BoxCount; i++)
- {
- var boxOfs = GetBoxOffset(i) - ListHeaderSize;
- const int size = BoxSizeJ - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- var actual = BigEndian.ToUInt16(Data, boxOfs + size);
- if (chk != actual)
- return false;
- }
- return true;
- }
-
- private void SetBoxChecksums()
- {
- for (int i = 0; i < BoxCount; i++)
- {
- var boxOfs = GetBoxOffset(i) - ListHeaderSize;
- const int size = BoxSizeJ - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
- }
- }
+ private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLength); // 0x2D
+ protected override int SIZE_STORED => SIZE_PK1J;
+ protected override int SIZE_PARTY => SIZE_PK1J;
public override Type PKMType => typeof(PK1);
+ public override PKM BlankPKM => new PK1(true);
+
+ private const int ListHeaderSize = 0x14;
+ private const int ListFooterSize = 6; // POKE + 2byte checksum
+ private const uint FOOTER_MAGIC = 0x454B4F50; // POKE
+
+ protected override int TeamCount => 16; // 32 teams stored sequentially; latter 16 are backups
+ private const int TeamSizeJ = 0x14 + (SIZE_PK1J * 6) + ListFooterSize; // 0x128
+ private const int BoxSizeJ = 0x560;
+
+ public SAV1StadiumJ(byte[] data) : base(data, true, StadiumUtil.IsMagicPresentSwap(data, TeamSizeJ, FOOTER_MAGIC))
+ {
+ Box = 0x2500;
+ }
+
+ public SAV1StadiumJ() : base(true, SaveUtil.SIZE_G1STAD)
+ {
+ Box = 0x2500;
+ ClearBoxes();
+ }
+
+ protected override bool GetIsBoxChecksumValid(int i)
+ {
+ var boxOfs = GetBoxOffset(i) - ListHeaderSize;
+ const int size = BoxSizeJ - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ var actual = BigEndian.ToUInt16(Data, boxOfs + size);
+ return chk == actual;
+ }
+
+ protected override void SetBoxChecksum(int i)
+ {
+ var boxOfs = GetBoxOffset(i) - ListHeaderSize;
+ const int size = BoxSizeJ - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
+ }
+
+ protected override void SetBoxMetadata(int i)
+ {
+ // Not implemented
+ }
protected override PKM GetPKM(byte[] data)
{
@@ -103,47 +87,11 @@ protected override PKM GetPKM(byte[] data)
return new PK1(data, true) { OT_Trash = ot, Nickname_Trash = nick };
}
- protected override byte[] DecryptPKM(byte[] data) => data;
-
- public override PKM BlankPKM => new PK1(true);
- private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLength); // 0x2D
- protected override int SIZE_STORED => SIZE_PK1J;
- protected override int SIZE_PARTY => SIZE_PK1J;
-
- public SAV1StadiumJ(byte[] data) : base(data)
- {
- var swap = StadiumUtil.IsMagicPresentSwap(data, TeamSizeJ, MAGIC_POKE);
- if (swap)
- {
- BigEndian.SwapBytes32(Data);
- IsPairSwapped = true;
- }
- Box = 0x2500;
- }
-
- public SAV1StadiumJ() : base(SaveUtil.SIZE_G1STAD)
- {
- Box = 0x2500;
- ClearBoxes();
- }
-
- private const int ListHeaderSize = 0x14;
- private const int ListFooterSize = 6; // POKE + 2byte checksum
-
- private const int TeamCount = 16; // 32 teams stored sequentially; latter 16 are backups
- private const int TeamSizeJ = 0x14 + (SIZE_PK1J * 6) + ListFooterSize; // 0x128
+ public override int GetBoxOffset(int box) => Box + ListHeaderSize + (box * BoxSizeJ);
public static int GetTeamOffset(int team) => 0 + ListHeaderSize + (team * TeamSizeJ);
public static string GetTeamName(int team) => $"Team {team + 1}";
- public SlotGroup[] GetRegisteredTeams()
- {
- var result = new SlotGroup[TeamCount];
- for (int i = 0; i < result.Length; i++)
- result[i] = GetTeam(i);
- return result;
- }
-
- public SlotGroup GetTeam(int team)
+ public override SlotGroup GetTeam(int team)
{
if ((uint)team >= TeamCount)
throw new ArgumentOutOfRangeException(nameof(team));
@@ -159,11 +107,6 @@ public SlotGroup GetTeam(int team)
return new SlotGroup(name, members);
}
- private const int BoxSizeJ = 0x560;
- public override int GetBoxOffset(int box) => Box + ListHeaderSize + (box * BoxSizeJ);
- public override string GetBoxName(int box) => $"Box {box + 1}";
- public override void SetBoxName(int box, string value) { }
-
public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset)
{
// pkm that have never been boxed have yet to save the 'current level' for box indication
@@ -180,13 +123,11 @@ public override void WriteBoxSlot(PKM pkm, byte[] data, int offset)
base.WriteBoxSlot(pkm, Data, offset);
}
- private const uint MAGIC_POKE = 0x454B4F50;
-
- public static bool IsStadiumJ(byte[] data)
+ public static bool IsStadium(byte[] data)
{
if (data.Length != SaveUtil.SIZE_G1STADJ)
return false;
- return StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, MAGIC_POKE);
+ return StadiumUtil.IsMagicPresentEither(data, TeamSizeJ, FOOTER_MAGIC);
}
}
}
diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs
index c84c91f16..cd5ed380b 100644
--- a/PKHeX.Core/Saves/SAV2Stadium.cs
+++ b/PKHeX.Core/Saves/SAV2Stadium.cs
@@ -3,16 +3,13 @@
namespace PKHeX.Core
{
- public sealed class SAV2Stadium : SaveFile, ILangDeviantSave
+ ///
+ /// Pokémon Stadium 2 (Pokémon Stadium GS in Japan)
+ ///
+ public sealed class SAV2Stadium : SAV_STADIUM
{
- protected override string BAKText => $"{OT} ({Version})";
- public override string Filter => "SAV File|*.sav|All Files|*.*";
- public override string Extension => ".sav";
-
- public int SaveRevision => Japanese ? 0 : 1;
- public string SaveRevisionString => Japanese ? "J" : "U";
- public bool Japanese { get; }
- public bool Korean => false;
+ public override int SaveRevision => Japanese ? 0 : 1;
+ public override string SaveRevisionString => Japanese ? "J" : "U";
public override PersonalTable Personal => PersonalTable.C;
public override int MaxEV => ushort.MaxValue;
@@ -21,18 +18,7 @@ public sealed class SAV2Stadium : SaveFile, ILangDeviantSave
public override SaveFile Clone() => new SAV1Stadium((byte[])Data.Clone(), Japanese);
- public override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
public override int Generation => 2;
-
- public override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese);
-
- public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
- {
- if (PadToSize == 0)
- PadToSize = maxLength + 1;
- return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, PadWith);
- }
-
private const int StringLength = 12;
public override int OTLength => StringLength;
public override int NickLength => StringLength;
@@ -43,53 +29,56 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0,
public override int MaxSpeciesID => Legal.MaxSpeciesID_2;
public override int MaxAbilityID => Legal.MaxAbilityID_2;
public override int MaxItemID => Legal.MaxItemID_2;
- public override int MaxBallID => 0; // unused
- public override int MaxGameID => 99; // unused
- public override int MaxMoney => 999999;
- public override int MaxCoins => 9999;
- public override int GetPartyOffset(int slot) => -1;
+ public override Type PKMType => typeof(SK2);
+ public override PKM BlankPKM => new SK2(Japanese);
+ protected override PKM GetPKM(byte[] data) => new SK2(data, Japanese);
- private readonly bool IsPairSwapped;
+ private const int SIZE_SK2 = PokeCrypto.SIZE_2STADIUM; // 60
+ protected override int SIZE_STORED => SIZE_SK2;
+ protected override int SIZE_PARTY => SIZE_SK2;
- protected override byte[] GetFinalData()
+ private const int ListHeaderSizeTeam = 0x10;
+ private const int ListHeaderSizeBox = 0x20;
+ private const int ListFooterSize = 6; // POKE + 2byte checksum
+
+ protected override int TeamCount => 60;
+ private const int TeamCountType = 10;
+ private const int TeamSize = ListHeaderSizeTeam + (SIZE_SK2 * 6) + 2 + ListFooterSize; // 0x180
+
+ private int BoxSize => Japanese ? BoxSizeJ : BoxSizeU;
+ private const int BoxSizeJ = ListHeaderSizeBox + (SIZE_SK2 * 30) + 2 + ListFooterSize; // 0x730
+ private const int BoxSizeU = ListHeaderSizeBox + (SIZE_SK2 * 20) + 2 + ListFooterSize; // 0x4D8
+
+ // Box 1 is stored separately from the remainder of the boxes.
+ private const int BoxStart = 0x5E00; // Box 1
+ private const int BoxContinue = 0x8000; // Box 2+
+
+ private const uint MAGIC_FOOTER = 0x30763350; // P3v0
+
+ public SAV2Stadium(byte[] data) : this(data, IsStadiumJ(data)) { }
+
+ public SAV2Stadium(byte[] data, bool japanese) : base(data, japanese, StadiumUtil.IsMagicPresentSwap(data, TeamSize, MAGIC_FOOTER))
{
- var result = base.GetFinalData();
- if (IsPairSwapped)
- BigEndian.SwapBytes32(result = (byte[])result.Clone());
- return result;
+ Box = BoxStart;
}
- public override bool ChecksumsValid => GetBoxChecksumsValid();
- protected override void SetChecksums() => SetBoxChecksums();
-
- private bool GetBoxChecksumsValid()
+ public SAV2Stadium(bool japanese = false) : base(japanese, SaveUtil.SIZE_G1STAD)
{
- for (int i = 0; i < BoxCount; i++)
- {
- var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
- var size = BoxSize - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- var actual = BigEndian.ToUInt16(Data, boxOfs + size);
- if (chk != actual)
- return false;
- }
- return true;
+ Box = BoxStart;
+ ClearBoxes();
}
- private void SetBoxChecksums()
+ protected override bool GetIsBoxChecksumValid(int i)
{
- for (int i = 0; i < BoxCount; i++)
- {
- SetBoxMetadata(i);
- var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
- var size = BoxSize - 2;
- var chk = Checksums.CheckSum16(Data, boxOfs, size);
- BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
- }
+ var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
+ var size = BoxSize - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ var actual = BigEndian.ToUInt16(Data, boxOfs + size);
+ return chk == actual;
}
- private void SetBoxMetadata(int i)
+ protected override void SetBoxMetadata(int i)
{
var bdata = GetBoxOffset(i);
@@ -106,54 +95,24 @@ private void SetBoxMetadata(int i)
if (Data[boxOfs] == 0)
{
Data[boxOfs] = 1;
- Data[boxOfs + 1] = (byte) count;
+ Data[boxOfs + 1] = (byte)count;
Data[boxOfs + 4] = StringConverter12.G1TerminatorCode;
StringConverter12.SetString1("1234", 4, Japanese).CopyTo(Data, boxOfs + 0x10);
}
else
{
- Data[boxOfs + 1] = (byte) count;
+ Data[boxOfs + 1] = (byte)count;
}
}
- public override Type PKMType => typeof(SK2);
- protected override PKM GetPKM(byte[] data) => new SK2(data, Japanese);
- protected override byte[] DecryptPKM(byte[] data) => data;
-
- public override PKM BlankPKM => new SK2(Japanese);
- private const int SIZE_SK2 = PokeCrypto.SIZE_2STADIUM; // 60
- protected override int SIZE_STORED => SIZE_SK2;
- protected override int SIZE_PARTY => SIZE_SK2;
-
- public SAV2Stadium(byte[] data) : this(data, IsJapanese(data)) { }
-
- public SAV2Stadium(byte[] data, bool japanese) : base(data)
+ protected override void SetBoxChecksum(int i)
{
- var swap = StadiumUtil.IsMagicPresentSwap(data, TeamSize, MAGIC_POKE);
- if (swap)
- {
- BigEndian.SwapBytes32(Data);
- IsPairSwapped = true;
- }
- Japanese = japanese;
- Box = BoxStart;
+ var boxOfs = GetBoxOffset(i) - ListHeaderSizeBox;
+ var size = BoxSize - 2;
+ var chk = Checksums.CheckSum16(Data, boxOfs, size);
+ BigEndian.GetBytes(chk).CopyTo(Data, boxOfs + size);
}
- public SAV2Stadium(bool japanese = false) : base(SaveUtil.SIZE_G1STAD)
- {
- Japanese = japanese;
- Box = BoxStart;
- ClearBoxes();
- }
-
- private const int ListHeaderSizeTeam = 0x10;
- private const int ListHeaderSizeBox = 0x20;
- private const int ListFooterSize = 6; // POKE + 2byte checksum
-
- private const int TeamCount = 60;
- private const int TeamCountType = 10;
- private const int TeamSize = ListHeaderSizeTeam + (SIZE_SK2 * 6) + 2 + ListFooterSize; // 0x180
-
public static int GetTeamOffset(Stadium2TeamType type, int team)
{
if ((uint)team >= TeamCountType)
@@ -173,15 +132,7 @@ public static int GetTeamOffset(int team)
public static string GetTeamName(int team) => $"{((Stadium2TeamType)(team / TeamCountType)).ToString().Replace('_', ' ')} {(team % 10) + 1}";
- public SlotGroup[] GetRegisteredTeams()
- {
- var result = new SlotGroup[TeamCount];
- for (int i = 0; i < result.Length; i++)
- result[i] = GetTeam(i);
- return result;
- }
-
- public SlotGroup GetTeam(int team)
+ public override SlotGroup GetTeam(int team)
{
if ((uint)team >= TeamCount)
throw new ArgumentOutOfRangeException(nameof(team));
@@ -197,14 +148,6 @@ public SlotGroup GetTeam(int team)
return new SlotGroup(name, members);
}
- private int BoxSize => Japanese ? BoxSizeJ : BoxSizeU;
- private const int BoxSizeJ = ListHeaderSizeBox + (SIZE_SK2 * 30) + 2 + ListFooterSize; // 0x730
- private const int BoxSizeU = ListHeaderSizeBox + (SIZE_SK2 * 20) + 2 + ListFooterSize; // 0x4D8
-
- // Box 1 is stored separately from the remainder of the boxes.
- private const int BoxStart = 0x5E00; // Box 1
- private const int BoxContinue = 0x8000; // Box 2+
-
public override int GetBoxOffset(int box)
{
if (box == 0)
@@ -212,20 +155,15 @@ public override int GetBoxOffset(int box)
return BoxContinue + ListHeaderSizeBox + ((box - 1) * BoxSize);
}
- public override string GetBoxName(int box) => $"Box {box + 1}";
- public override void SetBoxName(int box, string value) { }
-
- private const uint MAGIC_POKE = 0x30763350; // P3v0
-
public static bool IsStadium(byte[] data)
{
if (data.Length != SaveUtil.SIZE_G1STAD)
return false;
- return StadiumUtil.IsMagicPresentEither(data, TeamSize, MAGIC_POKE);
+ return StadiumUtil.IsMagicPresentEither(data, TeamSize, MAGIC_FOOTER);
}
// Check Box 1's footer magic.
- private static bool IsJapanese(byte[] data) => StadiumUtil.IsMagicPresentAbsolute(data, BoxStart + BoxSizeJ - ListFooterSize, MAGIC_POKE);
+ private static bool IsStadiumJ(byte[] data) => StadiumUtil.IsMagicPresentAbsolute(data, BoxStart + BoxSizeJ - ListFooterSize, MAGIC_FOOTER);
}
public enum Stadium2TeamType
diff --git a/PKHeX.Core/Saves/SAV_STADIUM.cs b/PKHeX.Core/Saves/SAV_STADIUM.cs
new file mode 100644
index 000000000..17d0f5ff2
--- /dev/null
+++ b/PKHeX.Core/Saves/SAV_STADIUM.cs
@@ -0,0 +1,102 @@
+namespace PKHeX.Core
+{
+ ///
+ /// Base class for GB Era Stadium files.
+ ///
+ public abstract class SAV_STADIUM : SaveFile, ILangDeviantSave
+ {
+ protected sealed override string BAKText => $"{OT} ({Version})";
+ public sealed override string Filter => "SAV File|*.sav|All Files|*.*";
+ public sealed override string Extension => ".sav";
+
+ public abstract int SaveRevision { get; }
+ public abstract string SaveRevisionString { get; }
+ public bool Japanese { get; }
+ public bool Korean => false;
+
+ public sealed override int MaxBallID => 0; // unused
+ public sealed override int MaxGameID => 99; // unused
+ public sealed override int MaxMoney => 999999;
+ public sealed override int MaxCoins => 9999;
+
+ private readonly bool IsPairSwapped;
+
+ protected abstract int TeamCount { get; }
+ public sealed override string OT { get; set; }
+ public sealed override int Language => Japanese ? 1 : 2;
+
+ protected SAV_STADIUM(byte[] data, bool japanese, bool swap) : base(data)
+ {
+ Japanese = japanese;
+ OT = SaveUtil.GetSafeTrainerName(this, (LanguageID)Language);
+
+ if (!swap)
+ return;
+ BigEndian.SwapBytes32(Data);
+ IsPairSwapped = true;
+ }
+
+ protected SAV_STADIUM(bool japanese, int size) : base(size)
+ {
+ Japanese = japanese;
+ OT = SaveUtil.GetSafeTrainerName(this, (LanguageID)Language);
+ }
+
+ protected sealed override byte[] DecryptPKM(byte[] data) => data;
+ public sealed override int GetPartyOffset(int slot) => -1;
+ public sealed override string GetBoxName(int box) => $"Box {box + 1}";
+ public sealed override void SetBoxName(int box, string value) { }
+ public sealed override bool ChecksumsValid => GetBoxChecksumsValid();
+ public sealed override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
+ protected abstract void SetBoxChecksum(int i);
+ protected abstract bool GetIsBoxChecksumValid(int i);
+ protected sealed override void SetChecksums() => SetBoxChecksums();
+ protected abstract void SetBoxMetadata(int i);
+
+ protected void SetBoxChecksums()
+ {
+ for (int i = 0; i < BoxCount; i++)
+ {
+ SetBoxMetadata(i);
+ SetBoxChecksum(i);
+ }
+ }
+
+ private bool GetBoxChecksumsValid()
+ {
+ for (int i = 0; i < BoxCount; i++)
+ {
+ if (!GetIsBoxChecksumValid(i))
+ return false;
+ }
+ return true;
+ }
+
+ protected sealed override byte[] GetFinalData()
+ {
+ var result = base.GetFinalData();
+ if (IsPairSwapped)
+ BigEndian.SwapBytes32(result = (byte[])result.Clone());
+ return result;
+ }
+
+ public abstract SlotGroup GetTeam(int team);
+
+ public virtual SlotGroup[] GetRegisteredTeams()
+ {
+ var result = new SlotGroup[TeamCount];
+ for (int i = 0; i < result.Length; i++)
+ result[i] = GetTeam(i);
+ return result;
+ }
+
+ public sealed override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese);
+
+ public sealed override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
+ {
+ if (PadToSize == 0)
+ PadToSize = maxLength + 1;
+ return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, PadWith);
+ }
+ }
+}
diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs
index 401007f8d..02b658051 100644
--- a/PKHeX.Core/Saves/Util/SaveUtil.cs
+++ b/PKHeX.Core/Saves/Util/SaveUtil.cs
@@ -125,9 +125,9 @@ private static GameVersion GetSAVType(byte[] data)
return DPPt;
if (SAV2Stadium.IsStadium(data))
return Stadium2;
- if (SAV1Stadium.IsStadiumU(data) || SAV1Stadium.IsStadiumJ(data))
+ if (SAV1Stadium.IsStadium(data))
return Stadium;
- if (SAV1StadiumJ.IsStadiumJ(data))
+ if (SAV1StadiumJ.IsStadium(data))
return StadiumJ;
if ((ver = GetIsG8SAV(data)) != Invalid)