Extract PokemonList* to inherited class

Logic is essentially identical; implement a base class and have the
generation specific structures implment the differences.

Reduce the verbosity a little
This commit is contained in:
Kurt 2018-08-05 17:28:54 -07:00
parent 84185dc6c9
commit fc73eee570
9 changed files with 289 additions and 362 deletions

View file

@ -11,8 +11,6 @@ namespace PKHeX.Core
internal byte[] nick; internal byte[] nick;
public override PersonalInfo PersonalInfo => PersonalTable.Y[Species]; public override PersonalInfo PersonalInfo => PersonalTable.Y[Species];
public byte[] OT_Name_Raw => (byte[])otname.Clone();
public byte[] Nickname_Raw => (byte[])nick.Clone();
public override bool Valid => Species <= 151 && (Data[0] == 0 || Species != 0); public override bool Valid => Species <= 151 && (Data[0] == 0 || Species != 0);
public override int SIZE_PARTY => PKX.SIZE_1PARTY; public override int SIZE_PARTY => PKX.SIZE_1PARTY;
@ -97,7 +95,7 @@ namespace PKHeX.Core
} }
} }
protected override byte[] Encrypt() => new PokemonList1(this).GetBytes(); protected override byte[] Encrypt() => new PokeList1(this).Write();
public override byte[] EncryptedPartyData => Encrypt(); public override byte[] EncryptedPartyData => Encrypt();
public override byte[] EncryptedBoxData => Encrypt(); public override byte[] EncryptedBoxData => Encrypt();
public override byte[] DecryptedBoxData => Encrypt(); public override byte[] DecryptedBoxData => Encrypt();
@ -434,121 +432,4 @@ namespace PKHeX.Core
return pk7; return pk7;
} }
} }
public class PokemonList1
{
private const int CAPACITY_DAYCARE = 1;
private const int CAPACITY_PARTY = 6;
private const int CAPACITY_STORED = 20;
private const int CAPACITY_STORED_JP = 30;
private readonly bool Japanese;
private int StringLength => Japanese ? PK1.STRLEN_J : PK1.STRLEN_U;
public enum CapacityType
{
Daycare = CAPACITY_DAYCARE,
Party = CAPACITY_PARTY,
Stored = CAPACITY_STORED,
StoredJP = CAPACITY_STORED_JP,
Single
}
private static int GetEntrySize(CapacityType c) => c == CapacityType.Single || c == CapacityType.Party
? PKX.SIZE_1PARTY
: PKX.SIZE_1STORED;
private static byte GetCapacity(CapacityType c) => c == CapacityType.Single ? (byte)1 : (byte)c;
private static byte[] GetEmptyList(CapacityType c, bool is_JP = false)
{
int cap = GetCapacity(c);
return new[] { (byte)0 }.Concat(Enumerable.Repeat((byte)0xFF, cap + 1)).Concat(Enumerable.Repeat((byte)0, GetEntrySize(c) * cap)).Concat(Enumerable.Repeat((byte)0x50, (is_JP ? PK1.STRLEN_J : PK1.STRLEN_U) * 2 * cap)).ToArray();
}
public PokemonList1(byte[] d, CapacityType c = CapacityType.Single, bool jp = false)
{
Japanese = jp;
Data = d ?? GetEmptyList(c, Japanese);
Capacity = GetCapacity(c);
Entry_Size = GetEntrySize(c);
if (Data.Length != DataSize)
Array.Resize(ref Data, DataSize);
Pokemon = new PK1[Capacity];
for (int i = 0; i < Capacity; i++)
{
int base_ofs = 2 + Capacity;
byte[] dat = new byte[Entry_Size];
byte[] otname = new byte[StringLength];
byte[] nick = new byte[StringLength];
Buffer.BlockCopy(Data, base_ofs + Entry_Size * i, dat, 0, Entry_Size);
Buffer.BlockCopy(Data, base_ofs + Capacity * Entry_Size + StringLength * i, otname, 0, StringLength);
Buffer.BlockCopy(Data, base_ofs + Capacity * Entry_Size + StringLength * (i + Capacity), nick, 0, StringLength);
Pokemon[i] = new PK1(dat, null, jp) {otname = otname, nick = nick};
}
}
public PokemonList1(CapacityType c = CapacityType.Single, bool jp = false)
: this(null, c, jp) => Count = 1;
public PokemonList1(PK1 pk)
: this(CapacityType.Single, pk.Japanese)
{
this[0] = pk;
Count = 1;
}
private readonly byte[] Data;
private readonly byte Capacity;
private readonly int Entry_Size;
public byte Count
{
get => Data[0];
set => Data[0] = value > Capacity ? Capacity : value;
}
public readonly PK1[] Pokemon;
public PK1 this[int i]
{
get
{
if (i > Capacity || i < 0) throw new ArgumentOutOfRangeException($"Invalid PokemonList Access: {i}");
return Pokemon[i];
}
set
{
if (value == null) return;
Pokemon[i] = (PK1)value.Clone();
}
}
private void Update()
{
int count = Array.FindIndex(Pokemon, pk => pk.Species == 0);
Count = count < 0 ? Capacity : (byte)count;
for (int i = 0; i < Count; i++)
{
int base_ofs = 2 + Capacity;
Data[1 + i] = (byte)SpeciesConverter.SetG1Species(Pokemon[i].Species);
Array.Copy(Pokemon[i].Data, 0, Data, base_ofs + Entry_Size * i, Entry_Size);
Array.Copy(Pokemon[i].OT_Name_Raw, 0, Data, base_ofs + Capacity * Entry_Size + StringLength * i, StringLength);
Array.Copy(Pokemon[i].Nickname_Raw, 0, Data, base_ofs + Capacity * Entry_Size + StringLength * (i + Capacity), StringLength);
}
Data[1 + Count] = byte.MaxValue;
}
public byte[] GetBytes()
{
Update();
return Data;
}
private int DataSize => Capacity * (Entry_Size + 1 + 2 * StringLength) + 2;
public static int GetDataLength(CapacityType c, bool jp = false) => GetCapacity(c) * (GetEntrySize(c) + 1 + 2 * (jp ? PK1.STRLEN_J : PK1.STRLEN_U)) + 2;
}
} }

