SAV1 skeleton implementation

This commit is contained in:
Michael Scire 2016-08-27 04:33:21 -07:00
parent e4ec1cb45f
commit b2342cddb9
7 changed files with 378 additions and 12 deletions

View file

@ -90,6 +90,7 @@
<Compile Include="PersonalInfo\PersonalInfo.cs" />
<Compile Include="PersonalInfo\PersonalInfoB2W2.cs" />
<Compile Include="PersonalInfo\PersonalInfoBW.cs" />
<Compile Include="PersonalInfo\PersonalInfoG1.cs" />
<Compile Include="PersonalInfo\PersonalInfoG3.cs" />
<Compile Include="PersonalInfo\PersonalInfoG4.cs" />
<Compile Include="PersonalInfo\PersonalInfoORAS.cs" />
@ -115,6 +116,7 @@
<Compile Include="Saves\BlockInfo.cs" />
<Compile Include="Saves\BoxWallpaper.cs" />
<Compile Include="Saves\Inventory.cs" />
<Compile Include="Saves\SAV1.cs" />
<Compile Include="Saves\SAV3.cs" />
<Compile Include="Saves\SAV4.cs" />
<Compile Include="Saves\SAV5.cs" />
@ -462,6 +464,7 @@
<None Include="Resources\byte\personal_hgss" />
<None Include="Resources\byte\personal_lg" />
<None Include="Resources\byte\personal_pt" />
<None Include="Resources\byte\personal_rby" />
<None Include="Resources\byte\personal_rs" />
<None Include="Resources\byte\personal_xy" />
<None Include="Resources\byte\wc6.pkl" />

View file

