mirror of
https://github.com/kwsch/PKHeX
synced 2024-12-18 08:23:12 +00:00
2805f699ac
Thanks @poutros ! Resolves #143
698 lines
28 KiB
C#
698 lines
28 KiB
C#
using System;
|
|
using System.Linq;
|
|
|
|
namespace PKHeX
|
|
{
|
|
public sealed class SAV4 : SaveFile
|
|
{
|
|
public override string BAKName => $"{FileName} [{OT} ({Version})" +/* - {LastSavedTime}*/ "].bak";
|
|
public override string Filter => (Footer.Length > 0 ? "DeSmuME DSV|*.dsv|" : "") + "SAV File|*.sav";
|
|
public override string Extension => ".sav";
|
|
public SAV4(byte[] data = null, GameVersion versionOverride = GameVersion.Any)
|
|
{
|
|
Data = data == null ? new byte[SaveUtil.SIZE_G4RAW] : (byte[])data.Clone();
|
|
BAK = (byte[])Data.Clone();
|
|
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
|
|
|
|
// Get Version
|
|
if (data == null)
|
|
Version = GameVersion.HGSS;
|
|
else if (versionOverride != GameVersion.Any)
|
|
Version = versionOverride;
|
|
else Version = SaveUtil.getIsG4SAV(Data);
|
|
if (Version == GameVersion.Invalid)
|
|
return;
|
|
|
|
getActiveBlock();
|
|
getSAVOffsets();
|
|
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: Personal = PersonalTable.DP; break;
|
|
case GameVersion.Pt: Personal = PersonalTable.Pt; break;
|
|
case GameVersion.HGSS: Personal = PersonalTable.HGSS; break;
|
|
}
|
|
|
|
if (!Exportable)
|
|
resetBoxes();
|
|
}
|
|
|
|
// Configuration
|
|
public override SaveFile Clone() { return new SAV4(Data, Version); }
|
|
|
|
public override int SIZE_STORED => PKX.SIZE_4STORED;
|
|
public override int SIZE_PARTY => PKX.SIZE_4PARTY;
|
|
public override PKM BlankPKM => new PK4();
|
|
protected override Type PKMType => typeof(PK4);
|
|
|
|
public override int BoxCount => 18;
|
|
public override int MaxEV => 255;
|
|
public override int Generation => 4;
|
|
protected override int EventFlagMax => int.MinValue;
|
|
protected override int EventConstMax => int.MinValue;
|
|
protected override int GiftCountMax => 11;
|
|
public override int OTLength => 8;
|
|
public override int NickLength => 10;
|
|
|
|
public override int MaxMoveID => 467;
|
|
public override int MaxSpeciesID => 493;
|
|
public override int MaxItemID => Version == GameVersion.HGSS ? 536 : Version == GameVersion.Pt ? 467 : 464;
|
|
public override int MaxAbilityID => 123;
|
|
public override int MaxBallID => 0x18;
|
|
public override int MaxGameID => 15; // Colo/XD
|
|
|
|
// Checksums
|
|
protected override void setChecksums()
|
|
{
|
|
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
// 0x0000-0xC0EC @ 0xC0FE
|
|
// 0xc100-0x1E2CC @ 0x1E2DE
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xC0EC).ToArray())).CopyTo(Data, 0xC0FE + GBO);
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0xc100 + SBO).Take(0x121CC).ToArray())).CopyTo(Data, 0x1E2DE + SBO);
|
|
break;
|
|
case GameVersion.Pt:
|
|
// 0x0000-0xCF18 @ 0xCF2A
|
|
// 0xCF2C-0x1F0FC @ 0x1F10E
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xCF18).ToArray())).CopyTo(Data, 0xCF2A + GBO);
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0xCF2C + SBO).Take(0x121D0).ToArray())).CopyTo(Data, 0x1F10E + SBO);
|
|
break;
|
|
case GameVersion.HGSS:
|
|
// 0x0000-0xF618 @ 0xF626
|
|
// 0xF700-0x219FC @ 0x21A0E
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xF618).ToArray())).CopyTo(Data, 0xF626 + GBO);
|
|
BitConverter.GetBytes(SaveUtil.ccitt16(Data.Skip(0xF700 + SBO).Take(0x12300).ToArray())).CopyTo(Data, 0x21A0E + SBO);
|
|
break;
|
|
}
|
|
}
|
|
public override bool ChecksumsValid
|
|
{
|
|
get
|
|
{
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
// 0x0000-0xC0EC @ 0xC0FE
|
|
// 0xc100-0x1E2CC @ 0x1E2DE
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xC0EC).ToArray()) != BitConverter.ToUInt16(Data, 0xC0FE + GBO))
|
|
return false; // Small Fail
|
|
if (SaveUtil.ccitt16(Data.Skip(0xc100 + SBO).Take(0x121CC).ToArray()) != BitConverter.ToUInt16(Data, 0x1E2DE + SBO))
|
|
return false; // Large Fail
|
|
break;
|
|
case GameVersion.Pt:
|
|
// 0x0000-0xCF18 @ 0xCF2A
|
|
// 0xCF2C-0x1F0FC @ 0x1F10E
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xCF18).ToArray()) != BitConverter.ToUInt16(Data, 0xCF2A + GBO))
|
|
return false; // Small Fail
|
|
if (SaveUtil.ccitt16(Data.Skip(0xCF2C + SBO).Take(0x121D0).ToArray()) != BitConverter.ToUInt16(Data, 0x1F10E + SBO))
|
|
return false; // Large Fail
|
|
break;
|
|
case GameVersion.HGSS:
|
|
// 0x0000-0xF618 @ 0xF626
|
|
// 0xF700-0x219FC @ 0x21A0E
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xF618).ToArray()) != BitConverter.ToUInt16(Data, 0xF626 + GBO))
|
|
return false; // Small Fail
|
|
if (SaveUtil.ccitt16(Data.Skip(0xF700 + SBO).Take(0x12300).ToArray()) != BitConverter.ToUInt16(Data, 0x21A0E + SBO))
|
|
return false; // Large Fail
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
public override string ChecksumInfo
|
|
{
|
|
get
|
|
{
|
|
string r = "";
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
// 0x0000-0xC0EC @ 0xC0FE
|
|
// 0xc100-0x1E2CC @ 0x1E2DE
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xC0EC).ToArray()) != BitConverter.ToUInt16(Data, 0xC0FE + GBO))
|
|
r += "Small block checksum is invalid" + Environment.NewLine;
|
|
if (SaveUtil.ccitt16(Data.Skip(0xc100 + SBO).Take(0x121CC).ToArray()) != BitConverter.ToUInt16(Data, 0x1E2DE + SBO))
|
|
r += "Large block checksum is invalid" + Environment.NewLine;
|
|
break;
|
|
case GameVersion.Pt:
|
|
// 0x0000-0xCF18 @ 0xCF2A
|
|
// 0xCF2C-0x1F0FC @ 0x1F10E
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xCF18).ToArray()) != BitConverter.ToUInt16(Data, 0xCF2A + GBO))
|
|
r += "Small block checksum is invalid" + Environment.NewLine;
|
|
if (SaveUtil.ccitt16(Data.Skip(0xCF2C + SBO).Take(0x121D0).ToArray()) != BitConverter.ToUInt16(Data, 0x1F10E + SBO))
|
|
r += "Large block checksum is invalid" + Environment.NewLine;
|
|
break;
|
|
case GameVersion.HGSS:
|
|
// 0x0000-0xF618 @ 0xF626
|
|
// 0xF700-0x219FC @ 0x21A0E
|
|
if (SaveUtil.ccitt16(Data.Skip(0 + GBO).Take(0xF618).ToArray()) != BitConverter.ToUInt16(Data, 0xF626 + GBO))
|
|
r += "Small block checksum is invalid" + Environment.NewLine;
|
|
if (SaveUtil.ccitt16(Data.Skip(0xF700 + SBO).Take(0x12300).ToArray()) != BitConverter.ToUInt16(Data, 0x21A0E + SBO))
|
|
r += "Large block checksum is invalid" + Environment.NewLine;
|
|
break;
|
|
}
|
|
return r.Length == 0 ? "Checksums valid." : r.TrimEnd();
|
|
}
|
|
}
|
|
|
|
// Blocks & Offsets
|
|
private int generalBlock = -1; // Small Block
|
|
private int storageBlock = -1; // Big Block
|
|
private int hofBlock = -1; // Hall of Fame Block
|
|
private int SBO => 0x40000 * storageBlock;
|
|
private int GBO => 0x40000 * generalBlock;
|
|
private int HBO => 0x40000 * hofBlock;
|
|
private void getActiveBlock()
|
|
{
|
|
if (Version < 0)
|
|
return;
|
|
int ofs = 0;
|
|
|
|
if (Version == GameVersion.DP) ofs = 0xC0F0; // DP
|
|
else if (Version == GameVersion.Pt) ofs = 0xCF1C; // PT
|
|
else if (Version == GameVersion.HGSS) ofs = 0xF626; // HGSS
|
|
generalBlock = BitConverter.ToUInt16(Data, ofs) >= BitConverter.ToUInt16(Data, ofs + 0x40000) ? 0 : 1;
|
|
|
|
if (Version == GameVersion.DP) ofs = 0x1E2D0; // DP
|
|
else if (Version == GameVersion.Pt) ofs = 0x1F100; // PT
|
|
else if (Version == GameVersion.HGSS) ofs = 0x21A00; // HGSS
|
|
storageBlock = BitConverter.ToUInt16(Data, ofs) >= BitConverter.ToUInt16(Data, ofs + 0x40000) ? 0 : 1;
|
|
}
|
|
private void getSAVOffsets()
|
|
{
|
|
if (Version < 0)
|
|
return;
|
|
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
AdventureInfo = 0 + GBO;
|
|
Trainer1 = 0x64 + GBO;
|
|
Party = 0x98 + GBO;
|
|
PokeDex = 0x12DC + GBO;
|
|
WondercardFlags = 0xA6D0 + GBO;
|
|
WondercardData = 0xA7fC + GBO;
|
|
|
|
OFS_PouchHeldItem = 0x624 + GBO;
|
|
OFS_PouchKeyItem = 0x8B8 + GBO;
|
|
OFS_PouchTMHM = 0x980 + GBO;
|
|
OFS_PouchMedicine = 0xB40 + GBO;
|
|
OFS_PouchBerry = 0xBE0 + GBO;
|
|
OFS_PouchBalls = 0xCE0 + GBO;
|
|
OFS_BattleItems = 0xD1C + GBO;
|
|
OFS_MailItems = 0xD50 + GBO;
|
|
LegalItems = Legal.Pouch_Items_DP;
|
|
LegalKeyItems = Legal.Pouch_Key_DP;
|
|
LegalTMHMs = Legal.Pouch_TMHM_DP;
|
|
LegalMedicine = Legal.Pouch_Medicine_DP;
|
|
LegalBerries = Legal.Pouch_Berries_DP;
|
|
LegalBalls = Legal.Pouch_Ball_DP;
|
|
LegalBattleItems = Legal.Pouch_Battle_DP;
|
|
LegalMailItems = Legal.Pouch_Mail_DP;
|
|
|
|
HeldItems = Legal.HeldItems_DP;
|
|
|
|
Daycare = 0x141C + GBO;
|
|
Box = 0xC104 + SBO;
|
|
break;
|
|
case GameVersion.Pt:
|
|
AdventureInfo = 0 + GBO;
|
|
Trainer1 = 0x68 + GBO;
|
|
Party = 0xA0 + GBO;
|
|
PokeDex = 0x1328 + GBO;
|
|
WondercardFlags = 0xB4C0 + GBO;
|
|
WondercardData = 0xB5C0 + GBO;
|
|
|
|
OFS_PouchHeldItem = 0x630 + GBO;
|
|
OFS_PouchKeyItem = 0x8C4 + GBO;
|
|
OFS_PouchTMHM = 0x98C + GBO;
|
|
OFS_MailItems = 0xB1C + GBO;
|
|
OFS_PouchMedicine = 0xB4C + GBO;
|
|
OFS_PouchBerry = 0xBEC + GBO;
|
|
OFS_PouchBalls = 0xCEC + GBO;
|
|
OFS_BattleItems = 0xD28 + GBO;
|
|
LegalItems = Legal.Pouch_Items_Pt;
|
|
LegalKeyItems = Legal.Pouch_Key_Pt;
|
|
LegalTMHMs = Legal.Pouch_TMHM_Pt;
|
|
LegalMedicine = Legal.Pouch_Medicine_Pt;
|
|
LegalBerries = Legal.Pouch_Berries_Pt;
|
|
LegalBalls = Legal.Pouch_Ball_Pt;
|
|
LegalBattleItems = Legal.Pouch_Battle_Pt;
|
|
LegalMailItems = Legal.Pouch_Mail_Pt;
|
|
|
|
HeldItems = Legal.HeldItems_Pt;
|
|
|
|
Daycare = 0x1654 + GBO;
|
|
Box = 0xCF30 + SBO;
|
|
break;
|
|
case GameVersion.HGSS:
|
|
AdventureInfo = 0 + GBO;
|
|
Trainer1 = 0x64 + GBO;
|
|
Party = 0x98 + GBO;
|
|
PokeDex = 0x12B8 + GBO;
|
|
WondercardFlags = 0x9D3C + GBO;
|
|
WondercardData = 0x9E3C + GBO;
|
|
|
|
OFS_PouchHeldItem = 0x644 + GBO; // 0x644-0x8D7 (0x8CB)
|
|
OFS_PouchKeyItem = 0x8D8 + GBO; // 0x8D8-0x99F (0x979)
|
|
OFS_PouchTMHM = 0x9A0 + GBO; // 0x9A0-0xB33 (0xB2F)
|
|
OFS_MailItems = 0xB34 + GBO; // 0xB34-0xB63 (0xB63)
|
|
OFS_PouchMedicine = 0xB64 + GBO; // 0xB64-0xC03 (0xBFB)
|
|
OFS_PouchBerry = 0xC04 + GBO; // 0xC04-0xD03
|
|
OFS_PouchBalls = 0xD04 + GBO; // 0xD04-0xD63
|
|
OFS_BattleItems = 0xD64 + GBO; // 0xD64-0xD97
|
|
LegalItems = Legal.Pouch_Items_HGSS;
|
|
LegalKeyItems = Legal.Pouch_Key_HGSS;
|
|
LegalTMHMs = Legal.Pouch_TMHM_HGSS;
|
|
LegalMedicine = Legal.Pouch_Medicine_HGSS;
|
|
LegalBerries = Legal.Pouch_Berries_HGSS;
|
|
LegalBalls = Legal.Pouch_Ball_HGSS;
|
|
LegalBattleItems = Legal.Pouch_Battle_HGSS;
|
|
LegalMailItems = Legal.Pouch_Mail_HGSS;
|
|
|
|
HeldItems = Legal.HeldItems_HGSS;
|
|
|
|
Daycare = 0x15FC + GBO;
|
|
Box = 0xF700 + SBO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private int WondercardFlags = int.MinValue;
|
|
private int AdventureInfo = int.MinValue;
|
|
public override bool HasPokeDex => false;
|
|
|
|
// Inventory
|
|
private ushort[] LegalItems, LegalKeyItems, LegalTMHMs, LegalMedicine, LegalBerries, LegalBalls, LegalBattleItems, LegalMailItems;
|
|
public override InventoryPouch[] Inventory
|
|
{
|
|
get
|
|
{
|
|
InventoryPouch[] pouch =
|
|
{
|
|
new InventoryPouch(InventoryType.Items, LegalItems, 995, OFS_PouchHeldItem),
|
|
new InventoryPouch(InventoryType.KeyItems, LegalKeyItems, 1, OFS_PouchKeyItem),
|
|
new InventoryPouch(InventoryType.TMHMs, LegalTMHMs, 95, OFS_PouchTMHM),
|
|
new InventoryPouch(InventoryType.Medicine, LegalMedicine, 995, OFS_PouchMedicine),
|
|
new InventoryPouch(InventoryType.Berries, LegalBerries, 995, OFS_PouchBerry),
|
|
new InventoryPouch(InventoryType.Balls, LegalBalls, 995, OFS_PouchBalls),
|
|
new InventoryPouch(InventoryType.BattleItems, LegalBattleItems, 995, OFS_BattleItems),
|
|
new InventoryPouch(InventoryType.MailItems, LegalMailItems, 995, OFS_MailItems),
|
|
};
|
|
foreach (var p in pouch)
|
|
p.getPouch(ref Data);
|
|
return pouch;
|
|
}
|
|
set
|
|
{
|
|
foreach (var p in value)
|
|
p.setPouch(ref Data);
|
|
}
|
|
}
|
|
|
|
// Storage
|
|
public override int PartyCount
|
|
{
|
|
get { return Data[Party - 4]; }
|
|
protected set { Data[Party - 4] = (byte)value; }
|
|
}
|
|
public override int getBoxOffset(int box)
|
|
{
|
|
return Box + SIZE_STORED*box*30 + (Version == GameVersion.HGSS ? box * 0x10 : 0);
|
|
}
|
|
public override int getPartyOffset(int slot)
|
|
{
|
|
return Party + SIZE_PARTY*slot;
|
|
}
|
|
|
|
// Trainer Info
|
|
public override string OT
|
|
{
|
|
get
|
|
{
|
|
return PKX.array2strG4(Data.Skip(Trainer1).Take(0x10).ToArray())
|
|
.Replace("\uE08F", "\u2640") // Nidoran ♂
|
|
.Replace("\uE08E", "\u2642") // Nidoran ♀
|
|
.Replace("\u2019", "\u0027"); // Farfetch'd
|
|
}
|
|
set
|
|
{
|
|
if (value.Length > 7)
|
|
value = value.Substring(0, 7); // Hard cap
|
|
string TempNick = value // Replace Special Characters and add Terminator
|
|
.Replace("\u2640", "\uE08F") // Nidoran ♂
|
|
.Replace("\u2642", "\uE08E") // Nidoran ♀
|
|
.Replace("\u0027", "\u2019"); // Farfetch'd
|
|
PKX.str2arrayG4(TempNick).CopyTo(Data, Trainer1);
|
|
}
|
|
}
|
|
public override ushort TID
|
|
{
|
|
get { return BitConverter.ToUInt16(Data, Trainer1 + 0x10 + 0); }
|
|
set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0x10 + 0); }
|
|
}
|
|
public override ushort SID
|
|
{
|
|
get { return BitConverter.ToUInt16(Data, Trainer1 + 0x12); }
|
|
set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0x12); }
|
|
}
|
|
public override uint Money
|
|
{
|
|
get { return BitConverter.ToUInt32(Data, Trainer1 + 0x14); }
|
|
set { BitConverter.GetBytes(value).CopyTo(Data, Trainer1 + 0x14); }
|
|
}
|
|
public override int Gender
|
|
{
|
|
get { return Data[Trainer1 + 0x18]; }
|
|
set { Data[Trainer1 + 0x18] = (byte)value; }
|
|
}
|
|
public override int Language
|
|
{
|
|
get { return Data[Trainer1 + 0x19]; }
|
|
set { Data[Trainer1 + 0x19] = (byte)value; }
|
|
}
|
|
public int Badges
|
|
{
|
|
get { return Data[Trainer1 + 0x1A]; }
|
|
set { if (value < 0) return; Data[Trainer1 + 0x1A] = (byte)value; }
|
|
}
|
|
public int Sprite
|
|
{
|
|
get { return Data[Trainer1 + 0x1B]; }
|
|
set { if (value < 0) return; Data[Trainer1 + 0x1B] = (byte)value; }
|
|
}
|
|
public int Badges16
|
|
{
|
|
get
|
|
{
|
|
if (Version != GameVersion.HGSS)
|
|
return -1;
|
|
return Data[Trainer1 + 0x1F];
|
|
}
|
|
set
|
|
{
|
|
if (value < 0)
|
|
return;
|
|
if (Version != GameVersion.HGSS)
|
|
return;
|
|
Data[Trainer1 + 0x1F] = (byte)value;
|
|
}
|
|
}
|
|
public override int PlayedHours
|
|
{
|
|
get { return BitConverter.ToUInt16(Data, Trainer1 + 0x22); }
|
|
set { BitConverter.GetBytes((ushort)value).CopyTo(Data, Trainer1 + 0x22); }
|
|
}
|
|
public override int PlayedMinutes
|
|
{
|
|
get { return Data[Trainer1 + 0x24]; }
|
|
set { Data[Trainer1 + 0x24] = (byte)value; }
|
|
}
|
|
public override int PlayedSeconds
|
|
{
|
|
get { return Data[Trainer1 + 0x25]; }
|
|
set { Data[Trainer1 + 0x25] = (byte)value; }
|
|
}
|
|
public int M
|
|
{
|
|
get
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
case GameVersion.HGSS: ofs = 0x1234; break;
|
|
case GameVersion.Pt: ofs = 0x1280; break;
|
|
}
|
|
return BitConverter.ToUInt16(Data, ofs);
|
|
}
|
|
set
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
case GameVersion.HGSS: ofs = 0x1234; break;
|
|
case GameVersion.Pt: ofs = 0x1280; break;
|
|
}
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs);
|
|
}
|
|
}
|
|
public int X
|
|
{
|
|
get
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x25FA; break;
|
|
case GameVersion.Pt: ofs = 0x287E; break;
|
|
case GameVersion.HGSS: ofs = 0x236E; break;
|
|
}
|
|
return BitConverter.ToUInt16(Data, ofs);
|
|
}
|
|
set
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x25FA; break;
|
|
case GameVersion.Pt: ofs = 0x287E; break;
|
|
case GameVersion.HGSS: ofs = 0x236E; break;
|
|
}
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs);
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
case GameVersion.HGSS:
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x123C);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
public int Z
|
|
{
|
|
get
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x2602; break;
|
|
case GameVersion.Pt: ofs = 0x2886; break;
|
|
case GameVersion.HGSS: ofs = 0x2376; break;
|
|
}
|
|
return BitConverter.ToUInt16(Data, ofs);
|
|
}
|
|
set
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x2602; break;
|
|
case GameVersion.Pt: ofs = 0x2886; break;
|
|
case GameVersion.HGSS: ofs = 0x2376; break;
|
|
}
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs);
|
|
}
|
|
}
|
|
public int Y
|
|
{
|
|
get
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x25FE; break;
|
|
case GameVersion.Pt: ofs = 0x2882; break;
|
|
case GameVersion.HGSS: ofs = 0x2372; break;
|
|
}
|
|
return BitConverter.ToUInt16(Data, ofs);
|
|
}
|
|
set
|
|
{
|
|
int ofs = 0;
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP: ofs = 0x25FE; break;
|
|
case GameVersion.Pt: ofs = 0x2882; break;
|
|
case GameVersion.HGSS: ofs = 0x2372; break;
|
|
}
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, ofs);
|
|
switch (Version)
|
|
{
|
|
case GameVersion.DP:
|
|
case GameVersion.HGSS:
|
|
BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1240);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
public override int SecondsToStart { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x34); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x34); } }
|
|
public override int SecondsToFame { get { return BitConverter.ToInt32(Data, AdventureInfo + 0x3C); } set { BitConverter.GetBytes(value).CopyTo(Data, AdventureInfo + 0x3C); } }
|
|
|
|
// Storage
|
|
public override int CurrentBox
|
|
{
|
|
get { return Data[Version == GameVersion.HGSS ? getBoxOffset(BoxCount) : Box - 4]; }
|
|
set { Data[Version == GameVersion.HGSS ? getBoxOffset(BoxCount) : Box - 4] = (byte)value; }
|
|
}
|
|
public override int getBoxWallpaper(int box)
|
|
{
|
|
// Box Wallpaper is directly after the Box Names
|
|
int offset = getBoxOffset(BoxCount);
|
|
if (Version == GameVersion.HGSS) offset += 0x18;
|
|
offset += BoxCount*0x28 + box;
|
|
return Data[offset];
|
|
}
|
|
public override string getBoxName(int box)
|
|
{
|
|
int offset = getBoxOffset(BoxCount);
|
|
if (Version == GameVersion.HGSS) offset += 0x8;
|
|
return PKX.array2strG4(Data.Skip(offset + box*0x28).Take(0x28).ToArray());
|
|
}
|
|
public override void setBoxName(int box, string value)
|
|
{
|
|
if (value.Length > 13)
|
|
value = value.Substring(0, 13); // Hard cap
|
|
int offset = getBoxOffset(BoxCount);
|
|
if (Version == GameVersion.HGSS) offset += 0x8;
|
|
PKX.str2arrayG4(value).CopyTo(Data, offset + box*0x28);
|
|
}
|
|
public override PKM getPKM(byte[] data)
|
|
{
|
|
return new PK4(data);
|
|
}
|
|
public override byte[] decryptPKM(byte[] data)
|
|
{
|
|
return PKX.decryptArray45(data);
|
|
}
|
|
|
|
// Daycare
|
|
public override int getDaycareSlotOffset(int loc, int slot)
|
|
{
|
|
return Daycare + slot * SIZE_PARTY;
|
|
}
|
|
public override ulong? getDaycareRNGSeed(int loc)
|
|
{
|
|
return null;
|
|
}
|
|
public override uint? getDaycareEXP(int loc, int slot)
|
|
{
|
|
int ofs = Daycare + (slot+1)*SIZE_PARTY - 4;
|
|
return BitConverter.ToUInt32(Data, ofs);
|
|
}
|
|
public override bool? getDaycareOccupied(int loc, int slot)
|
|
{
|
|
return null;
|
|
}
|
|
public override void setDaycareEXP(int loc, int slot, uint EXP)
|
|
{
|
|
int ofs = Daycare + (slot+1)*SIZE_PARTY - 4;
|
|
BitConverter.GetBytes(EXP).CopyTo(Data, ofs);
|
|
}
|
|
public override void setDaycareOccupied(int loc, int slot, bool occupied)
|
|
{
|
|
|
|
}
|
|
|
|
// Mystery Gift
|
|
public override MysteryGiftAlbum GiftAlbum
|
|
{
|
|
get
|
|
{
|
|
return new MysteryGiftAlbum
|
|
{
|
|
Flags = MysteryGiftReceivedFlags,
|
|
Gifts = MysteryGiftCards,
|
|
};
|
|
}
|
|
set
|
|
{
|
|
MysteryGiftReceivedFlags = value.Flags;
|
|
MysteryGiftCards = value.Gifts;
|
|
}
|
|
}
|
|
protected override bool[] MysteryGiftReceivedFlags
|
|
{
|
|
get
|
|
{
|
|
if (WondercardData < 0 || WondercardFlags < 0)
|
|
return null;
|
|
|
|
bool[] r = new bool[GiftFlagMax];
|
|
for (int i = 0; i < r.Length; i++)
|
|
r[i] = (Data[WondercardFlags + (i >> 3)] >> (i & 7) & 0x1) == 1;
|
|
return r;
|
|
}
|
|
set
|
|
{
|
|
if (WondercardData < 0 || WondercardFlags < 0)
|
|
return;
|
|
if (GiftFlagMax != value?.Length)
|
|
return;
|
|
|
|
byte[] data = new byte[value.Length / 8];
|
|
for (int i = 0; i < value.Length; i++)
|
|
if (value[i])
|
|
data[i >> 3] |= (byte)(1 << (i & 7));
|
|
|
|
data.CopyTo(Data, WondercardFlags);
|
|
Edited = true;
|
|
}
|
|
}
|
|
protected override MysteryGift[] MysteryGiftCards
|
|
{
|
|
get
|
|
{
|
|
MysteryGift[] cards = new MysteryGift[8 + 3];
|
|
for (int i = 0; i < 8; i++) // 8 PGT
|
|
cards[i] = new PGT(Data.Skip(WondercardData + i * PGT.Size).Take(PGT.Size).ToArray());
|
|
for (int i = 8; i < 11; i++) // 3 PCD
|
|
cards[i] = new PCD(Data.Skip(WondercardData + 8 * PGT.Size+ (i-8) * PCD.Size).Take(PCD.Size).ToArray());
|
|
return cards;
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
return;
|
|
for (int i = 0; i < 8; i++) // 8 PGT
|
|
{
|
|
if (value[i].GetType() != typeof(PGT))
|
|
continue;
|
|
value[i].Data.CopyTo(Data, WondercardData + i * PGT.Size);
|
|
}
|
|
for (int i = 8; i < 11; i++) // 3 PCD
|
|
{
|
|
if (value[i].GetType() != typeof(PCD))
|
|
continue;
|
|
value[i].Data.CopyTo(Data, WondercardData + 8 * PGT.Size + (i-8) * PGT.Size);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void setDex(PKM pkm)
|
|
{
|
|
if (pkm.Species == 0)
|
|
return;
|
|
if (pkm.Species > MaxSpeciesID)
|
|
return;
|
|
if (Version == GameVersion.Unknown)
|
|
return;
|
|
if (PokeDex < 0)
|
|
return;
|
|
|
|
const int brSize = 0x40;
|
|
int bit = pkm.Species - 1;
|
|
|
|
// Set the Species Owned Flag
|
|
Data[PokeDex + brSize * 0 + bit / 8 + 0x4] |= (byte)(1 << (bit % 8));
|
|
|
|
// Set the Species Seen Flag
|
|
Data[PokeDex + brSize * 1 + bit / 8 + 0x4] |= (byte)(1 << (bit % 8));
|
|
|
|
// Formes : todo
|
|
}
|
|
}
|
|
}
|