View file

@ -11,8 +11,6 @@ namespace PKHeX.Core
internal byte[] nick; internal byte[] nick;
public override PersonalInfo PersonalInfo => PersonalTable.C[Species]; public override PersonalInfo PersonalInfo => PersonalTable.C[Species];
public byte[] OT_Name_Raw => (byte[])otname.Clone();
public byte[] Nickname_Raw => (byte[])nick.Clone();
public override bool Valid => Species <= 252; public override bool Valid => Species <= 252;
public override int SIZE_PARTY => PKX.SIZE_2PARTY; public override int SIZE_PARTY => PKX.SIZE_2PARTY;
@ -119,7 +117,7 @@ namespace PKHeX.Core
} }
} }
protected override byte[] Encrypt() => new PokemonList2(this).GetBytes(); protected override byte[] Encrypt() => new PokeList2(this).Write();
public override byte[] EncryptedPartyData => Encrypt(); public override byte[] EncryptedPartyData => Encrypt();
public override byte[] EncryptedBoxData => Encrypt(); public override byte[] EncryptedBoxData => Encrypt();
public override byte[] DecryptedBoxData => Encrypt(); public override byte[] DecryptedBoxData => Encrypt();
@ -361,7 +359,7 @@ namespace PKHeX.Core
public PK1 ConvertToPK1() public PK1 ConvertToPK1()
{ {
PK1 pk1 = new PK1(null, Identifier, Japanese) {TradebackStatus = TradebackType.WasTradeback}; PK1 pk1 = new PK1(null, Identifier) {TradebackStatus = TradebackType.WasTradeback};
Array.Copy(Data, 0x1, pk1.Data, 0x7, 0x1A); Array.Copy(Data, 0x1, pk1.Data, 0x7, 0x1A);
pk1.Species = Species; // This will take care of Typing :) pk1.Species = Species; // This will take care of Typing :)
@ -377,8 +375,8 @@ namespace PKHeX.Core
pk1.Stat_Level = Stat_Level; pk1.Stat_Level = Stat_Level;
} }
// Status = 0 // Status = 0
Array.Copy(otname, 0, pk1.otname, 0, otname.Length); pk1.otname = (byte[])otname.Clone();
Array.Copy(nick, 0, pk1.nick, 0, nick.Length); pk1.nick = (byte[])nick.Clone();
pk1.ClearInvalidMoves(); pk1.ClearInvalidMoves();
@ -479,122 +477,4 @@ namespace PKHeX.Core
return pk7; return pk7;
} }
} }
public class PokemonList2
{
private const int CAPACITY_DAYCARE = 1;
private const int CAPACITY_PARTY = 6;
private const int CAPACITY_STORED = 20;
private const int CAPACITY_STORED_JP = 30;
private readonly bool Japanese;
private int StringLength => Japanese ? PK2.STRLEN_J : PK2.STRLEN_U;
public enum CapacityType
{
Daycare = CAPACITY_DAYCARE,
Party = CAPACITY_PARTY,
Stored = CAPACITY_STORED,
StoredJP = CAPACITY_STORED_JP,
Single
}
private static int GetEntrySize(CapacityType c) => c == CapacityType.Single || c == CapacityType.Party
? PKX.SIZE_2PARTY
: PKX.SIZE_2STORED;
private static byte GetCapacity(CapacityType c) => c == CapacityType.Single ? (byte)1 : (byte)c;
private static byte[] GetEmptyList(CapacityType c, bool is_JP = false)
{
int cap = GetCapacity(c);
return new[] { (byte)0 }.Concat(Enumerable.Repeat((byte)0xFF, cap + 1)).Concat(Enumerable.Repeat((byte)0, GetEntrySize(c) * cap)).Concat(Enumerable.Repeat((byte)0x50, (is_JP ? PK2.STRLEN_J : PK2.STRLEN_U) * 2 * cap)).ToArray();
}
public PokemonList2(byte[] d, CapacityType c = CapacityType.Single, bool jp = false)
{
Japanese = jp;
Data = d ?? GetEmptyList(c, Japanese);
Capacity = GetCapacity(c);
Entry_Size = GetEntrySize(c);
if (Data.Length != DataSize)
{
Array.Resize(ref Data, DataSize);
}
Pokemon = new PK2[Capacity];
for (int i = 0; i < Capacity; i++)
{
int base_ofs = 2 + Capacity;
byte[] dat = new byte[Entry_Size];
byte[] otname = new byte[StringLength];
byte[] nick = new byte[StringLength];
Buffer.BlockCopy(Data, base_ofs + Entry_Size * i, dat, 0, Entry_Size);
Buffer.BlockCopy(Data, base_ofs + Capacity * Entry_Size + StringLength * i, otname, 0, StringLength);
Buffer.BlockCopy(Data, base_ofs + Capacity * Entry_Size + StringLength * (i + Capacity), nick, 0, StringLength);
Pokemon[i] = new PK2(dat, null, jp) {IsEgg = Data[1 + i] == 0xFD, otname = otname, nick = nick};
}
}
public PokemonList2(CapacityType c = CapacityType.Single, bool jp = false)
: this(null, c, jp) => Count = 1;
public PokemonList2(PK2 pk)
: this(CapacityType.Single, pk.Japanese)
{
this[0] = pk;
Count = 1;
}
private readonly byte[] Data;
private readonly byte Capacity;
private readonly int Entry_Size;
public byte Count
{
get => Data[0];
set => Data[0] = value > Capacity ? Capacity : value;
}
public readonly PK2[] Pokemon;
public PK2 this[int i]
{
get
{
if (i > Capacity || i < 0) throw new ArgumentOutOfRangeException($"Invalid PokemonList Access: {i}");
return Pokemon[i];
}
set
{
if (value == null) return;
Pokemon[i] = (PK2)value.Clone();
}
}
private void Update()
{
int count = Array.FindIndex(Pokemon, pk => pk.Species == 0);
Count = count < 0 ? Capacity : (byte)count;
for (int i = 0; i < Count; i++)
{
Data[1 + i] = Pokemon[i].IsEgg ? (byte)0xFD : (byte)Pokemon[i].Species;
Array.Copy(Pokemon[i].Data, 0, Data, 2 + Capacity + Entry_Size * i, Entry_Size);
Array.Copy(Pokemon[i].OT_Name_Raw, 0, Data, 2 + Capacity + Capacity * Entry_Size + StringLength * i, StringLength);
Array.Copy(Pokemon[i].Nickname_Raw, 0, Data, 2 + Capacity + Capacity * Entry_Size + StringLength * Capacity + StringLength * i, StringLength);
}
Data[1 + Count] = byte.MaxValue;
}
public byte[] GetBytes()
{
Update();
return Data;
}
private int DataSize => Capacity * (Entry_Size + 1 + 2 * StringLength) + 2;
public static int GetDataLength(CapacityType c, bool jp = false) => GetCapacity(c) * (GetEntrySize(c) + 1 + 2 * (jp ? PK2.STRLEN_J : PK2.STRLEN_U)) + 2;
}
} }

