2016-08-27 11:33:21 +00:00
|
|
|
|
using System;
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
|
using System.Collections.Generic;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2017-01-08 07:54:09 +00:00
|
|
|
|
namespace PKHeX.Core
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2017-10-24 06:12:58 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generation 1 <see cref="SaveFile"/> object.
|
|
|
|
|
/// </summary>
|
2019-07-14 22:06:45 +00:00
|
|
|
|
public sealed class SAV1 : SaveFile, ILangDeviantSave
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2020-12-05 13:36:23 +00:00
|
|
|
|
protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}";
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override string Extension => ".sav";
|
2020-09-26 20:30:17 +00:00
|
|
|
|
|
|
|
|
|
public int SaveRevision => Japanese ? 0 : 1;
|
|
|
|
|
public string SaveRevisionString => Japanese ? "J" : "U";
|
2019-07-14 22:06:45 +00:00
|
|
|
|
public bool Japanese { get; }
|
|
|
|
|
public bool Korean => false;
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
|
public override PersonalTable Personal { get; }
|
|
|
|
|
public override IReadOnlyList<ushort> HeldItems => Array.Empty<ushort>();
|
|
|
|
|
|
2021-08-06 05:39:38 +00:00
|
|
|
|
public override IReadOnlyList<string> PKMExtensions => Array.FindAll(PKM.Extensions, f =>
|
2017-01-05 06:22:50 +00:00
|
|
|
|
{
|
2021-07-27 06:33:56 +00:00
|
|
|
|
int gen = f[^1] - 0x30;
|
2020-12-29 08:37:59 +00:00
|
|
|
|
return gen is 1 or 2;
|
2021-08-06 05:39:38 +00:00
|
|
|
|
});
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2019-07-21 19:30:21 +00:00
|
|
|
|
public SAV1(GameVersion version = GameVersion.RBY, bool japanese = false) : base(SaveUtil.SIZE_G1RAW)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2019-07-21 19:30:21 +00:00
|
|
|
|
Version = version;
|
2019-06-09 02:56:11 +00:00
|
|
|
|
Japanese = japanese;
|
|
|
|
|
Offsets = Japanese ? SAV1Offsets.JPN : SAV1Offsets.INT;
|
2021-06-10 05:56:19 +00:00
|
|
|
|
Personal = version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB;
|
2019-07-21 19:30:21 +00:00
|
|
|
|
Initialize(version);
|
2019-06-09 02:56:11 +00:00
|
|
|
|
ClearBoxes();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SAV1(byte[] data, GameVersion versionOverride = GameVersion.Any) : base(data)
|
|
|
|
|
{
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
|
Japanese = SaveUtil.GetIsG1SAVJ(Data);
|
|
|
|
|
Offsets = Japanese ? SAV1Offsets.JPN : SAV1Offsets.INT;
|
|
|
|
|
|
2019-06-09 02:56:11 +00:00
|
|
|
|
Version = versionOverride != GameVersion.Any ? versionOverride : SaveUtil.GetIsG1SAV(data);
|
2021-06-10 05:56:19 +00:00
|
|
|
|
Personal = Version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
if (Version == GameVersion.Invalid)
|
|
|
|
|
return;
|
2018-08-07 04:29:47 +00:00
|
|
|
|
|
2019-06-09 02:56:11 +00:00
|
|
|
|
Initialize(versionOverride);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Initialize(GameVersion versionOverride)
|
|
|
|
|
{
|
2019-01-22 04:06:02 +00:00
|
|
|
|
// see if RBY can be differentiated
|
2021-01-04 02:11:59 +00:00
|
|
|
|
if (Starter != 0 && versionOverride is not GameVersion.RB or GameVersion.YW)
|
2017-10-23 00:11:48 +00:00
|
|
|
|
Version = Yellow ? GameVersion.YW : GameVersion.RB;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2016-08-28 10:18:22 +00:00
|
|
|
|
Box = Data.Length;
|
|
|
|
|
Array.Resize(ref Data, Data.Length + SIZE_RESERVED);
|
2017-06-18 01:37:19 +00:00
|
|
|
|
Party = GetPartyOffset(0);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2016-08-28 10:18:22 +00:00
|
|
|
|
// Stash boxes after the save file's end.
|
2018-08-06 00:28:54 +00:00
|
|
|
|
int stored = SIZE_STOREDBOX;
|
|
|
|
|
int baseDest = Data.Length - SIZE_RESERVED;
|
|
|
|
|
var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;
|
2016-08-28 10:18:22 +00:00
|
|
|
|
for (int i = 0; i < BoxCount; i++)
|
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
int ofs = GetBoxRawDataOffset(i);
|
|
|
|
|
var box = GetData(ofs, stored);
|
|
|
|
|
var boxDest = baseDest + (i * SIZE_BOX);
|
|
|
|
|
var boxPL = new PokeList1(box, capacity, Japanese);
|
|
|
|
|
for (int j = 0; j < boxPL.Pokemon.Length; j++)
|
2016-08-28 10:18:22 +00:00
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var dest = boxDest + (j * SIZE_STORED);
|
|
|
|
|
var pkDat = (j < boxPL.Count)
|
|
|
|
|
? new PokeList1(boxPL[j]).Write()
|
|
|
|
|
: new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
|
|
|
|
|
pkDat.CopyTo(Data, dest);
|
2016-08-28 10:18:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-06 03:27:25 +00:00
|
|
|
|
var current = GetData(Offsets.CurrentBox, SIZE_STOREDBOX);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var curBoxPL = new PokeList1(current, capacity, Japanese);
|
2016-08-28 10:18:22 +00:00
|
|
|
|
for (int i = 0; i < curBoxPL.Pokemon.Length; i++)
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
var dest = Data.Length - SIZE_RESERVED + (CurrentBox * SIZE_BOX) + (i * SIZE_STORED);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var pkDat = i < curBoxPL.Count
|
|
|
|
|
? new PokeList1(curBoxPL[i]).Write()
|
|
|
|
|
: new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
|
|
|
|
|
pkDat.CopyTo(Data, dest);
|
2016-08-28 10:18:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-06 03:27:25 +00:00
|
|
|
|
var party = GetData(Offsets.Party, SIZE_STOREDPARTY);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var partyPL = new PokeList1(party, PokeListType.Party, Japanese);
|
|
|
|
|
for (int i = 0; i < partyPL.Pokemon.Length; i++)
|
2016-08-28 10:18:22 +00:00
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var dest = GetPartyOffset(i);
|
|
|
|
|
var pkDat = i < partyPL.Count
|
|
|
|
|
? new PokeList1(partyPL[i]).Write()
|
|
|
|
|
: new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
|
|
|
|
|
pkDat.CopyTo(Data, dest);
|
2016-08-28 10:18:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 20:27:17 +00:00
|
|
|
|
byte[] rawDC = new byte[0x38];
|
2018-08-06 03:27:25 +00:00
|
|
|
|
Array.Copy(Data, Offsets.Daycare, rawDC, 0, rawDC.Length);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
byte[] TempDaycare = new byte[PokeList1.GetDataLength(PokeListType.Single, Japanese)];
|
2016-08-29 20:27:17 +00:00
|
|
|
|
TempDaycare[0] = rawDC[0];
|
2020-01-04 22:48:39 +00:00
|
|
|
|
Array.Copy(rawDC, 1, TempDaycare, 2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, StringLength);
|
|
|
|
|
Array.Copy(rawDC, 1 + StringLength, TempDaycare, 2 + 1 + PokeCrypto.SIZE_1PARTY, StringLength);
|
|
|
|
|
Array.Copy(rawDC, 1 + (2 * StringLength), TempDaycare, 2 + 1, PokeCrypto.SIZE_1STORED);
|
2020-12-22 01:17:56 +00:00
|
|
|
|
PokeList1 daycareList = new(TempDaycare, PokeListType.Single, Japanese);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
daycareList.Write().CopyTo(Data, GetPartyOffset(7));
|
2019-10-19 03:42:03 +00:00
|
|
|
|
DaycareOffset = GetPartyOffset(7);
|
2017-09-24 05:13:48 +00:00
|
|
|
|
|
2018-08-07 14:54:14 +00:00
|
|
|
|
EventFlag = Offsets.EventFlag;
|
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
// Enable Pokedex editing
|
|
|
|
|
PokeDex = 0;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-06 03:27:25 +00:00
|
|
|
|
private readonly SAV1Offsets Offsets;
|
|
|
|
|
|
2017-09-24 05:13:48 +00:00
|
|
|
|
// Event Flags
|
2019-03-30 23:21:45 +00:00
|
|
|
|
protected override int EventFlagMax => 0xA00; // 320 * 8
|
2017-09-24 05:13:48 +00:00
|
|
|
|
protected override int EventConstMax => 0;
|
|
|
|
|
|
2016-08-28 10:18:22 +00:00
|
|
|
|
private const int SIZE_RESERVED = 0x8000; // unpacked box data
|
2017-06-18 01:37:19 +00:00
|
|
|
|
|
2019-02-19 05:59:57 +00:00
|
|
|
|
protected override byte[] GetFinalData()
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var capacity = Japanese ? PokeListType.StoredJP : PokeListType.Stored;
|
2016-08-29 20:27:17 +00:00
|
|
|
|
for (int i = 0; i < BoxCount; i++)
|
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var boxPL = new PokeList1(capacity, Japanese);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
int slot = 0;
|
|
|
|
|
for (int j = 0; j < boxPL.Pokemon.Length; j++)
|
|
|
|
|
{
|
2018-08-06 00:28:54 +00:00
|
|
|
|
PK1 boxPK = (PK1)GetPKM(GetData(GetBoxOffset(i) + (j * SIZE_STORED), SIZE_STORED));
|
2016-08-29 20:27:17 +00:00
|
|
|
|
if (boxPK.Species > 0)
|
|
|
|
|
boxPL[slot++] = boxPK;
|
|
|
|
|
}
|
2018-08-06 00:28:54 +00:00
|
|
|
|
|
|
|
|
|
// copy to box location
|
|
|
|
|
var boxdata = boxPL.Write();
|
|
|
|
|
int ofs = GetBoxRawDataOffset(i);
|
|
|
|
|
SetData(boxdata, ofs);
|
|
|
|
|
|
|
|
|
|
// copy to active loc if current box
|
2016-08-29 20:27:17 +00:00
|
|
|
|
if (i == CurrentBox)
|
2018-08-06 03:27:25 +00:00
|
|
|
|
SetData(boxdata, Offsets.CurrentBox);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-06 00:28:54 +00:00
|
|
|
|
var partyPL = new PokeList1(PokeListType.Party, Japanese);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
int pSlot = 0;
|
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
|
|
|
{
|
2017-06-18 01:37:19 +00:00
|
|
|
|
PK1 partyPK = (PK1)GetPKM(GetData(GetPartyOffset(i), SIZE_STORED));
|
2016-08-29 20:27:17 +00:00
|
|
|
|
if (partyPK.Species > 0)
|
|
|
|
|
partyPL[pSlot++] = partyPK;
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
partyPL.Write().CopyTo(Data, Offsets.Party);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
|
|
|
|
|
// Daycare is read-only, but in case it ever becomes editable, copy it back in.
|
2017-06-18 01:37:19 +00:00
|
|
|
|
byte[] rawDC = GetData(GetDaycareSlotOffset(loc: 0, slot: 0), SIZE_STORED);
|
2020-01-04 22:48:39 +00:00
|
|
|
|
byte[] dc = new byte[1 + (2 * StringLength) + PokeCrypto.SIZE_1STORED];
|
2021-03-29 07:14:44 +00:00
|
|
|
|
dc[0] = IsDaycareOccupied(0, 0) == true ? (byte)1 : (byte)0;
|
2020-01-04 22:48:39 +00:00
|
|
|
|
Array.Copy(rawDC, 2 + 1 + PokeCrypto.SIZE_1PARTY + StringLength, dc, 1, StringLength);
|
|
|
|
|
Array.Copy(rawDC, 2 + 1 + PokeCrypto.SIZE_1PARTY, dc, 1 + StringLength, StringLength);
|
|
|
|
|
Array.Copy(rawDC, 2 + 1, dc, 1 + (2 * StringLength), PokeCrypto.SIZE_1STORED);
|
2018-08-06 03:27:25 +00:00
|
|
|
|
dc.CopyTo(Data, Offsets.Daycare);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
SetChecksums();
|
2016-08-29 20:27:17 +00:00
|
|
|
|
byte[] outData = new byte[Data.Length - SIZE_RESERVED];
|
|
|
|
|
Array.Copy(Data, outData, outData.Length);
|
|
|
|
|
return outData;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-06 03:33:25 +00:00
|
|
|
|
private int GetBoxRawDataOffset(int box)
|
2018-08-06 00:28:54 +00:00
|
|
|
|
{
|
2021-08-06 03:33:25 +00:00
|
|
|
|
if (box < BoxCount / 2)
|
|
|
|
|
return 0x4000 + (box * SIZE_STOREDBOX);
|
|
|
|
|
return 0x6000 + ((box - (BoxCount / 2)) * SIZE_STOREDBOX);
|
2018-08-06 00:28:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
// Configuration
|
2020-12-05 13:36:23 +00:00
|
|
|
|
protected override SaveFile CloneInternal() => new SAV1(Write(), Version);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2020-08-07 23:16:10 +00:00
|
|
|
|
protected override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
2020-01-04 22:48:39 +00:00
|
|
|
|
protected override int SIZE_PARTY => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
2017-01-05 06:22:50 +00:00
|
|
|
|
private int SIZE_BOX => BoxSlotCount*SIZE_STORED;
|
2018-08-06 00:28:54 +00:00
|
|
|
|
private int SIZE_STOREDBOX => PokeList1.GetDataLength(Japanese ? PokeListType.StoredJP : PokeListType.Stored, Japanese);
|
|
|
|
|
private int SIZE_STOREDPARTY => PokeList1.GetDataLength(PokeListType.Party, Japanese);
|
2016-08-29 20:27:17 +00:00
|
|
|
|
|
2019-03-17 01:41:56 +00:00
|
|
|
|
public override PKM BlankPKM => new PK1(Japanese);
|
2016-09-26 23:14:11 +00:00
|
|
|
|
public override Type PKMType => typeof(PK1);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2017-01-27 03:18:20 +00:00
|
|
|
|
public override int MaxMoveID => Legal.MaxMoveID_1;
|
2016-10-24 04:59:27 +00:00
|
|
|
|
public override int MaxSpeciesID => Legal.MaxSpeciesID_1;
|
2017-01-27 03:18:20 +00:00
|
|
|
|
public override int MaxAbilityID => Legal.MaxAbilityID_1;
|
|
|
|
|
public override int MaxItemID => Legal.MaxItemID_1;
|
2017-05-13 03:32:36 +00:00
|
|
|
|
public override int MaxBallID => 0; // unused
|
|
|
|
|
public override int MaxGameID => 99; // unused
|
2016-09-09 03:20:32 +00:00
|
|
|
|
public override int MaxMoney => 999999;
|
2017-02-04 20:13:54 +00:00
|
|
|
|
public override int MaxCoins => 9999;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
|
|
|
|
public override int BoxCount => Japanese ? 8 : 12;
|
|
|
|
|
public override int MaxEV => 65535;
|
2016-08-28 10:18:22 +00:00
|
|
|
|
public override int MaxIV => 15;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int Generation => 1;
|
|
|
|
|
protected override int GiftCountMax => 0;
|
2016-09-04 22:44:54 +00:00
|
|
|
|
public override int OTLength => Japanese ? 5 : 7;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int NickLength => Japanese ? 5 : 10;
|
2016-08-31 05:58:39 +00:00
|
|
|
|
public override int BoxSlotCount => Japanese ? 30 : 20;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
|
|
|
|
public override bool HasParty => true;
|
2021-05-29 22:31:47 +00:00
|
|
|
|
private int StringLength => Japanese ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2019-09-03 02:30:58 +00:00
|
|
|
|
public override bool IsPKMPresent(byte[] data, int offset) => PKX.IsPKMPresentGB(data, offset);
|
2018-03-18 23:22:21 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
// Checksums
|
2019-03-30 23:21:45 +00:00
|
|
|
|
protected override void SetChecksums() => Data[Offsets.ChecksumOfs] = GetRBYChecksum(Offsets.OT, Offsets.ChecksumOfs);
|
|
|
|
|
public override bool ChecksumsValid => Data[Offsets.ChecksumOfs] == GetRBYChecksum(Offsets.OT, Offsets.ChecksumOfs);
|
2018-07-22 02:24:09 +00:00
|
|
|
|
public override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
2019-03-30 23:21:45 +00:00
|
|
|
|
private byte GetRBYChecksum(int start, int end)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-07-22 02:24:09 +00:00
|
|
|
|
byte chksum = 0;
|
2019-03-30 23:21:45 +00:00
|
|
|
|
for (int i = start; i < end; i++)
|
2018-07-22 02:24:09 +00:00
|
|
|
|
chksum += Data[i];
|
|
|
|
|
chksum ^= 0xFF;
|
|
|
|
|
return chksum;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trainer Info
|
|
|
|
|
public override GameVersion Version { get; protected set; }
|
|
|
|
|
|
|
|
|
|
public override string OT
|
|
|
|
|
{
|
2019-03-30 23:21:45 +00:00
|
|
|
|
get => GetString(Offsets.OT, OTLength);
|
|
|
|
|
set => SetString(value, OTLength).CopyTo(Data, Offsets.OT);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2021-06-30 03:58:06 +00:00
|
|
|
|
public Span<byte> OT_Trash { get => Data.AsSpan(Offsets.OT, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.OT)); } }
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int Gender
|
|
|
|
|
{
|
2017-05-13 03:32:36 +00:00
|
|
|
|
get => 0;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
set { }
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2018-04-28 18:06:58 +00:00
|
|
|
|
public override int TID
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => BigEndian.ToUInt16(Data, Offsets.TID);
|
|
|
|
|
set => BigEndian.GetBytes((ushort)value).CopyTo(Data, Offsets.TID);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2018-04-28 18:06:58 +00:00
|
|
|
|
public override int SID { get => 0; set { } }
|
2017-01-05 09:14:19 +00:00
|
|
|
|
|
2021-10-10 23:11:46 +00:00
|
|
|
|
public string Rival
|
|
|
|
|
{
|
|
|
|
|
get => GetString(Offsets.Rival, OTLength);
|
|
|
|
|
set => SetString(value, OTLength).CopyTo(Data, Offsets.Rival);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Span<byte> Rival_Trash { get => Data.AsSpan(Offsets.Rival, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Rival)); } }
|
|
|
|
|
|
2017-09-24 23:58:19 +00:00
|
|
|
|
public bool Yellow => Starter == 0x54; // Pikachu
|
2018-08-06 03:27:25 +00:00
|
|
|
|
public int Starter => Data[Offsets.Starter];
|
|
|
|
|
|
2017-01-05 09:14:19 +00:00
|
|
|
|
public byte PikaFriendship
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PikaFriendship];
|
|
|
|
|
set => Data[Offsets.PikaFriendship] = value;
|
2017-01-05 09:14:19 +00:00
|
|
|
|
}
|
2018-02-21 23:22:43 +00:00
|
|
|
|
|
2019-12-21 23:17:05 +00:00
|
|
|
|
public int PikaBeachScore
|
|
|
|
|
{
|
2021-05-14 23:32:26 +00:00
|
|
|
|
get => BinaryCodedDecimal.ToInt32LE(Data, Offsets.PikaBeachScore, 2);
|
|
|
|
|
set => BinaryCodedDecimal.WriteBytesLE(Data.AsSpan(Offsets.PikaBeachScore, 2), Math.Min(9999, value));
|
2019-12-21 23:17:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-16 19:01:21 +00:00
|
|
|
|
public override string PlayTimeString => !PlayedMaximum ? base.PlayTimeString : $"{base.PlayTimeString} {Checksums.CRC16_CCITT(Data):X4}";
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int PlayedHours
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PlayTime + 0];
|
2018-02-21 23:22:43 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value >= byte.MaxValue) // Set 255:00:00.00 and flag
|
|
|
|
|
{
|
|
|
|
|
PlayedMaximum = true;
|
|
|
|
|
value = byte.MaxValue;
|
|
|
|
|
PlayedMinutes = PlayedSeconds = PlayedFrames = 0;
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
Data[Offsets.PlayTime + 0] = (byte) value;
|
2018-02-21 23:22:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2018-02-21 23:22:43 +00:00
|
|
|
|
public bool PlayedMaximum
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PlayTime + 1] != 0;
|
2021-03-29 07:14:44 +00:00
|
|
|
|
set => Data[Offsets.PlayTime + 1] = value ? (byte)1 : (byte)0;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int PlayedMinutes
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PlayTime + 2];
|
|
|
|
|
set => Data[Offsets.PlayTime + 2] = (byte)value;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int PlayedSeconds
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PlayTime + 3];
|
|
|
|
|
set => Data[Offsets.PlayTime + 3] = (byte)value;
|
2018-02-21 23:22:43 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2018-02-21 23:22:43 +00:00
|
|
|
|
public int PlayedFrames
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.PlayTime + 4];
|
|
|
|
|
set => Data[Offsets.PlayTime + 4] = (byte)value;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
public int Badges
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.Badges];
|
|
|
|
|
set { if (value < 0) return; Data[Offsets.Badges] = (byte)value; }
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
private byte Options
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.Options];
|
|
|
|
|
set => Data[Offsets.Options] = value;
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
public bool BattleEffects
|
|
|
|
|
{
|
2017-05-13 03:32:36 +00:00
|
|
|
|
get => (Options & 0x80) == 0;
|
|
|
|
|
set => Options = (byte)((Options & 0x7F) | (value ? 0 : 0x80));
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
public bool BattleStyleSwitch
|
|
|
|
|
{
|
2017-05-13 03:32:36 +00:00
|
|
|
|
get => (Options & 0x40) == 0;
|
|
|
|
|
set => Options = (byte)((Options & 0xBF) | (value ? 0 : 0x40));
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
public int Sound
|
|
|
|
|
{
|
2017-05-13 03:32:36 +00:00
|
|
|
|
get => (Options & 0x30) >> 4;
|
2019-03-30 23:21:45 +00:00
|
|
|
|
set => Options = (byte)((Options & 0xCF) | ((value & 3) << 4));
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-30 00:48:05 +00:00
|
|
|
|
public int TextSpeed
|
|
|
|
|
{
|
2017-05-13 03:32:36 +00:00
|
|
|
|
get => Options & 0x7;
|
2019-03-30 23:21:45 +00:00
|
|
|
|
set => Options = (byte)((Options & 0xF8) | (value & 7));
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-06-09 21:15:42 +00:00
|
|
|
|
// yellow only
|
|
|
|
|
public byte GBPrinterBrightness { get => Data[Offsets.PrinterBrightness]; set => Data[Offsets.PrinterBrightness] = value; }
|
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override uint Money
|
|
|
|
|
{
|
2021-05-14 23:32:26 +00:00
|
|
|
|
get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Money, 3);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
2017-02-08 23:51:18 +00:00
|
|
|
|
value = (uint)Math.Min(value, MaxMoney);
|
2021-05-14 23:32:26 +00:00
|
|
|
|
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Money, 3), (int)value);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public uint Coin
|
|
|
|
|
{
|
2021-05-14 23:32:26 +00:00
|
|
|
|
get => (uint)BinaryCodedDecimal.ToInt32BE(Data, Offsets.Coin, 2);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
set
|
|
|
|
|
{
|
2017-02-08 23:51:18 +00:00
|
|
|
|
value = (ushort)Math.Min(value, MaxCoins);
|
2021-05-14 23:32:26 +00:00
|
|
|
|
BinaryCodedDecimal.WriteBytesBE(Data.AsSpan(Offsets.Coin, 2), (int)value);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-04 21:00:52 +00:00
|
|
|
|
private readonly ushort[] LegalItems = Legal.Pouch_Items_RBY;
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2020-12-05 14:09:33 +00:00
|
|
|
|
public override IReadOnlyList<InventoryPouch> Inventory
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
ushort[] legalItems = LegalItems;
|
|
|
|
|
InventoryPouch[] pouch =
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
new InventoryPouchGB(InventoryType.Items, legalItems, 99, Offsets.Items, 20),
|
2021-08-20 20:49:20 +00:00
|
|
|
|
new InventoryPouchGB(InventoryType.PCItems, legalItems, 99, Offsets.PCItems, 50),
|
2016-08-27 11:33:21 +00:00
|
|
|
|
};
|
2019-03-30 23:10:14 +00:00
|
|
|
|
return pouch.LoadAll(Data);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2019-03-30 23:21:45 +00:00
|
|
|
|
set => value.SaveAll(Data);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override int GetDaycareSlotOffset(int loc, int slot)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2019-10-19 03:42:03 +00:00
|
|
|
|
return DaycareOffset;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override uint? GetDaycareEXP(int loc, int slot)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override bool? IsDaycareOccupied(int loc, int slot)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-06-29 23:18:42 +00:00
|
|
|
|
if (loc == 0 && slot == 0)
|
2018-08-06 03:27:25 +00:00
|
|
|
|
return Data[Offsets.Daycare] == 0x01;
|
2020-10-18 16:43:41 +00:00
|
|
|
|
return null;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-02-02 18:19:41 +00:00
|
|
|
|
public override void SetDaycareEXP(int loc, int slot, uint EXP)
|
|
|
|
|
{
|
|
|
|
|
// todo
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-02-02 18:19:41 +00:00
|
|
|
|
public override void SetDaycareOccupied(int loc, int slot, bool occupied)
|
|
|
|
|
{
|
|
|
|
|
// todo
|
|
|
|
|
}
|
2016-08-27 11:33:21 +00:00
|
|
|
|
|
|
|
|
|
// Storage
|
|
|
|
|
public override int PartyCount
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.Party];
|
|
|
|
|
protected set => Data[Offsets.Party] = (byte)value;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override int GetBoxOffset(int box)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
return Data.Length - SIZE_RESERVED + (box * SIZE_BOX);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override int GetPartyOffset(int slot)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
return Data.Length - SIZE_RESERVED + (BoxCount * SIZE_BOX) + (slot * SIZE_STORED);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2016-08-27 11:33:21 +00:00
|
|
|
|
public override int CurrentBox
|
|
|
|
|
{
|
2018-08-06 03:27:25 +00:00
|
|
|
|
get => Data[Offsets.CurrentBoxIndex] & 0x7F;
|
|
|
|
|
set => Data[Offsets.CurrentBoxIndex] = (byte)((Data[Offsets.CurrentBoxIndex] & 0x80) | (value & 0x7F));
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override string GetBoxName(int box)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2016-08-31 05:58:39 +00:00
|
|
|
|
return $"BOX {box + 1}";
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override void SetBoxName(int box, string value)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2016-08-28 10:18:22 +00:00
|
|
|
|
// Don't allow for custom box names
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2016-08-28 10:18:22 +00:00
|
|
|
|
|
2019-03-30 02:43:33 +00:00
|
|
|
|
protected override PKM GetPKM(byte[] data)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
2016-08-28 10:18:22 +00:00
|
|
|
|
if (data.Length == SIZE_STORED)
|
2018-08-06 00:28:54 +00:00
|
|
|
|
return new PokeList1(data, PokeListType.Single, Japanese)[0];
|
2016-08-27 11:33:21 +00:00
|
|
|
|
return new PK1(data);
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-03-30 02:43:33 +00:00
|
|
|
|
protected override byte[] DecryptPKM(byte[] data)
|
2016-08-27 11:33:21 +00:00
|
|
|
|
{
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-31 05:58:39 +00:00
|
|
|
|
// Pokédex
|
2017-06-18 01:37:19 +00:00
|
|
|
|
protected override void SetDex(PKM pkm)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
{
|
2017-03-15 05:41:15 +00:00
|
|
|
|
int species = pkm.Species;
|
2017-06-18 01:37:19 +00:00
|
|
|
|
if (!CanSetDex(species))
|
2017-03-15 05:41:15 +00:00
|
|
|
|
return;
|
2016-08-30 00:48:05 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
SetCaught(pkm.Species, true);
|
|
|
|
|
SetSeen(pkm.Species, true);
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
private bool CanSetDex(int species)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
{
|
2017-03-15 05:41:15 +00:00
|
|
|
|
if (species <= 0)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
return false;
|
2017-03-15 05:41:15 +00:00
|
|
|
|
if (species > MaxSpeciesID)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
return false;
|
2018-03-12 01:07:48 +00:00
|
|
|
|
if (Version == GameVersion.Invalid)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
return false;
|
2017-03-15 05:41:15 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-03-30 23:21:45 +00:00
|
|
|
|
public override bool GetSeen(int species) => GetDexFlag(Offsets.DexSeen, species);
|
|
|
|
|
public override bool GetCaught(int species) => GetDexFlag(Offsets.DexCaught, species);
|
|
|
|
|
public override void SetSeen(int species, bool seen) => SetDexFlag(Offsets.DexSeen, species, seen);
|
|
|
|
|
public override void SetCaught(int species, bool caught) => SetDexFlag(Offsets.DexCaught, species, caught);
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-03-30 23:21:45 +00:00
|
|
|
|
private bool GetDexFlag(int region, int species)
|
2017-03-15 05:41:15 +00:00
|
|
|
|
{
|
|
|
|
|
int bit = species - 1;
|
2016-08-27 11:33:21 +00:00
|
|
|
|
int ofs = bit >> 3;
|
2019-03-30 23:21:45 +00:00
|
|
|
|
return GetFlag(region + ofs, bit & 7);
|
2016-08-30 00:48:05 +00:00
|
|
|
|
}
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2019-03-30 23:21:45 +00:00
|
|
|
|
private void SetDexFlag(int region, int species, bool value)
|
2016-08-30 00:48:05 +00:00
|
|
|
|
{
|
2017-03-15 05:41:15 +00:00
|
|
|
|
int bit = species - 1;
|
2016-08-30 00:48:05 +00:00
|
|
|
|
int ofs = bit >> 3;
|
2019-03-30 23:21:45 +00:00
|
|
|
|
SetFlag(region + ofs, bit & 7, value);
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
2017-04-09 21:06:50 +00:00
|
|
|
|
|
2019-11-16 01:34:18 +00:00
|
|
|
|
public override void WriteSlotFormatStored(PKM pkm, byte[] data, int offset)
|
2018-08-19 20:35:20 +00:00
|
|
|
|
{
|
|
|
|
|
// pkm that have never been boxed have yet to save the 'current level' for box indication
|
|
|
|
|
// set this value at this time
|
|
|
|
|
((PK1)pkm).Stat_LevelBox = pkm.CurrentLevel;
|
2019-11-16 01:34:18 +00:00
|
|
|
|
base.WriteSlotFormatStored(pkm, Data, offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void WriteBoxSlot(PKM pkm, byte[] data, int offset)
|
|
|
|
|
{
|
|
|
|
|
// pkm that have never been boxed have yet to save the 'current level' for box indication
|
|
|
|
|
// set this value at this time
|
|
|
|
|
((PK1)pkm).Stat_LevelBox = pkm.CurrentLevel;
|
|
|
|
|
base.WriteBoxSlot(pkm, Data, offset);
|
2018-08-19 20:35:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-24 23:36:51 +00:00
|
|
|
|
private const int SpawnFlagCount = 0xF0;
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-09-24 23:36:51 +00:00
|
|
|
|
public bool[] EventSpawnFlags
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
// RB uses 0xE4 (0xE8) flags, Yellow uses 0xF0 flags. Just grab 0xF0
|
|
|
|
|
bool[] data = new bool[SpawnFlagCount];
|
|
|
|
|
for (int i = 0; i < data.Length; i++)
|
2020-10-16 23:12:42 +00:00
|
|
|
|
data[i] = GetFlag(Offsets.ObjectSpawnFlags + (i >> 3), i & 7);
|
2017-09-24 23:36:51 +00:00
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
2020-04-12 20:20:40 +00:00
|
|
|
|
if (value.Length != SpawnFlagCount)
|
2017-09-24 23:36:51 +00:00
|
|
|
|
return;
|
|
|
|
|
for (int i = 0; i < value.Length; i++)
|
2020-10-16 23:12:42 +00:00
|
|
|
|
SetFlag(Offsets.ObjectSpawnFlags + (i >> 3), i & 7, value[i]);
|
2017-09-24 23:36:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 04:50:44 +00:00
|
|
|
|
public override string GetString(byte[] data, int offset, int length) => StringConverter12.GetString1(data, offset, length, Japanese);
|
2018-08-06 03:27:25 +00:00
|
|
|
|
|
2017-06-18 01:37:19 +00:00
|
|
|
|
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
|
2017-04-09 21:06:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (PadToSize == 0)
|
|
|
|
|
PadToSize = maxLength + 1;
|
2021-05-29 22:31:47 +00:00
|
|
|
|
return StringConverter12.SetString1(value, maxLength, Japanese, PadToSize, (byte)PadWith);
|
2017-04-09 21:06:50 +00:00
|
|
|
|
}
|
2016-08-27 11:33:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|