@ -186,7 +186,7 @@ namespace PKHeX
#endregion
}
class PokemonList
class PokemonList1
{
internal const int CAPACITY_DAYCARE = 1;
internal const int CAPACITY_PARTY = 6;
@ -225,7 +225,7 @@ namespace PKHeX
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 PokemonList(byte[] d, CapacityType c = CapacityType.Single, bool jp = false)
public PokemonList1(byte[] d, CapacityType c = CapacityType.Single, bool jp = false)
{
Japanese = jp;
Data = d ?? getEmptyList(c, Japanese);
@ -248,13 +248,13 @@ namespace PKHeX
}
}
public PokemonList(CapacityType c = CapacityType.Single, bool jp = false)
public PokemonList1(CapacityType c = CapacityType.Single, bool jp = false)
: this(null, c, jp)
{
Count = 1;
}
public PokemonList(PK1 pk)
public PokemonList1(PK1 pk)
: this(CapacityType.Single, pk.Japanese)
{
this[0] = pk;

View file

@ -47,6 +47,15 @@ namespace PKHeX
public int Move4 { get { return Data[0x0D]; } set { Data[0x0D] = (byte)value; } }
public override int EXPGrowth { get { return Data[0x13]; } set { Data[0x13] = (byte)value; } }
// EV Yields are just aliases for base stats in Gen I
public override int EV_HP { get { return HP; } set { } }
public override int EV_ATK { get { return ATK; } set { } }
public override int EV_DEF { get { return DEF; } set { } }
public override int EV_SPE { get { return SPE; } set { } }
public int EV_SPC { get { return SPC; } set { } }
public override int EV_SPA { get { return EV_SPC; } set { } }
public override int EV_SPD { get { return EV_SPC; } set { } }
// Future game values, unused
public override int[] Items { get { return new[] { 0, 0 }; } set { } }
public override int[] EggGroups { get { return new[] { 0, 0 }; } set { } }
@ -56,12 +65,5 @@ namespace PKHeX
public override int BaseFriendship { get { return 0; } set { } }
public override int EscapeRate { get { return 0; } set { } }
public override int Color { get { return 0; } set { } }
private int EVYield { get { return 0; } set { } }
public override int EV_HP { get { return 0; } set { } }
public override int EV_ATK { get { return 0; } set { } }
public override int EV_DEF { get { return 0; } set { } }
public override int EV_SPE { get { return 0; } set { } }
public override int EV_SPA { get { return 0; } set { } }
public override int EV_SPD { get { return 0; } set { } }
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace PKHeX
{
@ -23,7 +24,8 @@ namespace PKHeX
public readonly InventoryType Type;
public readonly ushort[] LegalItems;
public readonly int MaxCount;
private readonly int Offset;
public int Count => Items.Count(it => it.Count > 0);
public readonly int Offset;
private readonly int PouchDataSize;
public uint SecurityKey { private get; set; } // = 0 // Gen3 Only
public InventoryItem[] Items;

312
PKHeX/Saves/SAV1.cs Normal file
View file

@ -0,0 +1,312 @@
using System;
using System.Globalization;
using System.Linq;
namespace PKHeX
{
public sealed class SAV1 : SaveFile
{
public override string BAKName => $"{FileName} [{OT} ({Version})" +/* - {LastSavedTime}*/ "].bak";
public override string Filter => "SAV File|*.sav";
public override string Extension => ".sav";
public SAV1(byte[] data = null)
{
Data = data == null ? new byte[SaveUtil.SIZE_G1RAW] : (byte[])data.Clone();
BAK = (byte[])Data.Clone();
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
if (data == null)
Version = GameVersion.RBY;
else Version = SaveUtil.getIsG1SAV(Data);
if (Version == GameVersion.Invalid)
return;
Japanese = SaveUtil.getIsG1SAVJ(data);
OFS_PouchHeldItem = (Japanese ? 0x25C4 : 0x25C9);
OFS_PCItem = (Japanese ? 0x27DC : 0x27E6);
Personal = PersonalTable.RBY;
if (!Exportable)
resetBoxes();
}
private const int SIZE_RESERVED = 0x10000; // unpacked box data
public override byte[] Write(bool DSV)
{
setChecksums();
return Data;
}
// Configuration
public override SaveFile Clone() { return new SAV1(Data); }
public override int SIZE_STORED => PKX.SIZE_1STORED;
public override int SIZE_PARTY => PKX.SIZE_1PARTY;
public override PKM BlankPKM => new PK1();
protected override Type PKMType => typeof(PK1);
public override int MaxMoveID => 165;
public override int MaxSpeciesID => 151;
public override int MaxAbilityID => 0;
public override int MaxItemID => 255;
public override int MaxBallID => 0;
public override int MaxGameID => 99; // What do I set this to...?
public override int BoxCount => Japanese ? 8 : 12;
public override int MaxEV => 65535;
public override int Generation => 1;
protected override int GiftCountMax => 0;
public override int OTLength => Japanese ? 5 : 10;
public override int NickLength => Japanese ? 5 : 10;
public override bool HasParty => true;
// Checksums
protected override void setChecksums()
{
int CHECKSUM_OFS = Japanese ? 0x3594 : 0x3523;
Data[CHECKSUM_OFS] = 0;
uint chksum = 0;
for (int i = 0x2598; i < CHECKSUM_OFS; i++)
{
chksum += Data[i];
}
chksum = ~chksum;
chksum &= 0xFF;
Data[CHECKSUM_OFS] = (byte)chksum;
}
public override bool ChecksumsValid
{
get
{
int CHECKSUM_OFS = Japanese ? 0x3594 : 0x3523;
Data[CHECKSUM_OFS] = 0;
uint chksum = 0;
for (int i = 0x2598; i < CHECKSUM_OFS; i++)
{
chksum += Data[i];
}
chksum = ~chksum;
chksum &= 0xFF;
return Data[CHECKSUM_OFS] == (byte)chksum;
}
}
public override string ChecksumInfo
{
get
{
return ChecksumsValid ? "Checksum valid." : "Checksum invalid";
}
}
// Trainer Info
public override GameVersion Version { get; protected set; }
private int StringLength => Japanese ? PK1.STRLEN_J : PK1.STRLEN_U;
public override string OT
{
get { return PKX.getG3Str(Data.Skip(0x2598).Take(StringLength).ToArray(), Japanese); }
set
{
byte[] strdata = PKX.setG1Str(value, Japanese);
if (strdata.Length > StringLength)
throw new ArgumentOutOfRangeException("OT Name too long for given save file.");
strdata.CopyTo(Data, 0x2598);
}
}
public override int Gender
{
get { return 0; }
set { }
}
public override ushort TID
{
get { return Util.SwapEndianness(BitConverter.ToUInt16(Data, Japanese ? 0x25FB : 0x2605)); }
set { BitConverter.GetBytes(Util.SwapEndianness(value)).CopyTo(Data, Japanese ? 0x25FB : 0x2605); }
}
public override ushort SID
{
get { return 0; }
set { }
}
public override int PlayedHours
{
get { return BitConverter.ToUInt16(Data, Japanese ? 0x2CA0 : 0x2CED); }
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, Japanese ? 0x2CA0 : 0x2CED); }
}
public override int PlayedMinutes
{
get { return Data[Japanese ? 0x2CA2 : 0x2CEF]; }
set { Data[Japanese ? 0x2CA2 : 0x2CEF] = (byte)value; }
}
public override int PlayedSeconds
{
get { return Data[Japanese ? 0x2CA3 : 0x2CF0]; }
set { Data[Japanese ? 0x2CA3 : 0x2CF0] = (byte)value; }
}
public override uint Money
{
get { return uint.Parse((Util.SwapEndianness(BitConverter.ToUInt32(Data, Japanese ? 0x25EE : 0x25F3)) >> 8).ToString("X6")); }
set
{
BitConverter.GetBytes(Util.SwapEndianness(uint.Parse(value.ToString("000000"), NumberStyles.HexNumber))).Skip(1).ToArray().CopyTo(Data, Japanese ? 0x25EE : 0x25F3);
}
}
public uint Coin
{
get
{
return uint.Parse(Util.SwapEndianness(BitConverter.ToUInt16(Data, Japanese ? 0x2846 : 0x2850)).ToString("X4"));
}
set
{
BitConverter.GetBytes(Util.SwapEndianness(ushort.Parse(value.ToString("0000"), NumberStyles.HexNumber))).ToArray().CopyTo(Data, Japanese ? 0x2846 : 0x2850);
}
}
private readonly ushort[] LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs, LegalBerries;
public override InventoryPouch[] Inventory
{
get
{
ushort[] legalItems = LegalItems;
InventoryPouch[] pouch =
{
new InventoryPouch(InventoryType.Items, legalItems, 99, OFS_PouchHeldItem + 2, 20),
new InventoryPouch(InventoryType.Items, legalItems, 99, OFS_PCItem + 2, 50),
};
foreach (var p in pouch)
{
p.getPouch(ref Data);
int ofs = 0;
for (int i = 0; i < p.Count; i++)
{
while (p.Items[ofs].Count == 0)
ofs++;
p.Items[i] = p.Items[ofs++];
}
while (ofs < p.MaxCount)
p.Items[ofs++] = new InventoryItem { Count = 0, Index = 0 };
}
return pouch;
}
set
{
foreach (var p in value)
{
int ofs = 0;
for (int i = 0; i < p.Count; i++)
{
while (p.Items[ofs].Count == 0)
ofs++;
p.Items[i] = p.Items[ofs++];
}
while (ofs < p.MaxCount)
p.Items[ofs++] = new InventoryItem { Count = 0, Index = 0 };
p.setPouch(ref Data);
Data[p.Offset - 2] = (byte)p.Count;
}
}
}
public override int getDaycareSlotOffset(int loc = 0, int slot = 0)
{
return Daycare;
}
public override ulong? getDaycareRNGSeed(int loc)
{
return null;
}
public override uint? getDaycareEXP(int loc = 0, int slot = 0)
{
return null;
}
public override bool? getDaycareOccupied(int loc, int slot)
{
return null;
}
public override void setDaycareEXP(int loc, int slot, uint EXP)
{
}
public override void setDaycareOccupied(int loc, int slot, bool occupied)
{
}
// Storage
public override int PartyCount
{
get { return Data[Japanese ? 0x2ED5 : 0x2F2C]; }
protected set
{
Data[Japanese ? 0x2ED5 : 0x2F2C] = (byte)value;
}
}
public override int getBoxOffset(int box)
{
throw new NotImplementedException();
}
public override int getPartyOffset(int slot)
{
throw new NotImplementedException();
}
public override int CurrentBox
{
get { return Data[Japanese ? 0x2842 : 0x284C]; }
set { Data[Japanese ? 0x2842 : 0x284C] = (byte)value; }
}
public override int getBoxWallpaper(int box)
{
return 0;
}
public override string getBoxName(int box)
{
int boxNum = box + 1;
return $"Box {boxNum}";
}
public override void setBoxName(int box, string value)
{
}
public override PKM getPKM(byte[] data)
{
if (data.Length == PKX.SIZE_1JLIST || data.Length == PKX.SIZE_1ULIST)
return new PokemonList1(data, PokemonList1.CapacityType.Single, Japanese)[0];
return new PK1(data);
}
public override byte[] decryptPKM(byte[] data)
{
return data;
}
protected override void setDex(PKM pkm)
{
if (pkm.Species == 0)
return;
if (pkm.Species > MaxSpeciesID)
return;
if (Version == GameVersion.Unknown)
return;
int bit = pkm.Species - 1;
int ofs = bit >> 3;
byte bitval = (byte)(1 << (bit % 7));
// Set the Captured Flag
Data[(Japanese ? 0x259E : 0x25A3) + ofs] |= bitval;
// Set the Seen Flag
Data[(Japanese ? 0x25B1 : 0x25B6) + ofs] |= bitval;
}
}
}