View file

@ -0,0 +1,17 @@
namespace PKHeX.Core
{
public sealed class PokeList1 : PokeListGB<PK1>
{
protected override byte GetSpeciesBoxIdentifier(PK1 pk) => (byte)SpeciesConverter.SetG1Species(pk.Species);
protected override PK1 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) => new PK1(dat) { otname = otname, nick = nick };
protected override int GetEntrySize() => GetEntrySize(IsFormatParty);
public PokeList1(byte[] d, PokeListType c = PokeListType.Single, bool jp = false) : base(d, c, jp) { }
public PokeList1(PokeListType c = PokeListType.Single, bool jp = false) : base(c, jp) { }
public PokeList1(byte[] d) : base(d, PokeListType.Single, d.Length == PKX.SIZE_1JLIST) { }
public PokeList1(PK1 pk) : base(pk) { }
private static int GetEntrySize(bool party) => party ? PKX.SIZE_1PARTY : PKX.SIZE_1STORED;
public static int GetDataLength(PokeListType c, bool jp) => GetDataSize(c, jp, GetEntrySize(IsCapacityPartyFormat(c)));
}
}

View file

@ -0,0 +1,17 @@
namespace PKHeX.Core
{
public sealed class PokeList2 : PokeListGB<PK2>
{
protected override byte GetSpeciesBoxIdentifier(PK2 pk) => pk.IsEgg ? (byte)0xFD : (byte)pk.Species;
protected override PK2 GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg) => new PK2(dat) { IsEgg = egg, otname = otname, nick = nick };
protected override int GetEntrySize() => GetEntrySize(IsFormatParty);
public PokeList2(byte[] d, PokeListType c = PokeListType.Single, bool jp = false) : base(d, c, jp) { }
public PokeList2(PokeListType c = PokeListType.Single, bool jp = false) : base (c, jp) { }
public PokeList2(byte[] d) : base(d, PokeListType.Single, d.Length == PKX.SIZE_2JLIST) { }
public PokeList2(PK2 pk) : base(pk) { }
private static int GetEntrySize(bool party) => party ? PKX.SIZE_2PARTY : PKX.SIZE_2STORED;
public static int GetDataLength(PokeListType c, bool jp) => GetDataSize(c, jp, GetEntrySize(IsCapacityPartyFormat(c)));
}
}

View file

