mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 22:10:21 +00:00
Split SAV3 into version classes
Begone are the version-switch cases for value fetching.
This commit is contained in:
parent
6bce4eea14
commit
33e2c64721
14 changed files with 695 additions and 665 deletions
|
@ -66,7 +66,7 @@ namespace PKHeX.Core
|
|||
|
||||
private static List<SlotInfoMisc> GetExtraSlots3(SAV3 sav)
|
||||
{
|
||||
if (!sav.FRLG)
|
||||
if (sav is not SAV3FRLG)
|
||||
return None;
|
||||
return new List<SlotInfoMisc>
|
||||
{
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generation 3 <see cref="SaveFile"/> object.
|
||||
/// </summary>
|
||||
public sealed class SAV3 : SaveFile, ILangDeviantSave
|
||||
public abstract class SAV3 : SaveFile, ILangDeviantSave
|
||||
{
|
||||
protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}";
|
||||
public override string Extension => ".sav";
|
||||
protected internal sealed override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}";
|
||||
public sealed 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 bool IndeterminateGame => Version == GameVersion.Unknown;
|
||||
|
||||
// Similar to future games, the Generation 3 Mainline save files are comprised of two separate objects:
|
||||
// Object 1 - Small Block, containing misc configuration data & the Pokédex.
|
||||
|
@ -40,8 +38,8 @@ namespace PKHeX.Core
|
|||
public readonly byte[] Small = new byte[SIZE_SMALL];
|
||||
public readonly byte[] Large = new byte[SIZE_LARGE];
|
||||
public readonly byte[] Storage = new byte[SIZE_PC];
|
||||
protected override byte[] BoxBuffer => Storage;
|
||||
protected override byte[] PartyBuffer => Large;
|
||||
protected sealed override byte[] BoxBuffer => Storage;
|
||||
protected sealed override byte[] PartyBuffer => Large;
|
||||
|
||||
// 0x83D0
|
||||
private const int SIZE_PC = sizeof(int) // Current Box
|
||||
|
@ -67,37 +65,15 @@ namespace PKHeX.Core
|
|||
0x7d0 // D | PC Block 8
|
||||
};
|
||||
|
||||
private PersonalTable _personal;
|
||||
public override PersonalTable Personal => _personal;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
public sealed override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
|
||||
public SAV3(GameVersion version = GameVersion.FRLG, bool japanese = false)
|
||||
protected SAV3(bool japanese)
|
||||
{
|
||||
Version = version switch
|
||||
{
|
||||
GameVersion.FR or GameVersion.LG => GameVersion.FRLG,
|
||||
GameVersion.R or GameVersion.S => GameVersion.RS,
|
||||
_ => version
|
||||
};
|
||||
_personal = SaveUtil.GetG3Personal(Version);
|
||||
Japanese = japanese;
|
||||
|
||||
BlockOrder = Array.Empty<short>();
|
||||
|
||||
LegalKeyItems = Version switch
|
||||
{
|
||||
GameVersion.RS => Legal.Pouch_Key_RS,
|
||||
GameVersion.E => Legal.Pouch_Key_E,
|
||||
_ => Legal.Pouch_Key_FRLG
|
||||
};
|
||||
PokeDex = 0x18;
|
||||
SeenFlagOffsets = Array.Empty<int>();
|
||||
|
||||
Initialize();
|
||||
ClearBoxes();
|
||||
}
|
||||
|
||||
public SAV3(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
|
||||
protected SAV3(byte[] data) : base(data)
|
||||
{
|
||||
LoadBlocks(out BlockOrder);
|
||||
|
||||
|
@ -106,81 +82,10 @@ namespace PKHeX.Core
|
|||
LoadBlocks(Large, 1, 5);
|
||||
LoadBlocks(Storage, 5, BLOCK_COUNT);
|
||||
|
||||
Version = versionOverride != GameVersion.Any ? versionOverride : GetVersion(Small);
|
||||
_personal = SaveUtil.GetG3Personal(Version);
|
||||
|
||||
// Japanese games are limited to 5 character OT names; any unused characters are 0xFF.
|
||||
// 5 for JP, 7 for INT. There's always 1 terminator, thus we can check 0x6-0x7 being 0xFFFF = INT
|
||||
// OT name is stored at the top of the first block.
|
||||
Japanese = BitConverter.ToInt16(Small, 0x6) == 0;
|
||||
|
||||
LegalKeyItems = Version switch
|
||||
{
|
||||
GameVersion.RS => Legal.Pouch_Key_RS,
|
||||
GameVersion.E => Legal.Pouch_Key_E,
|
||||
_ => Legal.Pouch_Key_FRLG
|
||||
};
|
||||
|
||||
PokeDex = 0x18;
|
||||
SeenFlagOffsets = Version switch
|
||||
{
|
||||
GameVersion.RS => new[] { 0x938, 0x3A8C },
|
||||
GameVersion.E => new[] { 0x988, 0x3B24 },
|
||||
_ => new[] { 0x5F8, 0x3A18 }
|
||||
};
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Box = 0;
|
||||
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
OFS_PCItem = 0x0498;
|
||||
OFS_PouchHeldItem = 0x0560;
|
||||
OFS_PouchKeyItem = 0x05B0;
|
||||
OFS_PouchBalls = 0x0600;
|
||||
OFS_PouchTMHM = 0x0640;
|
||||
OFS_PouchBerry = 0x0740;
|
||||
EventFlag = 0x1220;
|
||||
EventConst = 0x1340;
|
||||
OFS_Decorations = 0x26A0;
|
||||
DaycareOffset = 0x2F9C;
|
||||
break;
|
||||
case GameVersion.E:
|
||||
OFS_PCItem = 0x0498;
|
||||
OFS_PouchHeldItem = 0x0560;
|
||||
OFS_PouchKeyItem = 0x05D8;
|
||||
OFS_PouchBalls = 0x0650;
|
||||
OFS_PouchTMHM = 0x0690;
|
||||
OFS_PouchBerry = 0x0790;
|
||||
EventFlag = 0x1270;
|
||||
EventConst = 0x139C;
|
||||
OFS_Decorations = 0x2734;
|
||||
DaycareOffset = 0x3030;
|
||||
break;
|
||||
case GameVersion.FRLG:
|
||||
OFS_PCItem = 0x0298;
|
||||
OFS_PouchHeldItem = 0x0310;
|
||||
OFS_PouchKeyItem = 0x03B8;
|
||||
OFS_PouchBalls = 0x0430;
|
||||
OFS_PouchTMHM = 0x0464;
|
||||
OFS_PouchBerry = 0x054C;
|
||||
EventFlag = 0xEE0;
|
||||
EventConst = 0x1000;
|
||||
DaycareOffset = 0x2F80;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException(nameof(Version));
|
||||
}
|
||||
|
||||
LoadEReaderBerryData();
|
||||
|
||||
// Sanity Check SeenFlagOffsets -- early saves may not have block 4 initialized yet
|
||||
SeenFlagOffsets = SeenFlagOffsets.Where(z => z >= 0).ToArray();
|
||||
}
|
||||
|
||||
private void LoadBlocks(byte[] dest, short start, short end)
|
||||
|
@ -250,28 +155,7 @@ namespace PKHeX.Core
|
|||
return count1 > count2 ? 0 : 1;
|
||||
}
|
||||
|
||||
public static GameVersion GetVersion(byte[] data, int offset = 0)
|
||||
{
|
||||
uint GameCode = BitConverter.ToUInt32(data, offset + 0xAC);
|
||||
switch (GameCode)
|
||||
{
|
||||
case 1: return GameVersion.FRLG; // fixed value
|
||||
case 0: return GameVersion.RS; // no battle tower record data
|
||||
case uint.MaxValue: return GameVersion.Unknown; // what a hack
|
||||
default:
|
||||
// Ruby doesn't set data as far down as Emerald.
|
||||
// 00 FF 00 00 00 00 00 00 00 FF 00 00 00 00 00 00
|
||||
// ^ byte pattern in Emerald saves, is all zero in Ruby/Sapphire as far as I can tell.
|
||||
// Some saves have had data @ 0x550
|
||||
if (BitConverter.ToUInt64(data, offset + 0xEE0) != 0)
|
||||
return GameVersion.E;
|
||||
if (BitConverter.ToUInt64(data, offset + 0xEE8) != 0)
|
||||
return GameVersion.E;
|
||||
return GameVersion.RS;
|
||||
}
|
||||
}
|
||||
|
||||
protected override byte[] GetFinalData()
|
||||
protected sealed override byte[] GetFinalData()
|
||||
{
|
||||
// Copy Box data back
|
||||
SaveBlocks(Small, 0, 1);
|
||||
|
@ -284,41 +168,34 @@ namespace PKHeX.Core
|
|||
private int ABO => ActiveSAV*SIZE_BLOCK*0xE;
|
||||
private readonly short[] BlockOrder;
|
||||
|
||||
// Configuration
|
||||
protected override SaveFile CloneInternal() => new SAV3(Write(), Version);
|
||||
protected sealed override int SIZE_STORED => PokeCrypto.SIZE_3STORED;
|
||||
protected sealed override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY;
|
||||
public sealed override PKM BlankPKM => new PK3();
|
||||
public sealed override Type PKMType => typeof(PK3);
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY;
|
||||
public override PKM BlankPKM => new PK3();
|
||||
public override Type PKMType => typeof(PK3);
|
||||
public sealed override int MaxMoveID => Legal.MaxMoveID_3;
|
||||
public sealed override int MaxSpeciesID => Legal.MaxSpeciesID_3;
|
||||
public sealed override int MaxAbilityID => Legal.MaxAbilityID_3;
|
||||
public sealed override int MaxItemID => Legal.MaxItemID_3;
|
||||
public sealed override int MaxBallID => Legal.MaxBallID_3;
|
||||
public sealed override int MaxGameID => Legal.MaxGameID_3;
|
||||
|
||||
public override int MaxMoveID => Legal.MaxMoveID_3;
|
||||
public override int MaxSpeciesID => Legal.MaxSpeciesID_3;
|
||||
public override int MaxAbilityID => Legal.MaxAbilityID_3;
|
||||
public override int MaxItemID => Legal.MaxItemID_3;
|
||||
public override int MaxBallID => Legal.MaxBallID_3;
|
||||
public override int MaxGameID => Legal.MaxGameID_3;
|
||||
public sealed override int BoxCount => 14;
|
||||
public sealed override int MaxEV => 255;
|
||||
public sealed override int Generation => 3;
|
||||
protected sealed override int GiftCountMax => 1;
|
||||
public sealed override int OTLength => 7;
|
||||
public sealed override int NickLength => 10;
|
||||
public sealed override int MaxMoney => 999999;
|
||||
|
||||
public override int BoxCount => 14;
|
||||
public override int MaxEV => 255;
|
||||
public override int Generation => 3;
|
||||
protected override int GiftCountMax => 1;
|
||||
public override int OTLength => 7;
|
||||
public override int NickLength => 10;
|
||||
public override int MaxMoney => 999999;
|
||||
protected override int EventFlagMax => 8 * (E ? 300 : 288); // 0x960 E, else 0x900
|
||||
protected override int EventConstMax => 0x100;
|
||||
public sealed override bool HasParty => true;
|
||||
|
||||
public bool E => Version == GameVersion.E;
|
||||
public bool FRLG => Version == GameVersion.FRLG;
|
||||
public bool RS => Version == GameVersion.RS;
|
||||
|
||||
public override bool HasParty => true;
|
||||
|
||||
public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGBA(data, offset);
|
||||
public sealed override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGBA(data, offset);
|
||||
protected sealed override PKM GetPKM(byte[] data) => new PK3(data);
|
||||
protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data);
|
||||
|
||||
// Checksums
|
||||
protected override void SetChecksums()
|
||||
protected sealed override void SetChecksums()
|
||||
{
|
||||
for (int i = 0; i < BLOCK_COUNT; i++)
|
||||
{
|
||||
|
@ -345,7 +222,7 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public override bool ChecksumsValid
|
||||
public sealed override bool ChecksumsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -372,15 +249,15 @@ namespace PKHeX.Core
|
|||
return chk == BitConverter.ToUInt16(Data, ofs + 0xFF4);
|
||||
}
|
||||
|
||||
private bool IsChunkValid(int i)
|
||||
private bool IsChunkValid(int chunk)
|
||||
{
|
||||
int ofs = ABO + (i * SIZE_BLOCK);
|
||||
int len = chunkLength[BlockOrder[i]];
|
||||
int ofs = ABO + (chunk * SIZE_BLOCK);
|
||||
int len = chunkLength[BlockOrder[chunk]];
|
||||
ushort chk = Checksums.CheckSum32(Data, ofs, len);
|
||||
return chk == BitConverter.ToUInt16(Data, ofs + 0xFF6);
|
||||
}
|
||||
|
||||
public override string ChecksumInfo
|
||||
public sealed override string ChecksumInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -402,17 +279,9 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
// Trainer Info
|
||||
public override GameVersion Version { get; protected set; }
|
||||
public abstract uint SecurityKey { get; set; }
|
||||
|
||||
public uint SecurityKey => Version switch
|
||||
{
|
||||
GameVersion.E => BitConverter.ToUInt32(Small, 0xAC),
|
||||
GameVersion.FRLG => BitConverter.ToUInt32(Small, 0xF20),
|
||||
_ => 0u
|
||||
};
|
||||
|
||||
public override string OT
|
||||
public sealed override string OT
|
||||
{
|
||||
get => GetString(Small, 0, 0x10);
|
||||
set
|
||||
|
@ -422,37 +291,37 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public override int Gender
|
||||
public sealed override int Gender
|
||||
{
|
||||
get => Small[8];
|
||||
set => Small[8] = (byte)value;
|
||||
}
|
||||
|
||||
public override int TID
|
||||
public sealed override int TID
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xA);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xA);
|
||||
}
|
||||
|
||||
public override int SID
|
||||
public sealed override int SID
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xC);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xC);
|
||||
}
|
||||
|
||||
public override int PlayedHours
|
||||
public sealed override int PlayedHours
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xE);
|
||||
set => BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xE);
|
||||
}
|
||||
|
||||
public override int PlayedMinutes
|
||||
public sealed override int PlayedMinutes
|
||||
{
|
||||
get => Small[0x10];
|
||||
set => Small[0x10] = (byte)value;
|
||||
}
|
||||
|
||||
public override int PlayedSeconds
|
||||
public sealed override int PlayedSeconds
|
||||
{
|
||||
get => Small[0x11];
|
||||
set => Small[0x11] = (byte)value;
|
||||
|
@ -464,7 +333,7 @@ namespace PKHeX.Core
|
|||
set => Small[0x12] = (byte)value;
|
||||
}
|
||||
|
||||
public override bool GetEventFlag(int flagNumber)
|
||||
public sealed override bool GetEventFlag(int flagNumber)
|
||||
{
|
||||
if (flagNumber >= EventFlagMax)
|
||||
throw new ArgumentException($"Event Flag to get ({flagNumber}) is greater than max ({EventFlagMax}).");
|
||||
|
@ -473,7 +342,7 @@ namespace PKHeX.Core
|
|||
return GetFlag(start + (flagNumber >> 3), flagNumber & 7);
|
||||
}
|
||||
|
||||
public override void SetEventFlag(int flagNumber, bool value)
|
||||
public sealed override void SetEventFlag(int flagNumber, bool value)
|
||||
{
|
||||
if (flagNumber >= EventFlagMax)
|
||||
throw new ArgumentException($"Event Flag to set ({flagNumber}) is greater than max ({EventFlagMax}).");
|
||||
|
@ -482,13 +351,13 @@ namespace PKHeX.Core
|
|||
SetFlag(start + (flagNumber >> 3), flagNumber & 7, value);
|
||||
}
|
||||
|
||||
public override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex);
|
||||
public override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Large, offset, bitIndex, value);
|
||||
public sealed override bool GetFlag(int offset, int bitIndex) => FlagUtil.GetFlag(Large, offset, bitIndex);
|
||||
public sealed override void SetFlag(int offset, int bitIndex, bool value) => FlagUtil.SetFlag(Large, offset, bitIndex, value);
|
||||
|
||||
public ushort GetEventConst(int index) => BitConverter.ToUInt16(Large, EventConst + (index * 2));
|
||||
public void SetEventConst(int index, ushort value) => BitConverter.GetBytes(value).CopyTo(Large, EventConst + (index * 2));
|
||||
|
||||
public override ushort[] GetEventConsts()
|
||||
public sealed override ushort[] GetEventConsts()
|
||||
{
|
||||
ushort[] Constants = new ushort[EventConstMax];
|
||||
for (int i = 0; i < Constants.Length; i++)
|
||||
|
@ -496,7 +365,7 @@ namespace PKHeX.Core
|
|||
return Constants;
|
||||
}
|
||||
|
||||
public override void SetEventConsts(ushort[] value)
|
||||
public sealed override void SetEventConsts(ushort[] value)
|
||||
{
|
||||
if (value.Length != EventConstMax)
|
||||
return;
|
||||
|
@ -505,6 +374,9 @@ namespace PKHeX.Core
|
|||
SetEventConst(i, value[i]);
|
||||
}
|
||||
|
||||
protected abstract int BadgeFlagStart { get; }
|
||||
public abstract uint Coin { get; set; }
|
||||
|
||||
public int Badges
|
||||
{
|
||||
get
|
||||
|
@ -527,216 +399,47 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
private int BadgeFlagStart
|
||||
public sealed override IReadOnlyList<InventoryPouch> Inventory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Version == GameVersion.FRLG)
|
||||
return 0x820;
|
||||
if (Version == GameVersion.RS)
|
||||
return 0x807;
|
||||
return 0x867; // emerald
|
||||
}
|
||||
}
|
||||
|
||||
public override uint Money
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
case GameVersion.E: return BitConverter.ToUInt32(Large, 0x0490) ^ SecurityKey;
|
||||
case GameVersion.FRLG: return BitConverter.ToUInt32(Large, 0x0290) ^ SecurityKey;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
case GameVersion.E: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0490); break;
|
||||
case GameVersion.FRLG: BitConverter.GetBytes(value ^ SecurityKey).CopyTo(Large, 0x0290); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint Coin
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
case GameVersion.E: return (ushort)(BitConverter.ToUInt16(Large, 0x0494) ^ SecurityKey);
|
||||
case GameVersion.FRLG: return (ushort)(BitConverter.ToUInt16(Large, 0x0294) ^ SecurityKey);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value > 9999)
|
||||
value = 9999;
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
case GameVersion.E: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0494); break;
|
||||
case GameVersion.FRLG: BitConverter.GetBytes((ushort)(value ^ SecurityKey)).CopyTo(Large, 0x0294); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint BP
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xEB8);
|
||||
set
|
||||
{
|
||||
if (value > 9999)
|
||||
value = 9999;
|
||||
BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEB8);
|
||||
}
|
||||
}
|
||||
|
||||
public uint BPEarned
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xEBA);
|
||||
set
|
||||
{
|
||||
if (value > 65535)
|
||||
value = 65535;
|
||||
BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEBA);
|
||||
}
|
||||
}
|
||||
|
||||
public uint BerryPowder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Version != GameVersion.FRLG)
|
||||
return 0;
|
||||
return BitConverter.ToUInt32(Small, 0xAF8) ^ SecurityKey;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Version != GameVersion.FRLG)
|
||||
return;
|
||||
SetData(Small, BitConverter.GetBytes(value ^ SecurityKey), 0xAF8);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ushort[] LegalKeyItems;
|
||||
private static ushort[] LegalItems => Legal.Pouch_Items_RS;
|
||||
private static ushort[] LegalBalls => Legal.Pouch_Ball_RS;
|
||||
private static ushort[] LegalTMHMs => Legal.Pouch_TMHM_RS;
|
||||
private static ushort[] LegalBerries => Legal.Pouch_Berries_RS;
|
||||
|
||||
private int OFS_PCItem, OFS_PouchHeldItem, OFS_PouchKeyItem, OFS_PouchBalls, OFS_PouchTMHM, OFS_PouchBerry, OFS_Decorations;
|
||||
|
||||
public override IReadOnlyList<InventoryPouch> Inventory
|
||||
{
|
||||
get
|
||||
{
|
||||
int max = Version == GameVersion.FRLG ? 999 : 99;
|
||||
var PCItems = new [] {LegalItems, LegalKeyItems, LegalBalls, LegalTMHMs, LegalBerries}.SelectMany(a => a).ToArray();
|
||||
InventoryPouch[] pouch =
|
||||
{
|
||||
new InventoryPouch3(InventoryType.Items, LegalItems, max, OFS_PouchHeldItem, (OFS_PouchKeyItem - OFS_PouchHeldItem)/4),
|
||||
new InventoryPouch3(InventoryType.KeyItems, LegalKeyItems, 1, OFS_PouchKeyItem, (OFS_PouchBalls - OFS_PouchKeyItem)/4),
|
||||
new InventoryPouch3(InventoryType.Balls, LegalBalls, max, OFS_PouchBalls, (OFS_PouchTMHM - OFS_PouchBalls)/4),
|
||||
new InventoryPouch3(InventoryType.TMHMs, LegalTMHMs, max, OFS_PouchTMHM, (OFS_PouchBerry - OFS_PouchTMHM)/4),
|
||||
new InventoryPouch3(InventoryType.Berries, LegalBerries, 999, OFS_PouchBerry, Version == GameVersion.FRLG ? 43 : 46),
|
||||
new InventoryPouch3(InventoryType.PCItems, PCItems, 999, OFS_PCItem, (OFS_PouchHeldItem - OFS_PCItem)/4),
|
||||
};
|
||||
var pouch = GetItems();
|
||||
foreach (var p in pouch)
|
||||
{
|
||||
if (p.Type != InventoryType.PCItems)
|
||||
((InventoryPouch3)p).SecurityKey = SecurityKey;
|
||||
p.SecurityKey = SecurityKey;
|
||||
}
|
||||
return pouch.LoadAll(Large);
|
||||
}
|
||||
set => value.SaveAll(Large);
|
||||
}
|
||||
|
||||
private int DaycareSlotSize => RS ? SIZE_STORED : SIZE_STORED + 0x3C; // 0x38 mail + 4 exp
|
||||
public override int DaycareSeedSize => E ? 8 : 4; // 32bit, 16bit
|
||||
public override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Large, GetDaycareEXPOffset(slot));
|
||||
public override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Large, GetDaycareEXPOffset(slot));
|
||||
public override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Large, GetDaycareSlotOffset(loc, slot));
|
||||
public override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ }
|
||||
public override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize);
|
||||
protected abstract InventoryPouch3[] GetItems();
|
||||
|
||||
private int EggEventFlag => GameVersion.FRLG.Contains(Version) ? 0x266 : 0x86;
|
||||
public override bool? IsDaycareHasEgg(int loc) => GetEventFlag(EggEventFlag);
|
||||
public override void SetDaycareHasEgg(int loc, bool hasEgg) => SetEventFlag(EggEventFlag, hasEgg);
|
||||
protected abstract int DaycareSlotSize { get; }
|
||||
|
||||
private int GetDaycareEXPOffset(int slot)
|
||||
{
|
||||
if (Version == GameVersion.RS)
|
||||
return GetDaycareSlotOffset(0, 2) + (2 * 0x38) + (4 * slot); // consecutive vals, after both consecutive slots & 2 mail
|
||||
return GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot
|
||||
}
|
||||
public sealed override uint? GetDaycareEXP(int loc, int slot) => BitConverter.ToUInt32(Large, GetDaycareEXPOffset(slot));
|
||||
public sealed override void SetDaycareEXP(int loc, int slot, uint EXP) => BitConverter.GetBytes(EXP).CopyTo(Large, GetDaycareEXPOffset(slot));
|
||||
public sealed override bool? IsDaycareOccupied(int loc, int slot) => IsPKMPresent(Large, GetDaycareSlotOffset(loc, slot));
|
||||
public sealed override void SetDaycareOccupied(int loc, int slot, bool occupied) { /* todo */ }
|
||||
public sealed override int GetDaycareSlotOffset(int loc, int slot) => DaycareOffset + (slot * DaycareSlotSize);
|
||||
|
||||
public override string GetDaycareRNGSeed(int loc)
|
||||
{
|
||||
if (Version == GameVersion.E)
|
||||
return BitConverter.ToUInt32(Large, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter
|
||||
return BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter
|
||||
}
|
||||
protected abstract int EggEventFlag { get; }
|
||||
public sealed override bool? IsDaycareHasEgg(int loc) => GetEventFlag(EggEventFlag);
|
||||
public sealed override void SetDaycareHasEgg(int loc, bool hasEgg) => SetEventFlag(EggEventFlag, hasEgg);
|
||||
|
||||
public override void SetDaycareRNGSeed(int loc, string seed)
|
||||
{
|
||||
if (Version == GameVersion.E) // egg pid
|
||||
{
|
||||
var val = Util.GetHexValue(seed);
|
||||
BitConverter.GetBytes(val).CopyTo(Large, GetDaycareSlotOffset(0, 2));
|
||||
}
|
||||
// egg pid half
|
||||
{
|
||||
var val = (ushort)Util.GetHexValue(seed);
|
||||
BitConverter.GetBytes(val).CopyTo(Large, GetDaycareEXPOffset(2));
|
||||
}
|
||||
}
|
||||
protected abstract int GetDaycareEXPOffset(int slot);
|
||||
|
||||
// Storage
|
||||
public override int PartyCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int ofs = 0x34;
|
||||
if (GameVersion.FRLG != Version)
|
||||
ofs += 0x200;
|
||||
return Large[ofs];
|
||||
}
|
||||
protected set
|
||||
{
|
||||
int ofs = 0x34;
|
||||
if (GameVersion.FRLG != Version)
|
||||
ofs += 0x200;
|
||||
Large[ofs] = (byte)value;
|
||||
}
|
||||
}
|
||||
#region Storage
|
||||
public sealed override int GetBoxOffset(int box) => Box + 4 + (SIZE_STORED * box * COUNT_SLOTSPERBOX);
|
||||
|
||||
public override int GetBoxOffset(int box)
|
||||
{
|
||||
return Box + 4 + (SIZE_STORED * box * 30);
|
||||
}
|
||||
|
||||
public override int GetPartyOffset(int slot)
|
||||
{
|
||||
int ofs = 0x38;
|
||||
if (GameVersion.FRLG != Version)
|
||||
ofs += 0x200;
|
||||
return ofs + (SIZE_PARTY * slot);
|
||||
}
|
||||
|
||||
public override int CurrentBox
|
||||
public sealed override int CurrentBox
|
||||
{
|
||||
get => Storage[0];
|
||||
set => Storage[0] = (byte)value;
|
||||
}
|
||||
|
||||
public override int GetBoxWallpaper(int box)
|
||||
public sealed override int GetBoxWallpaper(int box)
|
||||
{
|
||||
if (box > COUNT_BOX)
|
||||
return box;
|
||||
|
@ -746,7 +449,7 @@ namespace PKHeX.Core
|
|||
|
||||
private const int COUNT_BOXNAME = 8 + 1;
|
||||
|
||||
public override void SetBoxWallpaper(int box, int value)
|
||||
public sealed override void SetBoxWallpaper(int box, int value)
|
||||
{
|
||||
if (box > COUNT_BOX)
|
||||
return;
|
||||
|
@ -754,39 +457,28 @@ namespace PKHeX.Core
|
|||
Storage[offset] = (byte)value;
|
||||
}
|
||||
|
||||
protected override int GetBoxWallpaperOffset(int box)
|
||||
protected sealed override int GetBoxWallpaperOffset(int box)
|
||||
{
|
||||
int offset = GetBoxOffset(COUNT_BOX);
|
||||
offset += (COUNT_BOX * COUNT_BOXNAME) + box;
|
||||
return offset;
|
||||
}
|
||||
|
||||
public override string GetBoxName(int box)
|
||||
public sealed override string GetBoxName(int box)
|
||||
{
|
||||
int offset = GetBoxOffset(COUNT_BOX);
|
||||
return StringConverter3.GetString3(Storage, offset + (box * COUNT_BOXNAME), COUNT_BOXNAME, Japanese);
|
||||
}
|
||||
|
||||
public override void SetBoxName(int box, string value)
|
||||
public sealed override void SetBoxName(int box, string value)
|
||||
{
|
||||
int offset = GetBoxOffset(COUNT_BOX);
|
||||
SetString(value, COUNT_BOXNAME - 1).CopyTo(Storage, offset + (box * COUNT_BOXNAME));
|
||||
}
|
||||
#endregion
|
||||
|
||||
protected override PKM GetPKM(byte[] data)
|
||||
{
|
||||
return new PK3(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data)
|
||||
{
|
||||
return PokeCrypto.DecryptArray3(data);
|
||||
}
|
||||
|
||||
/// <summary> Mirrors of the Seen Flags (inside the Large block) </summary>
|
||||
private int[] SeenFlagOffsets;
|
||||
|
||||
protected override void SetDex(PKM pkm)
|
||||
#region Pokédex
|
||||
protected sealed override void SetDex(PKM pkm)
|
||||
{
|
||||
int species = pkm.Species;
|
||||
if (!CanSetDex(species))
|
||||
|
@ -811,8 +503,6 @@ namespace PKHeX.Core
|
|||
return false;
|
||||
if (species > MaxSpeciesID)
|
||||
return false;
|
||||
if (Version == GameVersion.Invalid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -820,7 +510,7 @@ namespace PKHeX.Core
|
|||
public uint DexPIDSpinda { get => BitConverter.ToUInt32(Small, PokeDex + 0x8); set => BitConverter.GetBytes(value).CopyTo(Small, PokeDex + 0x8); }
|
||||
public int DexUnownForm => PKX.GetUnownForm(DexPIDUnown);
|
||||
|
||||
public override bool GetCaught(int species)
|
||||
public sealed override bool GetCaught(int species)
|
||||
{
|
||||
int bit = species - 1;
|
||||
int ofs = bit >> 3;
|
||||
|
@ -828,7 +518,7 @@ namespace PKHeX.Core
|
|||
return FlagUtil.GetFlag(Small, caughtOffset + ofs, bit & 7);
|
||||
}
|
||||
|
||||
public override void SetCaught(int species, bool caught)
|
||||
public sealed override void SetCaught(int species, bool caught)
|
||||
{
|
||||
int bit = species - 1;
|
||||
int ofs = bit >> 3;
|
||||
|
@ -836,7 +526,7 @@ namespace PKHeX.Core
|
|||
FlagUtil.SetFlag(Small, caughtOffset + ofs, bit & 7, caught);
|
||||
}
|
||||
|
||||
public override bool GetSeen(int species)
|
||||
public sealed override bool GetSeen(int species)
|
||||
{
|
||||
int bit = species - 1;
|
||||
int ofs = bit >> 3;
|
||||
|
@ -844,15 +534,18 @@ namespace PKHeX.Core
|
|||
return FlagUtil.GetFlag(Small, seenOffset + ofs, bit & 7);
|
||||
}
|
||||
|
||||
public override void SetSeen(int species, bool seen)
|
||||
protected abstract int SeenOffset2 { get; }
|
||||
protected abstract int SeenOffset3 { get; }
|
||||
|
||||
public sealed override void SetSeen(int species, bool seen)
|
||||
{
|
||||
int bit = species - 1;
|
||||
int ofs = bit >> 3;
|
||||
|
||||
int seenOffset = PokeDex + 0x44;
|
||||
FlagUtil.SetFlag(Small, seenOffset + ofs, bit & 7, seen);
|
||||
foreach (int o in SeenFlagOffsets)
|
||||
FlagUtil.SetFlag(Large, o + ofs, bit & 7, seen);
|
||||
FlagUtil.SetFlag(Large, SeenOffset2 + ofs, bit & 7, seen);
|
||||
FlagUtil.SetFlag(Large, SeenOffset3 + ofs, bit & 7, seen);
|
||||
}
|
||||
|
||||
public byte PokedexSort
|
||||
|
@ -879,201 +572,27 @@ namespace PKHeX.Core
|
|||
set => Small[PokeDex + 0x03] = value;
|
||||
}
|
||||
|
||||
private const int PokedexNationalUnlockRSE = 0xDA;
|
||||
private const int PokedexNationalUnlockFRLG = 0xDA;
|
||||
private const ushort PokedexNationalUnlockWorkRSE = 0x0302;
|
||||
private const ushort PokedexNationalUnlockWorkFRLG = 0x6258;
|
||||
protected const int PokedexNationalUnlockRSE = 0xDA;
|
||||
protected const int PokedexNationalUnlockFRLG = 0xDA;
|
||||
protected const ushort PokedexNationalUnlockWorkRSE = 0x0302;
|
||||
protected const ushort PokedexNationalUnlockWorkFRLG = 0x6258;
|
||||
|
||||
public bool NationalDex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Version switch // only check natdex status in Block0
|
||||
{
|
||||
// enable nat dex option magic value
|
||||
GameVersion.RS or GameVersion.E => PokedexNationalMagicRSE == PokedexNationalUnlockRSE,
|
||||
GameVersion.FRLG => PokedexNationalMagicFRLG == PokedexNationalUnlockFRLG,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
set
|
||||
{
|
||||
PokedexMode = value ? 1 : 0; // mode
|
||||
switch (Version)
|
||||
{
|
||||
case GameVersion.RS:
|
||||
PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : 0; // magic
|
||||
SetEventFlag(0x836, value);
|
||||
SetEventConst(0x46, PokedexNationalUnlockWorkRSE);
|
||||
break;
|
||||
case GameVersion.E:
|
||||
PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : 0; // magic
|
||||
SetEventFlag(0x896, value);
|
||||
SetEventConst(0x46, PokedexNationalUnlockWorkRSE);
|
||||
break;
|
||||
case GameVersion.FRLG:
|
||||
//PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : 0; // magic
|
||||
//SetEventFlag(0x838, value);
|
||||
//SetEventConst(0x3C, PokedexNationalUnlockWorkRSE);
|
||||
PokedexNationalMagicFRLG = value ? PokedexNationalUnlockFRLG : 0; // magic
|
||||
SetEventFlag(0x840, value);
|
||||
SetEventConst(0x4E, PokedexNationalUnlockWorkFRLG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public abstract bool NationalDex { get; set; }
|
||||
#endregion
|
||||
|
||||
public override string GetString(byte[] data, int offset, int length) => StringConverter3.GetString3(data, offset, length, Japanese);
|
||||
public sealed override string GetString(byte[] data, int offset, int length) => StringConverter3.GetString3(data, offset, length, Japanese);
|
||||
|
||||
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
|
||||
public sealed override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
|
||||
{
|
||||
if (PadToSize == 0)
|
||||
PadToSize = maxLength + 1;
|
||||
return StringConverter3.SetString3(value, maxLength, Japanese, PadToSize, PadWith);
|
||||
}
|
||||
|
||||
#region eBerry
|
||||
// Offset and checksum code based from
|
||||
// https://github.com/suloku/wc-tool by Suloku
|
||||
private const int SIZE_EBERRY = 0x530;
|
||||
private const int OFFSET_EBERRY = 0x2E80 + 0x2E0;
|
||||
protected abstract int MailOffset { get; }
|
||||
public int GetMailOffset(int index) => (index * Mail3.SIZE) + MailOffset;
|
||||
|
||||
private uint EBerryChecksum => BitConverter.ToUInt32(Large, OFFSET_EBERRY + SIZE_EBERRY - 4);
|
||||
private bool IsEBerryChecksumValid { get; set; }
|
||||
|
||||
public string EBerryName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!GameVersion.RS.Contains(Version) || !IsEBerryChecksumValid)
|
||||
return string.Empty;
|
||||
return StringConverter3.GetString3(Large, OFFSET_EBERRY, 7, Japanese).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEBerryIsEnigma => string.IsNullOrEmpty(EBerryName.Trim());
|
||||
|
||||
private void LoadEReaderBerryData()
|
||||
{
|
||||
if (!GameVersion.RS.Contains(Version))
|
||||
return;
|
||||
|
||||
byte[] data = GetData(Large, OFFSET_EBERRY, SIZE_EBERRY - 4);
|
||||
|
||||
// 8 bytes are 0x00 for chk calculation
|
||||
for (int i = 0; i < 8; i++)
|
||||
data[0xC + i] = 0x00;
|
||||
uint chk = (uint)data.Sum(z => z);
|
||||
IsEBerryChecksumValid = EBerryChecksum == chk;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public RTC3 ClockInitial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (FRLG)
|
||||
throw new ArgumentException(nameof(ClockInitial));
|
||||
return new RTC3(GetData(Small, 0x98, RTC3.Size));
|
||||
}
|
||||
set
|
||||
{
|
||||
if (FRLG)
|
||||
return;
|
||||
SetData(Small, value.Data, 0x98);
|
||||
}
|
||||
}
|
||||
|
||||
public RTC3 ClockElapsed
|
||||
{
|
||||
get
|
||||
{
|
||||
if (FRLG)
|
||||
throw new ArgumentException(nameof(ClockElapsed));
|
||||
return new RTC3(GetData(Small, 0xA0, RTC3.Size));
|
||||
}
|
||||
set
|
||||
{
|
||||
if (FRLG)
|
||||
return;
|
||||
SetData(Small, value.Data, 0xA0);
|
||||
}
|
||||
}
|
||||
|
||||
public PokeBlock3Case PokeBlocks
|
||||
{
|
||||
get
|
||||
{
|
||||
var ofs = PokeBlockOffset;
|
||||
if (ofs < 0)
|
||||
throw new ArgumentException($"Game does not support {nameof(PokeBlocks)}.");
|
||||
return new PokeBlock3Case(Large, ofs);
|
||||
}
|
||||
set => SetData(Large, value.Write(), PokeBlockOffset);
|
||||
}
|
||||
|
||||
private int PokeBlockOffset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Version == GameVersion.E)
|
||||
return 0x848;
|
||||
if (Version == GameVersion.RS)
|
||||
return 0x7F8;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetMailOffset(int index)
|
||||
{
|
||||
var offset = Version switch
|
||||
{
|
||||
GameVersion.RS => 0x2B4C,
|
||||
GameVersion.E => 0x2BE0,
|
||||
_ => 0x2CD0, // FRLG
|
||||
};
|
||||
return (index * Mail3.SIZE) + offset;
|
||||
}
|
||||
|
||||
private int ExternalEventFlags => Version switch
|
||||
{
|
||||
GameVersion.RS => 0x312F,
|
||||
GameVersion.E => 0x31C7,
|
||||
_ => throw new IndexOutOfRangeException(),
|
||||
};
|
||||
|
||||
public bool HasReceivedWishmkrJirachi
|
||||
{
|
||||
get => !GameVersion.FRLG.Contains(Version) && GetFlag(ExternalEventFlags + 2, 0);
|
||||
set
|
||||
{
|
||||
if (!GameVersion.FRLG.Contains(Version))
|
||||
SetFlag(ExternalEventFlags + 2, 0, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ResetPersonal(GameVersion g)
|
||||
{
|
||||
if (g.GetGeneration() != 3)
|
||||
return false;
|
||||
_personal = SaveUtil.GetG3Personal(g);
|
||||
return true;
|
||||
}
|
||||
|
||||
public DecorationInventory3 Decorations
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Version == GameVersion.FRLG)
|
||||
throw new Exception();
|
||||
return Large.Slice(OFS_Decorations, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Version == GameVersion.FRLG)
|
||||
throw new Exception();
|
||||
SetData(Large, value.ToBytes(), OFS_Decorations);
|
||||
}
|
||||
}
|
||||
public abstract string EBerryName { get; }
|
||||
public abstract bool IsEBerryEngima { get; }
|
||||
}
|
||||
}
|
||||
|
|
181
PKHeX.Core/Saves/SAV3E.cs
Normal file
181
PKHeX.Core/Saves/SAV3E.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class SAV3E : SAV3, IGen3Hoenn, IGen3Joyful
|
||||
{
|
||||
// Configuration
|
||||
protected override SaveFile CloneInternal() => new SAV3E(Write());
|
||||
public override GameVersion Version { get => GameVersion.E; protected set { } }
|
||||
public override PersonalTable Personal => PersonalTable.E;
|
||||
|
||||
protected override int EventFlagMax => 8 * 300;
|
||||
protected override int EventConstMax => 0x100;
|
||||
protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp
|
||||
public override int DaycareSeedSize => 8; // 32bit
|
||||
protected override int EggEventFlag => 0x86;
|
||||
protected override int BadgeFlagStart => 0x867;
|
||||
|
||||
public SAV3E(byte[] data) : base(data) => Initialize();
|
||||
public SAV3E(bool japanese = false) : base(japanese) => Initialize();
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// small
|
||||
PokeDex = 0x18;
|
||||
|
||||
// large
|
||||
EventFlag = 0x1270;
|
||||
EventConst = 0x139C;
|
||||
DaycareOffset = 0x3030;
|
||||
|
||||
// storage
|
||||
Box = 0;
|
||||
}
|
||||
|
||||
#region Small
|
||||
public override bool NationalDex
|
||||
{
|
||||
get => PokedexNationalMagicRSE == PokedexNationalUnlockRSE;
|
||||
set
|
||||
{
|
||||
PokedexMode = value ? 1 : 0; // mode
|
||||
PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : 0; // magic
|
||||
SetEventFlag(0x896, value);
|
||||
SetEventConst(0x46, PokedexNationalUnlockWorkRSE);
|
||||
}
|
||||
}
|
||||
|
||||
public override uint SecurityKey
|
||||
{
|
||||
get => BitConverter.ToUInt32(Small, 0xAC);
|
||||
set => SetData(Small, BitConverter.GetBytes(value), 0xAC);
|
||||
}
|
||||
|
||||
public RTC3 ClockInitial
|
||||
{
|
||||
get => new(GetData(Small, 0x98, RTC3.Size));
|
||||
set => SetData(Small, value.Data, 0x98);
|
||||
}
|
||||
|
||||
public RTC3 ClockElapsed
|
||||
{
|
||||
get => new(GetData(Small, 0xA0, RTC3.Size));
|
||||
set => SetData(Small, value.Data, 0xA0);
|
||||
}
|
||||
|
||||
public ushort JoyfulJumpInRow { get => BitConverter.ToUInt16(Small, 0x1FC); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x1FC); }
|
||||
// u16 field2;
|
||||
public ushort JoyfulJump5InRow { get => BitConverter.ToUInt16(Small, 0x200); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x200); }
|
||||
public ushort JoyfulJumpGamesMaxPlayers { get => BitConverter.ToUInt16(Small, 0x202); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x202); }
|
||||
// u32 field8;
|
||||
public uint JoyfulJumpScore { get => BitConverter.ToUInt16(Small, 0x208); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0x208); }
|
||||
|
||||
public uint JoyfulBerriesScore { get => BitConverter.ToUInt16(Small, 0x20C); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0x20C); }
|
||||
public ushort JoyfulBerriesInRow { get => BitConverter.ToUInt16(Small, 0x210); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x210); }
|
||||
public ushort JoyfulBerries5InRow { get => BitConverter.ToUInt16(Small, 0x212); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0x212); }
|
||||
|
||||
public uint BP
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xEB8);
|
||||
set
|
||||
{
|
||||
if (value > 9999)
|
||||
value = 9999;
|
||||
BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEB8);
|
||||
}
|
||||
}
|
||||
|
||||
public uint BPEarned
|
||||
{
|
||||
get => BitConverter.ToUInt16(Small, 0xEBA);
|
||||
set
|
||||
{
|
||||
if (value > 65535)
|
||||
value = 65535;
|
||||
BitConverter.GetBytes((ushort)value).CopyTo(Small, 0xEBA);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Large
|
||||
public override int PartyCount { get => Large[0x34]; protected set => Large[0x34] = (byte)value; }
|
||||
public override int GetPartyOffset(int slot) => 0x38 + (SIZE_PARTY * slot);
|
||||
|
||||
public override uint Money
|
||||
{
|
||||
get => BitConverter.ToUInt32(Large, 0x0490) ^ SecurityKey;
|
||||
set => SetData(BitConverter.GetBytes(value ^ SecurityKey), 0x0490);
|
||||
}
|
||||
|
||||
public override uint Coin
|
||||
{
|
||||
get => BitConverter.ToUInt16(Large, 0x0494) ^ SecurityKey;
|
||||
set => SetData(BitConverter.GetBytes(value ^ SecurityKey), 0x0494);
|
||||
}
|
||||
|
||||
private const int OFS_PCItem = 0x0498;
|
||||
private const int OFS_PouchHeldItem = 0x0560;
|
||||
private const int OFS_PouchKeyItem = 0x05D8;
|
||||
private const int OFS_PouchBalls = 0x0650;
|
||||
private const int OFS_PouchTMHM = 0x0690;
|
||||
private const int OFS_PouchBerry = 0x0790;
|
||||
|
||||
protected override InventoryPouch3[] GetItems()
|
||||
{
|
||||
const int max = 99;
|
||||
var PCItems = ArrayUtil.ConcatAll(Legal.Pouch_Items_RS, Legal.Pouch_Key_E, Legal.Pouch_Ball_RS, Legal.Pouch_HM_RS, Legal.Pouch_Berries_RS);
|
||||
return new InventoryPouch3[]
|
||||
{
|
||||
new(InventoryType.Items, Legal.Pouch_Items_RS, max, OFS_PouchHeldItem, (OFS_PouchKeyItem - OFS_PouchHeldItem) / 4),
|
||||
new(InventoryType.KeyItems, Legal.Pouch_Key_E, 1, OFS_PouchKeyItem, (OFS_PouchBalls - OFS_PouchKeyItem) / 4),
|
||||
new(InventoryType.Balls, Legal.Pouch_Ball_RS, max, OFS_PouchBalls, (OFS_PouchTMHM - OFS_PouchBalls) / 4),
|
||||
new(InventoryType.TMHMs, Legal.Pouch_HM_RS, max, OFS_PouchTMHM, (OFS_PouchBerry - OFS_PouchTMHM) / 4),
|
||||
new(InventoryType.Berries, Legal.Pouch_Berries_RS, 999, OFS_PouchBerry, 46),
|
||||
new(InventoryType.PCItems, PCItems, 999, OFS_PCItem, (OFS_PouchHeldItem - OFS_PCItem) / 4),
|
||||
};
|
||||
}
|
||||
|
||||
public PokeBlock3Case PokeBlocks
|
||||
{
|
||||
get => new(Large, 0x848);
|
||||
set => SetData(Large, value.Write(), 0x848);
|
||||
}
|
||||
|
||||
protected override int SeenOffset2 => 0x988;
|
||||
|
||||
public DecorationInventory3 Decorations
|
||||
{
|
||||
get => Large.Slice(0x2734, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
|
||||
set => SetData(Large, value.ToBytes(), 0x2734);
|
||||
}
|
||||
|
||||
protected override int MailOffset => 0x2BE0;
|
||||
|
||||
protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot
|
||||
public override string GetDaycareRNGSeed(int loc) => BitConverter.ToUInt32(Large, GetDaycareSlotOffset(0, 2)).ToString("X8"); // after the 2 slots, before the step counter
|
||||
public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes(Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2));
|
||||
|
||||
private const int ExternalEventFlags = 0x31C7;
|
||||
|
||||
public bool HasReceivedWishmkrJirachi
|
||||
{
|
||||
get => GetFlag(ExternalEventFlags + 2, 0);
|
||||
set => SetFlag(ExternalEventFlags + 2, 0, value);
|
||||
}
|
||||
|
||||
#region eBerry
|
||||
private const int OFFSET_EBERRY = 0x31F8;
|
||||
private const int SIZE_EBERRY = 0x134;
|
||||
|
||||
public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY);
|
||||
public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY);
|
||||
|
||||
public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7);
|
||||
public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF;
|
||||
#endregion
|
||||
|
||||
protected override int SeenOffset3 => 0x3B24;
|
||||
#endregion
|
||||
}
|
||||
}
|
147
PKHeX.Core/Saves/SAV3FRLG.cs
Normal file
147
PKHeX.Core/Saves/SAV3FRLG.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class SAV3FRLG : SAV3, IGen3Joyful
|
||||
{
|
||||
// Configuration
|
||||
protected override SaveFile CloneInternal() => new SAV3FRLG(Write());
|
||||
public override GameVersion Version { get; protected set; } = GameVersion.FR; // allow mutation
|
||||
private PersonalTable _personal = PersonalTable.FR;
|
||||
public override PersonalTable Personal => _personal;
|
||||
|
||||
protected override int EventFlagMax => 8 * 288;
|
||||
protected override int EventConstMax => 0x100;
|
||||
protected override int DaycareSlotSize => SIZE_STORED + 0x3C; // 0x38 mail + 4 exp
|
||||
public override int DaycareSeedSize => 4; // 16bit
|
||||
protected override int EggEventFlag => 0x266;
|
||||
protected override int BadgeFlagStart => 0x820;
|
||||
|
||||
public SAV3FRLG(byte[] data) : base(data) => Initialize();
|
||||
public SAV3FRLG(bool japanese = false) : base(japanese) => Initialize();
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// small
|
||||
PokeDex = 0x18;
|
||||
|
||||
// large
|
||||
EventFlag = 0xEE0;
|
||||
EventConst = 0x1000;
|
||||
DaycareOffset = 0x2F80;
|
||||
|
||||
// storage
|
||||
Box = 0;
|
||||
}
|
||||
|
||||
public bool ResetPersonal(GameVersion g)
|
||||
{
|
||||
if (g is not (GameVersion.FR or GameVersion.LG))
|
||||
return false;
|
||||
_personal = g == GameVersion.FR ? PersonalTable.FR : PersonalTable.LG;
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Small
|
||||
public override bool NationalDex
|
||||
{
|
||||
get => PokedexNationalMagicFRLG == PokedexNationalUnlockFRLG;
|
||||
set
|
||||
{
|
||||
PokedexNationalMagicFRLG = value ? PokedexNationalUnlockFRLG : 0; // magic
|
||||
SetEventFlag(0x840, value);
|
||||
SetEventConst(0x4E, PokedexNationalUnlockWorkFRLG);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort JoyfulJumpInRow { get => BitConverter.ToUInt16(Small, 0xB00); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB00); }
|
||||
// u16 field2;
|
||||
public ushort JoyfulJump5InRow { get => BitConverter.ToUInt16(Small, 0xB04); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB04); }
|
||||
public ushort JoyfulJumpGamesMaxPlayers { get => BitConverter.ToUInt16(Small, 0xB06); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB06); }
|
||||
// u32 field8;
|
||||
public uint JoyfulJumpScore { get => BitConverter.ToUInt16(Small, 0xB0C); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0xB0C); }
|
||||
|
||||
public uint JoyfulBerriesScore { get => BitConverter.ToUInt16(Small, 0xB10); set => SetData(Small, BitConverter.GetBytes(Math.Min( 9999, value)), 0xB10); }
|
||||
public ushort JoyfulBerriesInRow { get => BitConverter.ToUInt16(Small, 0xB14); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB14); }
|
||||
public ushort JoyfulBerries5InRow { get => BitConverter.ToUInt16(Small, 0xB16); set => SetData(Small, BitConverter.GetBytes(Math.Min((ushort)9999, value)), 0xB16); }
|
||||
|
||||
public uint BerryPowder
|
||||
{
|
||||
get => BitConverter.ToUInt32(Small, 0xAF8) ^ SecurityKey;
|
||||
set => SetData(Small, BitConverter.GetBytes(value ^ SecurityKey), 0xAF8);
|
||||
}
|
||||
|
||||
public override uint SecurityKey
|
||||
{
|
||||
get => BitConverter.ToUInt32(Small, 0xF20);
|
||||
set => SetData(Small, BitConverter.GetBytes(value), 0xF20);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Large
|
||||
public override int PartyCount { get => Large[0x234]; protected set => Large[0x234] = (byte)value; }
|
||||
public override int GetPartyOffset(int slot) => 0x238 + (SIZE_PARTY * slot);
|
||||
|
||||
public override uint Money
|
||||
{
|
||||
get => BitConverter.ToUInt32(Large, 0x0290) ^ SecurityKey;
|
||||
set => SetData(BitConverter.GetBytes(value ^ SecurityKey), 0x0290);
|
||||
}
|
||||
|
||||
public override uint Coin
|
||||
{
|
||||
get => BitConverter.ToUInt16(Large, 0x0294) ^ SecurityKey;
|
||||
set => SetData(BitConverter.GetBytes(value ^ SecurityKey), 0x0294);
|
||||
}
|
||||
|
||||
private const int OFS_PCItem = 0x0298;
|
||||
private const int OFS_PouchHeldItem = 0x0310;
|
||||
private const int OFS_PouchKeyItem = 0x03B8;
|
||||
private const int OFS_PouchBalls = 0x0430;
|
||||
private const int OFS_PouchTMHM = 0x0464;
|
||||
private const int OFS_PouchBerry = 0x054C;
|
||||
|
||||
protected override InventoryPouch3[] GetItems()
|
||||
{
|
||||
const int max = 999;
|
||||
var PCItems = ArrayUtil.ConcatAll(Legal.Pouch_Items_RS, Legal.Pouch_Key_FRLG, Legal.Pouch_Ball_RS, Legal.Pouch_HM_RS, Legal.Pouch_Berries_RS);
|
||||
return new InventoryPouch3[]
|
||||
{
|
||||
new(InventoryType.Items, Legal.Pouch_Items_RS, max, OFS_PouchHeldItem, (OFS_PouchKeyItem - OFS_PouchHeldItem) / 4),
|
||||
new(InventoryType.KeyItems, Legal.Pouch_Key_FRLG, 1, OFS_PouchKeyItem, (OFS_PouchBalls - OFS_PouchKeyItem) / 4),
|
||||
new(InventoryType.Balls, Legal.Pouch_Ball_RS, max, OFS_PouchBalls, (OFS_PouchTMHM - OFS_PouchBalls) / 4),
|
||||
new(InventoryType.TMHMs, Legal.Pouch_HM_RS, max, OFS_PouchTMHM, (OFS_PouchBerry - OFS_PouchTMHM) / 4),
|
||||
new(InventoryType.Berries, Legal.Pouch_Berries_RS, 999, OFS_PouchBerry, 43),
|
||||
new(InventoryType.PCItems, PCItems, 999, OFS_PCItem, (OFS_PouchHeldItem - OFS_PCItem) / 4),
|
||||
};
|
||||
}
|
||||
|
||||
protected override int SeenOffset2 => 0x5F8;
|
||||
protected override int MailOffset => 0x2CD0;
|
||||
|
||||
protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, slot + 1) - 4; // @ end of each pkm slot
|
||||
public override string GetDaycareRNGSeed(int loc) => BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4"); // after the 2nd slot EXP, before the step counter
|
||||
public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes((ushort)Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2));
|
||||
|
||||
#region eBerry
|
||||
private const int OFFSET_EBERRY = 0x30EC;
|
||||
private const int SIZE_EBERRY = 0x134;
|
||||
|
||||
public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY);
|
||||
public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY);
|
||||
|
||||
public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7);
|
||||
public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF;
|
||||
#endregion
|
||||
|
||||
protected override int SeenOffset3 => 0x3A18;
|
||||
|
||||
public string RivalName
|
||||
{
|
||||
get => GetString(Large, 0x3A4C, 8);
|
||||
set => SetData(SetString(value, 7), 0x3A4C);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
144
PKHeX.Core/Saves/SAV3RS.cs
Normal file
144
PKHeX.Core/Saves/SAV3RS.cs
Normal file
|
@ -0,0 +1,144 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
public sealed class SAV3RS : SAV3, IGen3Hoenn
|
||||
{
|
||||
// Configuration
|
||||
protected override SaveFile CloneInternal() => new SAV3RS(Write());
|
||||
public override GameVersion Version { get => GameVersion.RS; protected set { } }
|
||||
public override PersonalTable Personal => PersonalTable.RS;
|
||||
|
||||
protected override int EventFlagMax => 8 * 288;
|
||||
protected override int EventConstMax => 0x100;
|
||||
protected override int DaycareSlotSize => SIZE_STORED;
|
||||
public override int DaycareSeedSize => 4; // 16bit
|
||||
protected override int EggEventFlag => 0x86;
|
||||
protected override int BadgeFlagStart => 0x807;
|
||||
|
||||
public SAV3RS(byte[] data) : base(data) => Initialize();
|
||||
public SAV3RS(bool japanese = false) : base(japanese) => Initialize();
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
// small
|
||||
PokeDex = 0x18;
|
||||
|
||||
// large
|
||||
EventFlag = 0x1220;
|
||||
EventConst = 0x1340;
|
||||
DaycareOffset = 0x2F9C;
|
||||
|
||||
// storage
|
||||
Box = 0;
|
||||
}
|
||||
|
||||
#region Small
|
||||
public override bool NationalDex
|
||||
{
|
||||
get => PokedexNationalMagicRSE == PokedexNationalUnlockRSE;
|
||||
set
|
||||
{
|
||||
PokedexMode = value ? 1 : 0; // mode
|
||||
PokedexNationalMagicRSE = value ? PokedexNationalUnlockRSE : 0; // magic
|
||||
SetEventFlag(0x836, value);
|
||||
SetEventConst(0x46, PokedexNationalUnlockWorkRSE);
|
||||
}
|
||||
}
|
||||
|
||||
public override uint SecurityKey { get => 0; set { } }
|
||||
|
||||
public RTC3 ClockInitial
|
||||
{
|
||||
get => new(GetData(Small, 0x98, RTC3.Size));
|
||||
set => SetData(Small, value.Data, 0x98);
|
||||
}
|
||||
|
||||
public RTC3 ClockElapsed
|
||||
{
|
||||
get => new(GetData(Small, 0xA0, RTC3.Size));
|
||||
set => SetData(Small, value.Data, 0xA0);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Large
|
||||
public override int PartyCount { get => Large[0x34]; protected set => Large[0x34] = (byte)value; }
|
||||
public override int GetPartyOffset(int slot) => 0x38 + (SIZE_PARTY * slot);
|
||||
|
||||
public override uint Money
|
||||
{
|
||||
get => BitConverter.ToUInt32(Large, 0x0490);
|
||||
set => SetData(BitConverter.GetBytes(value), 0x0490);
|
||||
}
|
||||
|
||||
public override uint Coin
|
||||
{
|
||||
get => BitConverter.ToUInt16(Large, 0x0494);
|
||||
set => SetData(BitConverter.GetBytes(value), 0x0494);
|
||||
}
|
||||
|
||||
private const int OFS_PCItem = 0x0498;
|
||||
private const int OFS_PouchHeldItem = 0x0560;
|
||||
private const int OFS_PouchKeyItem = 0x05B0;
|
||||
private const int OFS_PouchBalls = 0x0600;
|
||||
private const int OFS_PouchTMHM = 0x0640;
|
||||
private const int OFS_PouchBerry = 0x0740;
|
||||
|
||||
protected override InventoryPouch3[] GetItems()
|
||||
{
|
||||
const int max = 99;
|
||||
var PCItems = ArrayUtil.ConcatAll(Legal.Pouch_Items_RS, Legal.Pouch_Key_RS, Legal.Pouch_Ball_RS, Legal.Pouch_HM_RS, Legal.Pouch_Berries_RS);
|
||||
return new InventoryPouch3[]
|
||||
{
|
||||
new(InventoryType.Items, Legal.Pouch_Items_RS, max, OFS_PouchHeldItem, (OFS_PouchKeyItem - OFS_PouchHeldItem) / 4),
|
||||
new(InventoryType.KeyItems, Legal.Pouch_Key_RS, 1, OFS_PouchKeyItem, (OFS_PouchBalls - OFS_PouchKeyItem) / 4),
|
||||
new(InventoryType.Balls, Legal.Pouch_Ball_RS, max, OFS_PouchBalls, (OFS_PouchTMHM - OFS_PouchBalls) / 4),
|
||||
new(InventoryType.TMHMs, Legal.Pouch_HM_RS, max, OFS_PouchTMHM, (OFS_PouchBerry - OFS_PouchTMHM) / 4),
|
||||
new(InventoryType.Berries, Legal.Pouch_Berries_RS, 999, OFS_PouchBerry, 46),
|
||||
new(InventoryType.PCItems, PCItems, 999, OFS_PCItem, (OFS_PouchHeldItem - OFS_PCItem) / 4),
|
||||
};
|
||||
}
|
||||
|
||||
public PokeBlock3Case PokeBlocks
|
||||
{
|
||||
get => new(Large, 0x7F8);
|
||||
set => SetData(Large, value.Write(), 0x7F8);
|
||||
}
|
||||
|
||||
protected override int SeenOffset2 => 0x938;
|
||||
|
||||
public DecorationInventory3 Decorations
|
||||
{
|
||||
get => Large.Slice(0x26A0, DecorationInventory3.SIZE).ToStructure<DecorationInventory3>();
|
||||
set => SetData(Large, value.ToBytes(), 0x26A0);
|
||||
}
|
||||
|
||||
protected override int MailOffset => 0x2B4C;
|
||||
|
||||
protected override int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(0, 2) + (2 * 0x38) + (4 * slot); // consecutive vals, after both consecutive slots & 2 mail
|
||||
public override string GetDaycareRNGSeed(int loc) => BitConverter.ToUInt16(Large, GetDaycareEXPOffset(2)).ToString("X4");
|
||||
public override void SetDaycareRNGSeed(int loc, string seed) => BitConverter.GetBytes((ushort)Util.GetHexValue(seed)).CopyTo(Large, GetDaycareEXPOffset(2));
|
||||
|
||||
private const int ExternalEventFlags = 0x312F;
|
||||
|
||||
public bool HasReceivedWishmkrJirachi
|
||||
{
|
||||
get => GetFlag(ExternalEventFlags + 2, 0);
|
||||
set => SetFlag(ExternalEventFlags + 2, 0, value);
|
||||
}
|
||||
|
||||
#region eBerry
|
||||
private const int OFFSET_EBERRY = 0x3160;
|
||||
private const int SIZE_EBERRY = 0x530;
|
||||
|
||||
public byte[] GetEReaderBerry() => Large.Slice(OFFSET_EBERRY, SIZE_EBERRY);
|
||||
public void SetEReaderBerry(byte[] data) => SetData(Large, data, OFFSET_EBERRY);
|
||||
|
||||
public override string EBerryName => GetString(Large, OFFSET_EBERRY, 7);
|
||||
public override bool IsEBerryEngima => Large[OFFSET_EBERRY] is 0 or 0xFF;
|
||||
#endregion
|
||||
|
||||
protected override int SeenOffset3 => 0x3A8C;
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -19,13 +19,16 @@ namespace PKHeX.Core
|
|||
|
||||
private static int Language { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the most recently loaded Generation 3 Save File has a proper Enigma Berry that matches known distributions.
|
||||
/// </summary>
|
||||
public static EReaderBerryMatch GetStatus()
|
||||
{
|
||||
if (IsEnigma) // no e-Reader Berry data provided, can't hold berry.
|
||||
return NoData;
|
||||
|
||||
var matchUSA = EReaderBerriesNames_USA.Contains(Name);
|
||||
if (matchUSA)
|
||||
var name = Name;
|
||||
if (EReaderBerriesNames_USA.Contains(name))
|
||||
{
|
||||
return Language switch
|
||||
{
|
||||
|
@ -34,9 +37,7 @@ namespace PKHeX.Core
|
|||
_ => InvalidUSA
|
||||
};
|
||||
}
|
||||
|
||||
var matchJP = EReaderBerriesNames_JP.Contains(Name);
|
||||
if (matchJP)
|
||||
if (EReaderBerriesNames_JP.Contains(name))
|
||||
{
|
||||
return Language switch
|
||||
{
|
||||
|
@ -45,7 +46,6 @@ namespace PKHeX.Core
|
|||
_ => InvalidJPN
|
||||
};
|
||||
}
|
||||
|
||||
return NoMatch;
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,17 @@ namespace PKHeX.Core
|
|||
|
||||
public static void LoadFrom(SAV3 sav3)
|
||||
{
|
||||
IsEnigma = sav3.IsEBerryIsEnigma;
|
||||
Name = sav3.EBerryName;
|
||||
Language = sav3.Japanese ? (int)LanguageID.Japanese : (int)LanguageID.English;
|
||||
if (sav3.IsEBerryEngima)
|
||||
{
|
||||
IsEnigma = true;
|
||||
Name = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsEnigma = false;
|
||||
Name = sav3.EBerryName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
14
PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs
Normal file
14
PKHeX.Core/Saves/Substructures/Gen3/IGen3Hoenn.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties common to RS & Emerald save files.
|
||||
/// </summary>
|
||||
public interface IGen3Hoenn
|
||||
{
|
||||
RTC3 ClockInitial { get; set; }
|
||||
RTC3 ClockElapsed { get; set; }
|
||||
PokeBlock3Case PokeBlocks { get; set; }
|
||||
DecorationInventory3 Decorations { get; set; }
|
||||
bool HasReceivedWishmkrJirachi { get; set; }
|
||||
}
|
||||
}
|
14
PKHeX.Core/Saves/Substructures/Gen3/IGen3Joyful.cs
Normal file
14
PKHeX.Core/Saves/Substructures/Gen3/IGen3Joyful.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace PKHeX.Core
|
||||
{
|
||||
public interface IGen3Joyful
|
||||
{
|
||||
ushort JoyfulJumpInRow { get; set; }
|
||||
ushort JoyfulJump5InRow { get; set; }
|
||||
ushort JoyfulJumpGamesMaxPlayers { get; set; }
|
||||
uint JoyfulJumpScore { get; set; }
|
||||
|
||||
uint JoyfulBerriesScore { get; set; }
|
||||
ushort JoyfulBerriesInRow { get; set; }
|
||||
ushort JoyfulBerries5InRow { get; set; }
|
||||
}
|
||||
}
|
|
@ -289,11 +289,40 @@ namespace PKHeX.Core
|
|||
if (Array.TrueForAll(order, v => v == 0)) // all blocks are 0
|
||||
continue;
|
||||
// Detect RS/E/FRLG
|
||||
return SAV3.GetVersion(data, (blockSize * block0) + ofs);
|
||||
return GetVersionG3SAV(data, (blockSize * block0) + ofs);
|
||||
}
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the input <see cref="data"/> to see which game is for this file.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to check</param>
|
||||
/// <param name="offset">Offset for the start of the first Small chunk.</param>
|
||||
/// <returns>RS, E, or FR/LG.</returns>
|
||||
private static GameVersion GetVersionG3SAV(byte[] data, int offset = 0)
|
||||
{
|
||||
// 0xAC
|
||||
// RS: Battle Tower Data, which will never match the FR/LG fixed value.
|
||||
// E: Encryption Key
|
||||
// FR/LG @ 0xAC has a fixed value (01 00 00 00)
|
||||
// RS has battle tower data (variable)
|
||||
uint _0xAC = BitConverter.ToUInt32(data, offset + 0xAC);
|
||||
switch (_0xAC)
|
||||
{
|
||||
case 1: return FRLG; // fixed value
|
||||
case 0: return RS; // save has no battle tower record data
|
||||
default:
|
||||
// RS data structure only extends 0x890 bytes; check if any data is present afterwards.
|
||||
for (int i = 0x890; i < 0xF2C; i += 4)
|
||||
{
|
||||
if (BitConverter.ToUInt64(data, offset + i) != 0)
|
||||
return E;
|
||||
}
|
||||
return RS;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Checks to see if the data belongs to a Gen3 Box RS 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>
|
||||
|
@ -547,7 +576,10 @@ namespace PKHeX.Core
|
|||
// Main Games
|
||||
RBY => new SAV1(data, type),
|
||||
GS or C => new SAV2(data, type),
|
||||
RS or E or FRLG => new SAV3(data, type),
|
||||
|
||||
RS => new SAV3RS(data),
|
||||
E => new SAV3E(data),
|
||||
FRLG => new SAV3FRLG(data),
|
||||
|
||||
DP => new SAV4DP(data),
|
||||
Pt => new SAV4Pt(data),
|
||||
|
@ -684,10 +716,9 @@ namespace PKHeX.Core
|
|||
C or GSC => new SAV2(version: C, lang: language),
|
||||
Stadium2 => new SAV2Stadium(language == LanguageID.Japanese),
|
||||
|
||||
R or S or E or FR or LG => new SAV3(version: game, language == LanguageID.Japanese),
|
||||
RS => new SAV3(version: R, language == LanguageID.Japanese),
|
||||
RSE => new SAV3(version: E, language == LanguageID.Japanese),
|
||||
FRLG => new SAV3(version: FR, language == LanguageID.Japanese),
|
||||
R or S or RS => new SAV3RS(language == LanguageID.Japanese),
|
||||
E or RSE => new SAV3E(language == LanguageID.Japanese),
|
||||
FR or LG or FRLG => new SAV3FRLG(language == LanguageID.Japanese),
|
||||
|
||||
CXD or COLO => new SAV3Colosseum(),
|
||||
XD => new SAV3XD(),
|
||||
|
@ -778,13 +809,9 @@ namespace PKHeX.Core
|
|||
/// <returns>New <see cref="SaveFile"/> object.</returns>
|
||||
public static SAV3 GetG3SaveOverride(SaveFile sav, GameVersion ver) => ver switch // Reset save file info
|
||||
{
|
||||
R => new SAV3(sav.State.BAK, RS),
|
||||
S => new SAV3(sav.State.BAK, RS),
|
||||
RS => new SAV3(sav.State.BAK, RS),
|
||||
E => new SAV3(sav.State.BAK, E),
|
||||
FRLG => new SAV3(sav.State.BAK, FRLG),
|
||||
FR => new SAV3(sav.State.BAK, FRLG),
|
||||
LG => new SAV3(sav.State.BAK, FRLG),
|
||||
R or S or RS => new SAV3RS(sav.State.BAK),
|
||||
E => new SAV3E(sav.State.BAK),
|
||||
FR or LG or FRLG => new SAV3FRLG(sav.State.BAK),
|
||||
_ => throw new ArgumentException(nameof(ver))
|
||||
};
|
||||
|
||||
|
|
|
@ -1076,7 +1076,7 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
B_OpenHoneyTreeEditor.Visible = B_OpenUGSEditor.Visible = sav is SAV4Sinnoh;
|
||||
B_OpenApricorn.Visible = sav is SAV4HGSS;
|
||||
B_OpenRTCEditor.Visible = sav.Generation == 2 || (sav is SAV3 s3 && (s3.RS || s3.E));
|
||||
B_OpenRTCEditor.Visible = sav.Generation == 2 || sav is IGen3Hoenn;
|
||||
B_MailBox.Visible = sav is SAV2 or SAV3 or SAV4 or SAV5;
|
||||
|
||||
B_Raids.Visible = sav is SAV8SWSH;
|
||||
|
|
|
@ -867,7 +867,7 @@ namespace PKHeX.WinForms
|
|||
|
||||
if (sav.State.Exportable && sav is SAV3 s3)
|
||||
{
|
||||
if (s3.IndeterminateGame || ModifierKeys == Keys.Control)
|
||||
if (ModifierKeys == Keys.Control)
|
||||
{
|
||||
var g = new[] { GameVersion.R, GameVersion.S, GameVersion.E, GameVersion.FR, GameVersion.LG };
|
||||
var games = g.Select(z => GameInfo.VersionDataSource.First(v => v.Value == (int)z));
|
||||
|
@ -876,14 +876,14 @@ namespace PKHeX.WinForms
|
|||
dialog.ShowDialog();
|
||||
|
||||
sav = SaveUtil.GetG3SaveOverride(sav, dialog.Result);
|
||||
if (sav.Version == GameVersion.FRLG)
|
||||
if (s3 is SAV3FRLG frlg)
|
||||
{
|
||||
bool result = s3.ResetPersonal(dialog.Result);
|
||||
bool result = frlg.ResetPersonal(dialog.Result);
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (sav.Version == GameVersion.FRLG) // IndeterminateSubVersion
|
||||
else if (s3 is SAV3FRLG frlg) // IndeterminateSubVersion
|
||||
{
|
||||
string fr = GameInfo.GetVersionName(GameVersion.FR);
|
||||
string lg = GameInfo.GetVersionName(GameVersion.LG);
|
||||
|
@ -893,7 +893,7 @@ namespace PKHeX.WinForms
|
|||
var msg = string.Format(dual, "3", fr, lg);
|
||||
using var dialog = new SAV_GameSelect(games, msg, MsgFileLoadSaveSelectVersion);
|
||||
dialog.ShowDialog();
|
||||
bool result = s3.ResetPersonal(dialog.Result);
|
||||
bool result = frlg.ResetPersonal(dialog.Result);
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,12 +19,12 @@ namespace PKHeX.WinForms
|
|||
|
||||
LoadRecords();
|
||||
|
||||
if (SAV.FRLG || SAV.E)
|
||||
ReadJoyful();
|
||||
if (SAV is IGen3Joyful j)
|
||||
ReadJoyful(j);
|
||||
else
|
||||
tabControl1.Controls.Remove(TAB_Joyful);
|
||||
|
||||
if (SAV.E)
|
||||
if (SAV is SAV3E)
|
||||
{
|
||||
ReadFerry();
|
||||
ReadBattleFrontier();
|
||||
|
@ -35,9 +35,9 @@ namespace PKHeX.WinForms
|
|||
tabControl1.Controls.Remove(TAB_BF);
|
||||
}
|
||||
|
||||
if (SAV.FRLG)
|
||||
if (SAV is SAV3FRLG frlg)
|
||||
{
|
||||
TB_RivalName.Text = SAV.GetString(SAV.Large, 0x3A4C, 8);
|
||||
TB_RivalName.Text = frlg.RivalName;
|
||||
|
||||
// Trainer Card Species
|
||||
ComboBox[] cba = { CB_TCM1, CB_TCM2, CB_TCM3, CB_TCM4, CB_TCM5, CB_TCM6 };
|
||||
|
@ -59,22 +59,22 @@ namespace PKHeX.WinForms
|
|||
|
||||
private void B_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (tabControl1.Controls.Contains(TAB_Joyful))
|
||||
SaveJoyful();
|
||||
if (tabControl1.Controls.Contains(TAB_Joyful) && SAV is IGen3Joyful j)
|
||||
SaveJoyful(j);
|
||||
if (tabControl1.Controls.Contains(TAB_Ferry))
|
||||
SaveFerry();
|
||||
if (tabControl1.Controls.Contains(TAB_BF))
|
||||
SaveBattleFrontier();
|
||||
if (SAV.FRLG)
|
||||
if (SAV is SAV3FRLG frlg)
|
||||
{
|
||||
SAV.SetData(SAV.Large, SAV.SetString(TB_RivalName.Text, TB_RivalName.MaxLength), 0x3A4C);
|
||||
frlg.RivalName = TB_RivalName.Text;
|
||||
ComboBox[] cba = { CB_TCM1, CB_TCM2, CB_TCM3, CB_TCM4, CB_TCM5, CB_TCM6 };
|
||||
for (int i = 0; i < cba.Length; i++)
|
||||
SAV.SetEventConst(0x43 + i, (ushort)(int)cba[i].SelectedValue);
|
||||
}
|
||||
|
||||
if (!SAV.RS)
|
||||
SAV.BP = (ushort)NUD_BP.Value;
|
||||
if (SAV is SAV3E se)
|
||||
se.BP = (ushort)NUD_BP.Value;
|
||||
SAV.Coin = (ushort)NUD_Coins.Value;
|
||||
|
||||
Origin.CopyChangesFrom(SAV);
|
||||
|
@ -84,48 +84,24 @@ namespace PKHeX.WinForms
|
|||
private void B_Cancel_Click(object sender, EventArgs e) => Close();
|
||||
|
||||
#region Joyful
|
||||
private int JUMPS_IN_ROW, JUMPS_SCORE, JUMPS_5_IN_ROW;
|
||||
private int BERRIES_IN_ROW, BERRIES_SCORE, BERRIES_5_IN_ROW;
|
||||
|
||||
private void ReadJoyful()
|
||||
private void ReadJoyful(IGen3Joyful j)
|
||||
{
|
||||
switch (SAV.Version)
|
||||
{
|
||||
case GameVersion.E:
|
||||
JUMPS_IN_ROW = 0x1fc;
|
||||
JUMPS_SCORE = 0x208;
|
||||
JUMPS_5_IN_ROW = 0x200;
|
||||
|
||||
BERRIES_IN_ROW = 0x210;
|
||||
BERRIES_SCORE = 0x20c;
|
||||
BERRIES_5_IN_ROW = 0x212;
|
||||
break;
|
||||
case GameVersion.FRLG:
|
||||
JUMPS_IN_ROW = 0xB00;
|
||||
JUMPS_SCORE = 0xB0C;
|
||||
JUMPS_5_IN_ROW = 0xB04;
|
||||
|
||||
BERRIES_IN_ROW = 0xB14;
|
||||
BERRIES_SCORE = 0xB10;
|
||||
BERRIES_5_IN_ROW = 0xB16;
|
||||
break;
|
||||
}
|
||||
TB_J1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_IN_ROW)).ToString();
|
||||
TB_J2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_SCORE)).ToString();
|
||||
TB_J3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, JUMPS_5_IN_ROW)).ToString();
|
||||
TB_B1.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_IN_ROW)).ToString();
|
||||
TB_B2.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_SCORE)).ToString();
|
||||
TB_B3.Text = Math.Min((ushort)9999, BitConverter.ToUInt16(SAV.Small, BERRIES_5_IN_ROW)).ToString();
|
||||
TB_J1.Text = Math.Min((ushort)9999, j.JoyfulJumpInRow).ToString();
|
||||
TB_J2.Text = Math.Min( 9999, j.JoyfulJumpScore).ToString();
|
||||
TB_J3.Text = Math.Min((ushort)9999, j.JoyfulJump5InRow).ToString();
|
||||
TB_B1.Text = Math.Min((ushort)9999, j.JoyfulBerriesInRow).ToString();
|
||||
TB_B2.Text = Math.Min( 9999, j.JoyfulBerriesScore).ToString();
|
||||
TB_B3.Text = Math.Min((ushort)9999, j.JoyfulBerries5InRow).ToString();
|
||||
}
|
||||
|
||||
private void SaveJoyful()
|
||||
private void SaveJoyful(IGen3Joyful j)
|
||||
{
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J1.Text)).CopyTo(SAV.Small, JUMPS_IN_ROW);
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J2.Text)).CopyTo(SAV.Small, JUMPS_SCORE);
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_J3.Text)).CopyTo(SAV.Small, JUMPS_5_IN_ROW);
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B1.Text)).CopyTo(SAV.Small, BERRIES_IN_ROW);
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B2.Text)).CopyTo(SAV.Small, BERRIES_SCORE);
|
||||
BitConverter.GetBytes((ushort)Util.ToUInt32(TB_B3.Text)).CopyTo(SAV.Small, BERRIES_5_IN_ROW);
|
||||
j.JoyfulJumpInRow = (ushort)Util.ToUInt32(TB_J1.Text);
|
||||
j.JoyfulJumpScore = (ushort)Util.ToUInt32(TB_J2.Text);
|
||||
j.JoyfulJump5InRow = (ushort)Util.ToUInt32(TB_J3.Text);
|
||||
j.JoyfulBerriesInRow = (ushort)Util.ToUInt32(TB_B1.Text);
|
||||
j.JoyfulBerriesScore = (ushort)Util.ToUInt32(TB_B2.Text);
|
||||
j.JoyfulBerries5InRow = (ushort)Util.ToUInt32(TB_B3.Text);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
@ -468,11 +444,11 @@ namespace PKHeX.WinForms
|
|||
LoadFame(val);
|
||||
};
|
||||
|
||||
if (!SAV.RS)
|
||||
if (SAV is SAV3E em)
|
||||
{
|
||||
NUD_BP.Value = Math.Min(NUD_BP.Maximum, SAV.BP);
|
||||
NUD_BPEarned.Value = SAV.BPEarned;
|
||||
NUD_BPEarned.ValueChanged += (s, e) => SAV.BPEarned = (uint)NUD_BPEarned.Value;
|
||||
NUD_BP.Value = Math.Min(NUD_BP.Maximum, em.BP);
|
||||
NUD_BPEarned.Value = em.BPEarned;
|
||||
NUD_BPEarned.ValueChanged += (s, e) => em.BPEarned = (uint)NUD_BPEarned.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -7,13 +7,13 @@ namespace PKHeX.WinForms
|
|||
public partial class SAV_RTC3 : Form
|
||||
{
|
||||
private readonly SaveFile Origin;
|
||||
private readonly SAV3 SAV;
|
||||
private readonly IGen3Hoenn SAV;
|
||||
|
||||
public SAV_RTC3(SaveFile sav)
|
||||
{
|
||||
InitializeComponent();
|
||||
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
||||
SAV = (SAV3)(Origin = sav).Clone();
|
||||
SAV = (IGen3Hoenn)(Origin = sav).Clone();
|
||||
|
||||
ClockInitial = SAV.ClockInitial;
|
||||
ClockElapsed = SAV.ClockElapsed;
|
||||
|
@ -56,7 +56,7 @@ namespace PKHeX.WinForms
|
|||
SAV.ClockInitial = ClockInitial;
|
||||
SAV.ClockElapsed = ClockElapsed;
|
||||
|
||||
Origin.CopyChangesFrom(SAV);
|
||||
Origin.CopyChangesFrom((SaveFile)SAV);
|
||||
Close();
|
||||
}
|
||||
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace PKHeX.Tests.Simulator
|
|||
var pk7 = new PK7 { Species = set.Species, Form = set.Form, Moves = set.Moves, CurrentLevel = set.Level };
|
||||
var encs = EncounterMovesetGenerator.GenerateEncounters(pk7, set.Moves);
|
||||
var tr3 = encs.First(z => z is EncounterTrade3);
|
||||
var pk3 = tr3.ConvertToPKM(new SAV3());
|
||||
var pk3 = tr3.ConvertToPKM(new SAV3FRLG());
|
||||
|
||||
var la = new LegalityAnalysis(pk3);
|
||||
la.Valid.Should().BeTrue();
|
||||
|
|
Loading…
Reference in a new issue