View file

@ -58,6 +58,7 @@ namespace PKHeX
public bool E => Version == GameVersion.E;
public bool FRLG => Version == GameVersion.FRLG;
public bool RS => Version == GameVersion.RS;
public bool RBY => Version == GameVersion.RBY;
public virtual int MaxMoveID => int.MaxValue;
public virtual int MaxSpeciesID => int.MaxValue;
@ -260,6 +261,7 @@ namespace PKHeX
protected int OFS_PouchBalls { get; set; } = int.MinValue;
protected int OFS_BattleItems { get; set; } = int.MinValue;
protected int OFS_MailItems { get; set; } = int.MinValue;
protected int OFS_PCItem { get; set; } = int.MinValue;
// Mystery Gift
protected virtual bool[] MysteryGiftReceivedFlags { get { return null; } set { } }

View file

@ -45,6 +45,8 @@ namespace PKHeX
internal const int SIZE_G4RAW = 0x80000;
internal const int SIZE_G3RAW = 0x20000;
internal const int SIZE_G3RAWHALF = 0x10000;
internal const int SIZE_G1RAW = 0x8000;
internal const int SIZE_G1BAT = 0x802C;
internal static readonly byte[] FOOTER_DSV = Encoding.ASCII.GetBytes("|-DESMUME SAVE-|");
@ -53,6 +55,8 @@ namespace PKHeX
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
public static int getSAVGeneration(byte[] data)
{
if (getIsG1SAV(data) != GameVersion.Invalid)
return 1;
if (getIsG3SAV(data) != GameVersion.Invalid)
return 3;
if (getIsG4SAV(data) != GameVersion.Invalid)
@ -63,6 +67,47 @@ namespace PKHeX
return 6;
return -1;
}
/// <summary>Determines the type of 1st gen 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>
public static GameVersion getIsG1SAV(byte[] data)
{
if (data.Length != SIZE_G1RAW && data.Length != SIZE_G1BAT)
return GameVersion.Invalid;
// Check if it's not an american save or a japanese save
if (!(getIsG1SAVU(data) || getIsG1SAVJ(data)))
return GameVersion.Invalid;
// I can't actually detect which game version, because it's not stored anywhere.
// If you can think of anything to do here, please implement :)
return GameVersion.RBY;
}
/// <summary>Determines if 1st gen save is non-japanese</summary>
/// <param name="data">Save data of which to determine the region</param>
/// <returns>True if a valid non-japanese save, False otherwise.</returns>
public static bool getIsG1SAVU(byte[] data)
{
foreach (int ofs in new[] { 0x2F2C, 0x30C0 })
{
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>
/// <param name="data">Save data of which to determine the region</param>
/// <returns>True if a valid japanese save, False otherwise.</returns>
public static bool getIsG1SAVJ(byte[] data)
{
foreach (int ofs in new[] { 0x2ED5, 0x302D })
{
byte num_entries = data[ofs];
if (num_entries > 30 || data[ofs + 1 + num_entries] != 0xFF)
return false;
}
return true;
}
/// <summary>Determines the type of 3th gen 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>