@ -0,0 +1,129 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
public abstract class PokeListGB<T> where T : PKM
{
private readonly int StringLength;
private readonly byte[] Data;
private readonly byte Capacity;
private readonly int Entry_Size;
public readonly T[] Pokemon;
public byte Count { get => Data[0]; private set => Data[0] = value > Capacity ? Capacity : value; }
protected PokeListGB(byte[] d, PokeListType c = PokeListType.Single, bool jp = false)
{
Data = d ?? GetEmptyList(c, jp);
StringLength = GetStringLength(jp);
Capacity = (byte)c;
Entry_Size = GetEntrySize();
var dataSize = 2 + (Capacity * (Entry_Size + 1 + (2 * StringLength)));
if (Data.Length != dataSize)
Array.Resize(ref Data, dataSize);
Pokemon = Read();
}
protected PokeListGB(PokeListType c = PokeListType.Single, bool jp = false)
: this(null, c, jp) => Count = 1;
protected PokeListGB(T pk)
: this(PokeListType.Single, pk.Japanese)
{
this[0] = pk;
Count = 1;
}
private byte[] GetEmptyList(PokeListType c, bool jp = false)
{
int capacity = (byte)c;
var intro = Enumerable.Repeat((byte) 0xFF, capacity + 1);
var pkm = Enumerable.Repeat((byte) 0, GetEntrySize() * capacity);
var strings = Enumerable.Repeat((byte) 0x50, GetStringLength(jp) * 2 * capacity);
return new[] { (byte)0 }.Concat(intro).Concat(pkm).Concat(strings).ToArray();
}
private int GetOffsetPKMData(int base_ofs, int i) => base_ofs + (Entry_Size * i);
private int GetOffsetPKMOT(int base_ofs, int i) => GetOffsetPKMData(base_ofs, Capacity) + (StringLength * i);
private int GetOffsetPKMNickname(int base_ofs, int i) => GetOffsetPKMOT(base_ofs, Capacity) + (StringLength * i);
private static int GetStringLength(bool jp) => jp ? PK1.STRLEN_J : PK1.STRLEN_U;
protected bool IsFormatParty => IsCapacityPartyFormat((PokeListType)Capacity);
protected static bool IsCapacityPartyFormat(PokeListType Capacity) => Capacity == PokeListType.Single || Capacity == PokeListType.Party;
protected static int GetDataSize(PokeListType c, bool jp, int entrySize)
{
var entryLength = 1 + entrySize + (2 * GetStringLength(jp));
return 2 + ((byte)c * entryLength);
}
protected abstract int GetEntrySize();
protected abstract byte GetSpeciesBoxIdentifier(T pk);
protected abstract T GetEntry(byte[] dat, byte[] otname, byte[] nick, bool egg);
public T this[int i]
{
get
{
if (i > Capacity || i < 0) throw new ArgumentOutOfRangeException($"Invalid {nameof(PokeListGB<T>)} Access: {i}");
return Pokemon[i];
}
set
{
if (value == null) return;
Pokemon[i] = (T)value.Clone();
}
}
private T[] Read()
{
var arr = new T[Capacity];
int base_ofs = 2 + Capacity;
for (int i = 0; i < Capacity; i++)
arr[i] = GetEntry(base_ofs, i);
return arr;
}
public byte[] Write()
{
int count = Array.FindIndex(Pokemon, pk => pk.Species == 0);
Count = count < 0 ? Capacity : (byte)count;
int base_ofs = 2 + Capacity;
for (int i = 0; i < Count; i++)
{
Data[1 + i] = GetSpeciesBoxIdentifier(Pokemon[i]);
SetEntry(base_ofs, i);
}
Data[1 + Count] = byte.MaxValue;
return Data;
}
private T GetEntry(int base_ofs, int i)
{
byte[] dat = new byte[Entry_Size];
byte[] otname = new byte[StringLength];
byte[] nick = new byte[StringLength];
int pkOfs = GetOffsetPKMData(base_ofs, i);
int otOfs = GetOffsetPKMOT(base_ofs, i);
int nkOfs = GetOffsetPKMNickname(base_ofs, i);
Buffer.BlockCopy(Data, pkOfs, dat, 0, Entry_Size);
Buffer.BlockCopy(Data, otOfs, otname, 0, StringLength);
Buffer.BlockCopy(Data, nkOfs, nick, 0, StringLength);
return GetEntry(dat, otname, nick, Data[1 + i] == 0xFD);
}
private void SetEntry(int base_ofs, int i)
{
int pkOfs = GetOffsetPKMData(base_ofs, i);
int otOfs = GetOffsetPKMOT(base_ofs, i);
int nkOfs = GetOffsetPKMNickname(base_ofs, i);
Array.Copy(Pokemon[i].Data, 0, Data, pkOfs, Entry_Size);
Array.Copy(Pokemon[i].OT_Trash, 0, Data, otOfs, StringLength);
Array.Copy(Pokemon[i].Nickname_Trash, 0, Data, nkOfs, StringLength);
}
}
}

View file

@ -0,0 +1,10 @@
namespace PKHeX.Core
{
public enum PokeListType : byte
{
Single = 1,
Party = 6,
Stored = 20,
StoredJP = 30,
}
}

View file

@ -86,12 +86,12 @@ namespace PKHeX.Core
switch (format) switch (format)
{ {
case 1: case 1:
var PL1 = new PokemonList1(data, PokemonList1.CapacityType.Single, data.Length == PKX.SIZE_1JLIST); var PL1 = new PokeList1(data);
if (ident != null) if (ident != null)
PL1[0].Identifier = ident; PL1[0].Identifier = ident;
return PL1[0]; return PL1[0];
case 2: case 2:
var PL2 = new PokemonList2(data, PokemonList2.CapacityType.Single, data.Length == PKX.SIZE_2JLIST); var PL2 = new PokeList2(data);
if (ident != null) if (ident != null)
PL2[0].Identifier = ident; PL2[0].Identifier = ident;
return PL2[0]; return PL2[0];

View file

@ -41,71 +41,56 @@ namespace PKHeX.Core
Personal = PersonalTable.Y; Personal = PersonalTable.Y;
// Stash boxes after the save file's end. // Stash boxes after the save file's end.
byte[] TempBox = new byte[SIZE_STOREDBOX]; int stored = SIZE_STOREDBOX;
int baseDest = Data.Length - SIZE_RESERVED;
var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;
for (int i = 0; i < BoxCount; i++) for (int i = 0; i < BoxCount; i++)
{ {
if (i < BoxCount / 2) int ofs = GetBoxRawDataOffset(i);
Array.Copy(Data, 0x4000 + i * TempBox.Length, TempBox, 0, TempBox.Length); var box = GetData(ofs, stored);
else var boxDest = baseDest + (i * SIZE_BOX);
Array.Copy(Data, 0x6000 + (i - BoxCount / 2) * TempBox.Length, TempBox, 0, TempBox.Length); var boxPL = new PokeList1(box, capacity, Japanese);
PokemonList1 PL1 = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese); for (int j = 0; j < boxPL.Pokemon.Length; j++)
for (int j = 0; j < PL1.Pokemon.Length; j++)
{ {
if (j < PL1.Count) var dest = boxDest + (j * SIZE_STORED);
{ var pkDat = (j < boxPL.Count)
byte[] pkDat = new PokemonList1(PL1[j]).GetBytes(); ? new PokeList1(boxPL[j]).Write()
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED); : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
}
} }
} }
Array.Copy(Data, Japanese ? 0x302D : 0x30C0, TempBox, 0, TempBox.Length); var current = GetData(Japanese ? 0x302D : 0x30C0, SIZE_STOREDBOX);
PokemonList1 curBoxPL = new PokemonList1(TempBox, Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese); var curBoxPL = new PokeList1(current, capacity, Japanese);
for (int i = 0; i < curBoxPL.Pokemon.Length; i++) for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
{ {
if (i < curBoxPL.Count) var dest = Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED;
{ var pkDat = i < curBoxPL.Count
byte[] pkDat = new PokemonList1(curBoxPL[i]).GetBytes(); ? new PokeList1(curBoxPL[i]).Write()
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED); : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
}
} }
byte[] TempParty = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Party, Japanese)]; var party = GetData(Japanese ? 0x2ED5 : 0x2F2C, SIZE_STOREDPARTY);
Array.Copy(Data, Japanese ? 0x2ED5 : 0x2F2C, TempParty, 0, TempParty.Length); var partyPL = new PokeList1(party, PokeListType.Party, Japanese);
PokemonList1 partyList = new PokemonList1(TempParty, PokemonList1.CapacityType.Party, Japanese); for (int i = 0; i < partyPL.Pokemon.Length; i++)
for (int i = 0; i < partyList.Pokemon.Length; i++)
{ {
if (i < partyList.Count) var dest = GetPartyOffset(i);
{ var pkDat = i < partyPL.Count
byte[] pkDat = new PokemonList1(partyList[i]).GetBytes(); ? new PokeList1(partyPL[i]).Write()
pkDat.CopyTo(Data, GetPartyOffset(i)); : new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, GetPartyOffset(i));
}
} }
byte[] rawDC = new byte[0x38]; byte[] rawDC = new byte[0x38];
Array.Copy(Data, Japanese ? 0x2CA7 : 0x2CF4, rawDC, 0, rawDC.Length); Array.Copy(Data, Japanese ? 0x2CA7 : 0x2CF4, rawDC, 0, rawDC.Length);
byte[] TempDaycare = new byte[PokemonList1.GetDataLength(PokemonList1.CapacityType.Single, Japanese)]; byte[] TempDaycare = new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
TempDaycare[0] = rawDC[0]; TempDaycare[0] = rawDC[0];
Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY + StringLength, StringLength); Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY + StringLength, StringLength);
Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY, StringLength); Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PKX.SIZE_1PARTY, StringLength);
Array.Copy(rawDC, 1 + 2 * StringLength, TempDaycare, 2 + 1, PKX.SIZE_1STORED); Array.Copy(rawDC, 1 + 2 * StringLength, TempDaycare, 2 + 1, PKX.SIZE_1STORED);
PokemonList1 daycareList = new PokemonList1(TempDaycare, PokemonList1.CapacityType.Single, Japanese); PokeList1 daycareList = new PokeList1(TempDaycare, PokeListType.Single, Japanese);
daycareList.GetBytes().CopyTo(Data, GetPartyOffset(7)); daycareList.Write().CopyTo(Data, GetPartyOffset(7));
Daycare = GetPartyOffset(7); Daycare = GetPartyOffset(7);
EventFlag = Japanese ? 0x29E9 : 0x29F3; EventFlag = Japanese ? 0x29E9 : 0x29F3;
@ -127,25 +112,29 @@ namespace PKHeX.Core
protected override byte[] Write(bool DSV) protected override byte[] Write(bool DSV)
{ {
var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;
for (int i = 0; i < BoxCount; i++) for (int i = 0; i < BoxCount; i++)
{ {
PokemonList1 boxPL = new PokemonList1(Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese); var boxPL = new PokeList1(capacity, Japanese);
int slot = 0; int slot = 0;
for (int j = 0; j < boxPL.Pokemon.Length; j++) for (int j = 0; j < boxPL.Pokemon.Length; j++)
{ {
PK1 boxPK = (PK1) GetPKM(GetData(GetBoxOffset(i) + j*SIZE_STORED, SIZE_STORED)); PK1 boxPK = (PK1)GetPKM(GetData(GetBoxOffset(i) + (j * SIZE_STORED), SIZE_STORED));
if (boxPK.Species > 0) if (boxPK.Species > 0)
boxPL[slot++] = boxPK; boxPL[slot++] = boxPK;
} }
if (i < BoxCount / 2)
boxPL.GetBytes().CopyTo(Data, 0x4000 + i * SIZE_STOREDBOX); // copy to box location
else var boxdata = boxPL.Write();
boxPL.GetBytes().CopyTo(Data, 0x6000 + (i - BoxCount / 2) * SIZE_STOREDBOX); int ofs = GetBoxRawDataOffset(i);
SetData(boxdata, ofs);
// copy to active loc if current box
if (i == CurrentBox) if (i == CurrentBox)
boxPL.GetBytes().CopyTo(Data, Japanese ? 0x302D : 0x30C0); SetData(boxdata, Japanese ? 0x302D : 0x30C0);
} }
PokemonList1 partyPL = new PokemonList1(PokemonList1.CapacityType.Party, Japanese); var partyPL = new PokeList1(PokeListType.Party, Japanese);
int pSlot = 0; int pSlot = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
@ -153,7 +142,7 @@ namespace PKHeX.Core
if (partyPK.Species > 0) if (partyPK.Species > 0)
partyPL[pSlot++] = partyPK; partyPL[pSlot++] = partyPK;
} }
partyPL.GetBytes().CopyTo(Data, Japanese ? 0x2ED5 : 0x2F2C); partyPL.Write().CopyTo(Data, Japanese ? 0x2ED5 : 0x2F2C);
// Daycare is read-only, but in case it ever becomes editable, copy it back in. // Daycare is read-only, but in case it ever becomes editable, copy it back in.
byte[] rawDC = GetData(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED); byte[] rawDC = GetData(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED);
@ -170,13 +159,21 @@ namespace PKHeX.Core
return outData; return outData;
} }
private int GetBoxRawDataOffset(int i)
{
if (i < BoxCount / 2)
return 0x4000 + (i * SIZE_STOREDBOX);
return 0x6000 + ((i - (BoxCount / 2)) * SIZE_STOREDBOX);
}
// Configuration // Configuration
public override SaveFile Clone() { return new SAV1(Write(DSV: false)); } public override SaveFile Clone() { return new SAV1(Write(DSV: false)); }
public override int SIZE_STORED => Japanese ? PKX.SIZE_1JLIST : PKX.SIZE_1ULIST; public override int SIZE_STORED => Japanese ? PKX.SIZE_1JLIST : PKX.SIZE_1ULIST;
protected override int SIZE_PARTY => Japanese ? PKX.SIZE_1JLIST : PKX.SIZE_1ULIST; protected override int SIZE_PARTY => Japanese ? PKX.SIZE_1JLIST : PKX.SIZE_1ULIST;
private int SIZE_BOX => BoxSlotCount*SIZE_STORED; private int SIZE_BOX => BoxSlotCount*SIZE_STORED;
private int SIZE_STOREDBOX => PokemonList1.GetDataLength(Japanese ? PokemonList1.CapacityType.StoredJP : PokemonList1.CapacityType.Stored, Japanese); private int SIZE_STOREDBOX => PokeList1.GetDataLength(Japanese ? PokeListType.StoredJP : PokeListType.Stored, Japanese);
private int SIZE_STOREDPARTY => PokeList1.GetDataLength(PokeListType.Party, Japanese);
public override PKM BlankPKM => new PK1(null, null, Japanese); public override PKM BlankPKM => new PK1(null, null, Japanese);
public override Type PKMType => typeof(PK1); public override Type PKMType => typeof(PK1);
@ -441,7 +438,7 @@ namespace PKHeX.Core
public override PKM GetPKM(byte[] data) public override PKM GetPKM(byte[] data)
{ {
if (data.Length == SIZE_STORED) if (data.Length == SIZE_STORED)
return new PokemonList1(data, PokemonList1.CapacityType.Single, Japanese)[0]; return new PokeList1(data, PokeListType.Single, Japanese)[0];
return new PK1(data); return new PK1(data);
} }
public override byte[] DecryptPKM(byte[] data) public override byte[] DecryptPKM(byte[] data)

View file

@ -54,60 +54,47 @@ namespace PKHeX.Core
HeldItems = Legal.HeldItems_GSC; HeldItems = Legal.HeldItems_GSC;
// Stash boxes after the save file's end. // Stash boxes after the save file's end.
byte[] TempBox = new byte[SIZE_STOREDBOX]; int splitAtIndex = (Japanese ? 6 : 7);
int stored = SIZE_STOREDBOX;
int baseDest = Data.Length - SIZE_RESERVED;
var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;
for (int i = 0; i < BoxCount; i++) for (int i = 0; i < BoxCount; i++)
{ {
if (i < (Japanese ? 6 : 7)) int ofs = GetBoxRawDataOffset(i, splitAtIndex);
Array.Copy(Data, 0x4000 + i * (TempBox.Length + 2), TempBox, 0, TempBox.Length); var box = GetData(ofs, stored);
else var boxDest = baseDest + (i * SIZE_BOX);
Array.Copy(Data, 0x6000 + (i - (Japanese ? 6 : 7)) * (TempBox.Length + 2), TempBox, 0, TempBox.Length); var boxPL = new PokeList2(box, capacity, Japanese);
PokemonList2 PL2 = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese); for (int j = 0; j < boxPL.Pokemon.Length; j++)
for (int j = 0; j < PL2.Pokemon.Length; j++)
{ {
if (j < PL2.Count) var dest = boxDest + (j * SIZE_STORED);
{ var pkDat = (j < boxPL.Count)
byte[] pkDat = new PokemonList2(PL2[j]).GetBytes(); ? new PokeList2(boxPL[j]).Write()
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED); : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + i * SIZE_BOX + j * SIZE_STORED);
}
} }
} }
Array.Copy(Data, Offsets.CurrentBox, TempBox, 0, TempBox.Length); var current = GetData(Offsets.CurrentBox, stored);
PokemonList2 curBoxPL = new PokemonList2(TempBox, Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese); var curBoxPL = new PokeList2(current, capacity, Japanese);
var curDest = baseDest + (CurrentBox * SIZE_BOX);
for (int i = 0; i < curBoxPL.Pokemon.Length; i++) for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
{ {
if (i < curBoxPL.Count) var dest = curDest + (i * SIZE_STORED);
{ var pkDat = i < curBoxPL.Count
byte[] pkDat = new PokemonList2(curBoxPL[i]).GetBytes(); ? new PokeList2(curBoxPL[i]).Write()
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED); : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, Data.Length - SIZE_RESERVED + CurrentBox * SIZE_BOX + i * SIZE_STORED);
}
} }
byte[] TempParty = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Party, Japanese)]; var party = GetData(Offsets.Party, SIZE_STOREDPARTY);
Array.Copy(Data, Offsets.Party, TempParty, 0, TempParty.Length); var partyPL = new PokeList2(party, PokeListType.Party, Japanese);
PokemonList2 partyList = new PokemonList2(TempParty, PokemonList2.CapacityType.Party, Japanese); for (int i = 0; i < partyPL.Pokemon.Length; i++)
for (int i = 0; i < partyList.Pokemon.Length; i++)
{ {
if (i < partyList.Count) var dest = GetPartyOffset(i);
{ var pkDat = i < partyPL.Count
byte[] pkDat = new PokemonList2(partyList[i]).GetBytes(); ? new PokeList2(partyPL[i]).Write()
pkDat.CopyTo(Data, GetPartyOffset(i)); : new byte[PokeList2.GetDataLength(PokeListType.Single, Japanese)];
} pkDat.CopyTo(Data, dest);
else
{
byte[] pkDat = new byte[PokemonList2.GetDataLength(PokemonList2.CapacityType.Single, Japanese)];
pkDat.CopyTo(Data, GetPartyOffset(i));
}
} }
// Daycare currently undocumented for all Gen II games. // Daycare currently undocumented for all Gen II games.
@ -117,21 +104,21 @@ namespace PKHeX.Core
DaycareFlags[0] = Data[offset]; offset++; DaycareFlags[0] = Data[offset]; offset++;
var pk1 = ReadPKMFromOffset(offset); // parent 1 var pk1 = ReadPKMFromOffset(offset); // parent 1
var daycare1 = new PokemonList2(pk1); var daycare1 = new PokeList2(pk1);
offset += StringLength * 2 + 0x20; // nick/ot/pkm offset += StringLength * 2 + 0x20; // nick/ot/pkm
DaycareFlags[1] = Data[offset]; offset++; DaycareFlags[1] = Data[offset]; offset++;
byte steps = Data[offset]; offset++; byte steps = Data[offset]; offset++;
byte BreedMotherOrNonDitto = Data[offset]; offset++; byte BreedMotherOrNonDitto = Data[offset]; offset++;
var pk2 = ReadPKMFromOffset(offset); // parent 2 var pk2 = ReadPKMFromOffset(offset); // parent 2
var daycare2 = new PokemonList2(pk2); var daycare2 = new PokeList2(pk2);
offset += StringLength * 2 + PKX.SIZE_2STORED; // nick/ot/pkm offset += StringLength * 2 + PKX.SIZE_2STORED; // nick/ot/pkm
var pk3 = ReadPKMFromOffset(offset); // egg! var pk3 = ReadPKMFromOffset(offset); // egg!
pk3.IsEgg = true; pk3.IsEgg = true;
var daycare3 = new PokemonList2(pk3); var daycare3 = new PokeList2(pk3);
daycare1.GetBytes().CopyTo(Data, GetPartyOffset(7 + (0 * 2))); daycare1.Write().CopyTo(Data, GetPartyOffset(7 + (0 * 2)));
daycare2.GetBytes().CopyTo(Data, GetPartyOffset(7 + (1 * 2))); daycare2.Write().CopyTo(Data, GetPartyOffset(7 + (1 * 2)));
daycare3.GetBytes().CopyTo(Data, GetPartyOffset(7 + (2 * 2))); daycare3.Write().CopyTo(Data, GetPartyOffset(7 + (2 * 2)));
Daycare = Offsets.Daycare; Daycare = Offsets.Daycare;
} }
@ -160,11 +147,20 @@ namespace PKHeX.Core
public bool Korean { get; } public bool Korean { get; }
private readonly SAV2Offsets Offsets; private readonly SAV2Offsets Offsets;
private int GetBoxRawDataOffset(int i, int splitAtIndex)
{
if (i < splitAtIndex)
return 0x4000 + (i * (SIZE_STOREDBOX + 2));
return 0x6000 + ((i - splitAtIndex) * (SIZE_STOREDBOX + 2));
}
protected override byte[] Write(bool DSV) protected override byte[] Write(bool DSV)
{ {
int len = SIZE_STOREDBOX;
int splitAtIndex = (Japanese ? 6 : 7);
for (int i = 0; i < BoxCount; i++) for (int i = 0; i < BoxCount; i++)
{ {
PokemonList2 boxPL = new PokemonList2(Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese); var boxPL = new PokeList2(Japanese ? PokeListType.StoredJP : PokeListType.Stored, Japanese);
int slot = 0; int slot = 0;
for (int j = 0; j < boxPL.Pokemon.Length; j++) for (int j = 0; j < boxPL.Pokemon.Length; j++)
{ {
@ -172,15 +168,14 @@ namespace PKHeX.Core
if (boxPK.Species > 0) if (boxPK.Species > 0)
boxPL[slot++] = boxPK; boxPL[slot++] = boxPK;
} }
if (i < (Japanese ? 6 : 7))
boxPL.GetBytes().CopyTo(Data, 0x4000 + i * (SIZE_STOREDBOX + 2)); int src = GetBoxRawDataOffset(i, splitAtIndex);
else boxPL.Write().CopyTo(Data, src);
boxPL.GetBytes().CopyTo(Data, 0x6000 + (i - (Japanese ? 6 : 7)) * (SIZE_STOREDBOX + 2));
if (i == CurrentBox) if (i == CurrentBox)
boxPL.GetBytes().CopyTo(Data, Offsets.CurrentBox); boxPL.Write().CopyTo(Data, Offsets.CurrentBox);
} }
PokemonList2 partyPL = new PokemonList2(PokemonList2.CapacityType.Party, Japanese); var partyPL = new PokeList2(PokeListType.Party, Japanese);
int pSlot = 0; int pSlot = 0;
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
@ -188,7 +183,7 @@ namespace PKHeX.Core
if (partyPK.Species > 0) if (partyPK.Species > 0)
partyPL[pSlot++] = partyPK; partyPL[pSlot++] = partyPK;
} }
partyPL.GetBytes().CopyTo(Data, Offsets.Party); partyPL.Write().CopyTo(Data, Offsets.Party);
SetChecksums(); SetChecksums();
if (Japanese) if (Japanese)
@ -246,7 +241,8 @@ namespace PKHeX.Core
public override Type PKMType => typeof(PK2); public override Type PKMType => typeof(PK2);
private int SIZE_BOX => BoxSlotCount*SIZE_STORED; private int SIZE_BOX => BoxSlotCount*SIZE_STORED;
private int SIZE_STOREDBOX => PokemonList2.GetDataLength(Japanese ? PokemonList2.CapacityType.StoredJP : PokemonList2.CapacityType.Stored, Japanese); private int SIZE_STOREDBOX => PokeList2.GetDataLength(Japanese ? PokeListType.StoredJP : PokeListType.Stored, Japanese);
private int SIZE_STOREDPARTY => PokeList2.GetDataLength(PokeListType.Party, Japanese);
public override int MaxMoveID => Legal.MaxMoveID_2; public override int MaxMoveID => Legal.MaxMoveID_2;
public override int MaxSpeciesID => Legal.MaxSpeciesID_2; public override int MaxSpeciesID => Legal.MaxSpeciesID_2;
@ -482,7 +478,7 @@ namespace PKHeX.Core
public override PKM GetPKM(byte[] data) public override PKM GetPKM(byte[] data)
{ {
if (data.Length == SIZE_STORED) if (data.Length == SIZE_STORED)
return new PokemonList2(data, PokemonList2.CapacityType.Single, Japanese)[0]; return new PokeList2(data, PokeListType.Single, Japanese)[0];
return new PK2(data); return new PK2(data);
} }
public override byte[] DecryptPKM(byte[] data) public override byte[] DecryptPKM(byte[] data)