mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 22:10:21 +00:00
Add savedata models
Co-Authored-By: Matt <17801814+sora10pls@users.noreply.github.com> Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com> Co-Authored-By: Lusamine <30205550+Lusamine@users.noreply.github.com>
This commit is contained in:
parent
b2a65363ff
commit
691f941bb6
28 changed files with 3061 additions and 2 deletions
9
PKHeX.Core/Ribbons/ISociability.cs
Normal file
9
PKHeX.Core/Ribbons/ISociability.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates how sociable the entity is.
|
||||
/// </summary>
|
||||
public interface ISociability
|
||||
{
|
||||
uint Sociability { get; set; }
|
||||
}
|
19
PKHeX.Core/Saves/Access/ISCBlockArray.cs
Normal file
19
PKHeX.Core/Saves/Access/ISCBlockArray.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes useful <see cref="SCBlock"/> access information useful for more advanced data requests.
|
||||
/// </summary>
|
||||
public interface ISCBlockArray
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of all data blocks the implementing object has.
|
||||
/// </summary>
|
||||
public IReadOnlyList<SCBlock> AllBlocks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="SCBlockAccessor"/> for the implementing object, allowing for looking up specific blocks by key.
|
||||
/// </summary>
|
||||
SCBlockAccessor Accessor { get; }
|
||||
}
|
17
PKHeX.Core/Saves/Access/ISaveBlock8LA.cs
Normal file
17
PKHeX.Core/Saves/Access/ISaveBlock8LA.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for Accessing named blocks within a Generation 8 <see cref="GameVersion.PLA"/> save file.
|
||||
/// </summary>
|
||||
public interface ISaveBlock8LA
|
||||
{
|
||||
Box8 BoxInfo { get; }
|
||||
Party8a PartyInfo { get; }
|
||||
MyStatus8a MyStatus { get; }
|
||||
PokedexSave8a PokedexSave { get; }
|
||||
BoxLayout8a BoxLayout { get; }
|
||||
MyItem8a Items { get; }
|
||||
AdventureStart8a AdventureStart { get; }
|
||||
LastSaved8a LastSaved { get; }
|
||||
PlayTime8a Played { get; }
|
||||
}
|
175
PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs
Normal file
175
PKHeX.Core/Saves/Access/SaveBlockAccessor8LA.cs
Normal file
|
@ -0,0 +1,175 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
// ReSharper disable UnusedMember.Local
|
||||
#pragma warning disable IDE0051 // Remove unused private members
|
||||
#pragma warning disable RCS1213 // Remove unused member declaration.
|
||||
public sealed class SaveBlockAccessor8LA : SCBlockAccessor, ISaveBlock8LA
|
||||
{
|
||||
public override IReadOnlyList<SCBlock> BlockInfo { get; }
|
||||
public Party8a PartyInfo { get; }
|
||||
public Box8 BoxInfo { get; }
|
||||
public MyStatus8a MyStatus { get; }
|
||||
public PokedexSave8a PokedexSave { get; }
|
||||
public BoxLayout8a BoxLayout { get; }
|
||||
public MyItem8a Items { get; }
|
||||
public AdventureStart8a AdventureStart { get; }
|
||||
public Coordinates8a Coordinates { get; }
|
||||
public LastSaved8a LastSaved { get; }
|
||||
public PlayerFashion8a FashionPlayer { get; }
|
||||
public PlayTime8a Played { get; }
|
||||
|
||||
public SaveBlockAccessor8LA(SAV8LA sav)
|
||||
{
|
||||
BlockInfo = sav.AllBlocks;
|
||||
BoxInfo = new Box8(sav, GetBlock(KBox));
|
||||
PokedexSave = new PokedexSave8a(sav, GetBlock(KZukan));
|
||||
BoxLayout = new BoxLayout8a(sav, GetBlock(KBoxLayout));
|
||||
PartyInfo = new Party8a(sav, GetBlock(KParty));
|
||||
MyStatus = new MyStatus8a(sav, GetBlock(KMyStatus));
|
||||
Items = new MyItem8a(sav, GetBlock(KItemRegular));
|
||||
AdventureStart = new AdventureStart8a(sav, GetBlock(KAdventureStart));
|
||||
LastSaved = new LastSaved8a(sav, GetBlock(KLastSaved));
|
||||
Played = new PlayTime8a(sav, GetBlock(KPlayTime));
|
||||
Coordinates = new Coordinates8a(sav, GetBlock(KCoordinates));
|
||||
FashionPlayer = new PlayerFashion8a(sav, GetBlock(KFashionPlayer));
|
||||
// Misc = new Misc8(sav, GetBlock(KMisc));
|
||||
// TrainerCard = new TrainerCard8(sav, GetBlock(KTrainerCard));
|
||||
// Fashion = new FashionUnlock8(sav, GetBlock(KFashionUnlock));
|
||||
}
|
||||
|
||||
// Arrays (Blocks)
|
||||
private const uint KBoxLayout = 0x19722c89; // Box Names
|
||||
public const uint KBoxWallpapersUnused = 0x2EB1B190; // Box Wallpapers
|
||||
public const uint KItemFavorite = 0x00EF4BAE; // Favorite Item ID bitflags
|
||||
|
||||
// Objects (Blocks)
|
||||
private const uint KBox = 0x47E1CEAB; // Box Data
|
||||
public const uint KItemRegular = 0x9FE2790A;
|
||||
public const uint KItemKey = 0x59A4D0C3;
|
||||
public const uint KItemStored = 0x8E434F0D;
|
||||
public const uint KItemRecipe = 0xF5D9F4A5;
|
||||
private const uint KMysteryGift = 0x99E1625E;
|
||||
private const uint KZukan = 0x02168706;
|
||||
private const uint KAdventureStart = 0xAEE903A2; // Save File Started
|
||||
private const uint KParty = 0x2985fe5d; // Party Data
|
||||
private const uint KPlayTime = 0xC4FA7C8C; // Time Played
|
||||
private const uint KMyStatus = 0xf25c070e; // Trainer Details
|
||||
private const uint KLastSaved = 0x1B1E3D8B; // Last Saved
|
||||
private const uint KCoordinates = 0x267DD9DA; // Coordinates
|
||||
private const uint KFashionPlayer = 0x6B35BADB; // Player's Current Fashion
|
||||
private const uint KFashionUnlock = 0x3ADB8A98; // Unlocked Fashion Data
|
||||
private const uint KSwarm = 0x1E0F1BA3; // 5 entries, 0x50 each
|
||||
private const uint KCaptureRecords = 0x6506EE96; // 1000 entries, 0x1C each
|
||||
private const uint KOverworld = 0x511622B3; // 0x100 entries, 0x880 each
|
||||
|
||||
// Values
|
||||
public const uint KCurrentBox = 0x017C3CBB; // U8 Box Index
|
||||
public const uint KBoxesUnlocked = 0x71825204; // U8
|
||||
|
||||
private const uint KVolumeBGM = 0xF8154AC9; // U32 Background Music volume control (0-10)
|
||||
private const uint KVolumeSFX = 0x62F05895; // U32 Sound Effects volume control (0-10)
|
||||
private const uint KVolumeCry = 0x1D482A63; // U32 Pokémon Cries volume control (0-10)
|
||||
|
||||
private const uint KOptionTextSpeed = 0x92EB0306; // U32 text speed (0 = Slow, 1 = Normal, 2 = Fast, 3 = Instant)
|
||||
private const uint KOptionCameraVertical = 0x2846B7DB; // U32 vertical camera controls (0 = Normal, 1 = Inverted)
|
||||
private const uint KOptionCameraHorizontal = 0x7D249649; // U32 horizontal camera controls (0 = Normal, 1 = Inverted)
|
||||
private const uint KOptionCameraSensitivity = 0x22DEF108; // U32 camera sensitivity (0-4)
|
||||
private const uint KOptionMotionSensitivity = 0x82AD5F84; // U32 motion sensitivity (0-3)
|
||||
private const uint KOptionAutosave = 0xB027F396; // U32 Autosave (0 = Enabled, 1 = Disabled)
|
||||
private const uint KOptionToggleHUD = 0xF62D79D3; // U32 HUD Toggling (0 = Enabled, 1 = Disabled)
|
||||
private const uint KOptionZRButtonConfirmation = 0x4D7EADDD; // U32 ZR Button confirmation (0 = Enabled, 1 = Disabled)
|
||||
private const uint KOptionDynamicRange = 0xA4317061; // U32 Dynamic Range (0 = Wide, 1 = Narrow)
|
||||
|
||||
public const uint KGameLanguage = 0x0BFDEBA1; // U32 Game Language
|
||||
public const uint KMoney = 0x3279D927; // U32 Money
|
||||
public const uint KMeritCurrent = 0x9D5D1CA5; // U32 Current Merit Points
|
||||
public const uint KMeritEarnedTotal = 0xC25B0D5A; // U32 Merit Points Earned
|
||||
public const uint KSatchelUpgrades = 0x75CE2CF6; // U32 Satchel Upgrades (0-39)
|
||||
public const uint KExpeditionTeamRank = 0x50FE632A; // U32 Galaxy Expedition Team Rank (0-10)
|
||||
private const uint KTotalUnownCaptured = 0x3EBEE1A7; // U32 Unown Captured (0-28)
|
||||
private const uint KStealthSpray = 0x385F9860; // U32 time remaining on active Stealth Spray (0-60000 in milliseconds)
|
||||
|
||||
private const uint KRepelUnused = 0x9ec079da; // U16 Repel Steps remaining
|
||||
|
||||
private const uint KWispsFoundArea00 = 0x8B18ADE5; // U32 Wisps obtained in Jubilife Village (0-7)
|
||||
private const uint KWispsFoundArea01 = 0x8B18AC32; // U32 Wisps obtained in Obsidian Fieldlands (0-20)
|
||||
private const uint KWispsFoundArea02 = 0x8B18AA7F; // U32 Wisps obtained in Crimson Mirelands (0-20)
|
||||
private const uint KWispsFoundArea03 = 0x8B18A8CC; // U32 Wisps obtained in Cobalt Coastlands (0-20)
|
||||
private const uint KWispsFoundArea04 = 0x8B18A719; // U32 Wisps obtained in Coronet Highlands (0-20)
|
||||
private const uint KWispsFoundArea05 = 0x8B18A566; // U32 Wisps obtained in Alabaster Icelands (0-20)
|
||||
private const uint KWispsFoundTotal = 0xB79EF1FE; // U32 total Wisps obtained (0-107)
|
||||
private const uint KWispsReported = 0x8F0D8720; // U32 Wisps reported to Vessa (0-107)
|
||||
|
||||
// Flags
|
||||
private const uint KEnableSpawnerSpiritomb = 0x2DC7E4CC; // FSYS_MKRG_100_SPAWN
|
||||
private const uint KEnableSpawnerUxie = 0x9EC1F2C4; // FEVE_YUKUSII_ENCOUNT_ENABLE
|
||||
private const uint KEnableSpawnerMesprit = 0xEF5C95D8; // FEVE_EMURITTO_ENCOUNT_ENABLE
|
||||
private const uint KEnableSpawnerAzelf = 0xD038BD89; // FEVE_AGUNOMU_ENCOUNT_ENABLE
|
||||
private const uint KEnableSpawnerHeatran = 0x3F6301AC; // FEVE_HIIDORAN__ENCOUNT_ENABLE
|
||||
private const uint KEnableSpawnerCresselia = 0x85134D02; // FEVE_KURESERIA_ENCOUNT_ENABLE
|
||||
private const uint KEnableSpawnerDarkrai = 0xEE027506; // FSYS_SPAWN_START_DARKRAI
|
||||
private const uint KEnableSpawnerShaymin = 0x0DCE6659; // FSYS_SPAWN_START_SHAYMIN
|
||||
private const uint KEnableSpawnerTornadus = 0x07D8EC38; // FSYS_SPAWN_START_TORNELOS
|
||||
private const uint KEnableSpawnerThundurus = 0x136D3D88; // FSYS_SPAWN_START_VOLTOLOS
|
||||
private const uint KEnableSpawnerLandorus = 0xE079071B; // FSYS_SPAWN_START_LANDLOS
|
||||
private const uint KEnableSpawnerEnamorus = 0x3AA64045; // FSYS_SPAWN_START_FAIRTOLOS
|
||||
|
||||
private const uint KDisableSpawnerSpiritomb = 0x0AB16F69; // FSYS_MKRG_VALID_SPAWN
|
||||
private const uint KDisableSpawnerGiratina = 0x40B908EC; // FMAP_CANNOT_RESPAWN_GIRATINA
|
||||
private const uint KDisableSpawnerPhione01 = 0x3C4DB3BE; // FMAP_CANNOT_RESPAWN_PHIONE
|
||||
private const uint KDisableSpawnerPhione02 = 0xF6B469D3; // FMAP_CANNOT_RESPAWN_PHIONE_2
|
||||
private const uint KDisableSpawnerPhione03 = 0xF6B46820; // FMAP_CANNOT_RESPAWN_PHIONE_3
|
||||
private const uint KDisableSpawnerManaphy = 0xBBE677C7; // FMAP_CANNOT_RESPAWN_MANAPHY
|
||||
private const uint KDisableSpawnerDarkrai = 0x8AE49E85; // FMAP_CANNOT_RESPAWN_DARKRAI
|
||||
private const uint KDisableSpawnerShaymin = 0xF873BBFA; // FMAP_CANNOT_RESPAWN_SHAYMIN
|
||||
private const uint KDisableSpawnerTornadus = 0xC8AA3D69; // FMAP_CANNOT_RESPAWN_TORNELOS
|
||||
private const uint KDisableSpawnerThundurus = 0x79E259CD; // FMAP_CANNOT_RESPAWN_VOLTOLOS
|
||||
private const uint KDisableSpawnerLandorus = 0xD613F320; // FMAP_CANNOT_RESPAWN_LANDLOS
|
||||
private const uint KDisableSpawnerEnamorus = 0xE50F4B4E; // FMAP_CANNOT_RESPAWN_FAIRTOLOS
|
||||
|
||||
private const uint KReceivedAlolanVulpix = 0xAC90C782; // FEVE_POKE_SUB092_GET
|
||||
|
||||
private const uint KCanRideWyrdeer = 0x47365FE8; // FSYS_RIDE_OPEN_01
|
||||
//private const uint KCanRideUnused02 = 0x47366501; // FSYS_RIDE_OPEN_02
|
||||
private const uint KCanRideUrsaluna = 0x4736634E; // FSYS_RIDE_OPEN_03
|
||||
//private const uint KCanRideUnused04 = 0x47366867; // FSYS_RIDE_OPEN_04
|
||||
private const uint KCanRideBasculegion = 0x473666B4; // FSYS_RIDE_OPEN_05
|
||||
//private const uint KCanRideUnused06 = 0x47366BCD; // FSYS_RIDE_OPEN_06
|
||||
private const uint KCanRideSneasler = 0x47366A1A; // FSYS_RIDE_OPEN_07
|
||||
//private const uint KCanRideUnused08 = 0x47365403; // FSYS_RIDE_OPEN_08
|
||||
//private const uint KCanRideUnused09 = 0x47365250; // FSYS_RIDE_OPEN_09
|
||||
private const uint KCanRideBraviary = 0x47334812; // FSYS_RIDE_OPEN_10
|
||||
|
||||
private const uint KDefeatedLordKleavor = 0x96774421; // FSYS_NS_01_CLEARED
|
||||
private const uint KDefeatedLadyLilligant = 0x3A50000C; // FSYS_NS_02_CLEARED
|
||||
private const uint KDefeatedLordArcanine = 0xA5981A37; // FSYS_NS_03_CLEARED
|
||||
private const uint KDefeatedLordElectrode = 0x6EF3C712; // FSYS_NS_04_CLEARED
|
||||
private const uint KDefeatedLordAvalugg = 0x424E9F0D; // FSYS_NS_05_CLEARED
|
||||
private const uint KDefeatedOriginDialga = 0x5185ADC0; // FSYS_NS_D_CLEARED
|
||||
private const uint KDefeatedOriginPalkia = 0x5E5BFD94; // FSYS_NS_P_CLEARED
|
||||
private const uint KDefeatedArceus = 0x2F91EFD3; // FSYS_SCENARIO_CLEARED_URA
|
||||
private const uint KCompletedPokedex = 0xD985E1C2; // FEVE_EV110100_END (Enables using Azure Flute to reach Arceus)
|
||||
private const uint KPerfectedPokedex = 0x98ED661E; // FSYS_POKEDEX_COMPLETE_WITHOUT_EXCEPTION
|
||||
private const uint KUnlockedUnownNotes = 0xC9127B4E; // FSYS_UNNN_ENABLE_PLACEMENT
|
||||
private const uint KUnlockedLostAndFound = 0xFE837926; // FSYS_LOSTBAG_SEARCH_REQUEST_ENABLE
|
||||
private const uint KUnlockedMassRelease = 0x0C16BEF4; // FSYS_APP_BOX_SUMFREE_ENABLE
|
||||
private const uint KUnlockedDistortions = 0x7611BFC3; // FSYS_WORMHOLE_OPEN
|
||||
private const uint KCanFastTravel = 0xFE98F73F; // FSYS_CAN_USE_FAST_TRAVEL
|
||||
private const uint KUnlockedArea01 = 0x24C0252D; // FSYS_AREA_01_OPEN
|
||||
private const uint KUnlockedArea02 = 0x1599C206; // FSYS_AREA_02_OPEN
|
||||
private const uint KUnlockedArea03 = 0x408DE1D3; // FSYS_AREA_03_OPEN
|
||||
private const uint KUnlockedArea04 = 0x8C062C9C; // FSYS_AREA_04_OPEN
|
||||
private const uint KUnlockedArea05 = 0xC08D4C69; // FSYS_AREA_05_OPEN
|
||||
private const uint KUnlockedArea06 = 0x76350E52; // FSYS_AREA_06_OPEN
|
||||
private const uint KAutoConnectInternet = 0xAFA034A5;
|
||||
|
||||
private const uint KHasPlayRecordsBDSP = 0x52CE2052; // FSYS_SAVEDATA_LINKAGE_DEL_01
|
||||
private const uint KHasPlayRecordsSWSH = 0x530EF0B9; // FSYS_SAVEDATA_LINKAGE_ORI_01
|
||||
private const uint KHasPlayRecordsLGPE = 0x6CFA9468; // FSYS_SAVEDATA_LINKAGE_BEL_01
|
||||
|
||||
public const uint KUnlockedSecretBox01 = 0xF224CA8E; // FSYS_SECRET_BOX_01_OPEN
|
||||
public const uint KUnlockedSecretBox02 = 0x06924515; // FSYS_SECRET_BOX_02_OPEN
|
||||
public const uint KUnlockedSecretBox03 = 0xF67C6DC8; // FSYS_SECRET_BOX_03_OPEN
|
||||
}
|
243
PKHeX.Core/Saves/SAV8LA.cs
Normal file
243
PKHeX.Core/Saves/SAV8LA.cs
Normal file
|
@ -0,0 +1,243 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Generation 8 <see cref="SaveFile"/> object for <see cref="GameVersion.PLA"/> games.
|
||||
/// </summary>
|
||||
public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray
|
||||
{
|
||||
protected internal override string ShortSummary => $"{OT} ({Version}) - {LastSaved.LastSavedTime}";
|
||||
public override string Extension => string.Empty;
|
||||
|
||||
public SAV8LA(byte[] data) : base(data)
|
||||
{
|
||||
Data = Array.Empty<byte>();
|
||||
AllBlocks = SwishCrypto.Decrypt(data);
|
||||
Blocks = new SaveBlockAccessor8LA(this);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private SAV8LA(byte[] data, IReadOnlyList<SCBlock> blocks) : base(data)
|
||||
{
|
||||
Data = Array.Empty<byte>();
|
||||
AllBlocks = blocks;
|
||||
Blocks = new SaveBlockAccessor8LA(this);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public SAV8LA()
|
||||
{
|
||||
AllBlocks = Meta8.GetBlankDataLA();
|
||||
Blocks = new SaveBlockAccessor8LA(this);
|
||||
Initialize();
|
||||
ClearBoxes();
|
||||
}
|
||||
|
||||
public override string GetString(ReadOnlySpan<byte> data) => StringConverter8.GetString(data);
|
||||
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option) => StringConverter8.SetString(destBuffer, value, maxLength, option);
|
||||
|
||||
public override void CopyChangesFrom(SaveFile sav)
|
||||
{
|
||||
// Absorb changes from all blocks
|
||||
var z = (SAV8LA)sav;
|
||||
var mine = AllBlocks;
|
||||
var newB = z.AllBlocks;
|
||||
for (int i = 0; i < mine.Count; i++)
|
||||
newB[i].Data.CopyTo(mine[i].Data, 0);
|
||||
State.Edited = true;
|
||||
}
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8ASTORED;
|
||||
protected override PKM GetPKM(byte[] data) => new PA8(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray8A(data);
|
||||
|
||||
public override PKM BlankPKM => new PA8();
|
||||
public override Type PKMType => typeof(PA8);
|
||||
public override int MaxEV => 252;
|
||||
public override int Generation => 8;
|
||||
public override int OTLength => 12;
|
||||
public override int NickLength => 12;
|
||||
|
||||
public SCBlockAccessor Accessor => Blocks;
|
||||
public IReadOnlyList<SCBlock> AllBlocks { get; }
|
||||
public override bool ChecksumsValid => true;
|
||||
public override string ChecksumInfo => string.Empty;
|
||||
public override int BoxCount => BoxLayout8a.BoxCount; // 32
|
||||
public override int TID { get => MyStatus.TID; set => MyStatus.TID = value; }
|
||||
public override int SID { get => MyStatus.SID; set => MyStatus.SID = value; }
|
||||
public override int Game { get => MyStatus.Game; set => MyStatus.Game = value; }
|
||||
public override int Gender { get => MyStatus.Gender; set => MyStatus.Gender = value; }
|
||||
public override int Language { get => MyStatus.Language; set => MyStatus.Language = value; }
|
||||
public override string OT { get => MyStatus.OT; set => MyStatus.OT = value; }
|
||||
|
||||
public override GameVersion Version => Game switch
|
||||
{
|
||||
(int)GameVersion.PLA => GameVersion.PLA,
|
||||
_ => GameVersion.Invalid,
|
||||
};
|
||||
|
||||
protected override void SetChecksums() { } // None!
|
||||
protected override byte[] GetFinalData() => SwishCrypto.Encrypt(AllBlocks);
|
||||
|
||||
public override PersonalTable Personal => PersonalTable.LA;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_SWSH;
|
||||
|
||||
#region Blocks
|
||||
public SaveBlockAccessor8LA Blocks { get; }
|
||||
|
||||
public T GetValue<T>(uint key) where T : struct
|
||||
{
|
||||
if (!State.Exportable)
|
||||
return default;
|
||||
var value = Blocks.GetBlockValue(key);
|
||||
if (value is T v)
|
||||
return v;
|
||||
throw new ArgumentException($"Incorrect type request! Expected {typeof(T).Name}, received {value.GetType().Name}", nameof(T));
|
||||
}
|
||||
|
||||
public void SetValue<T>(uint key, T value) where T : struct
|
||||
{
|
||||
if (!State.Exportable)
|
||||
return;
|
||||
Blocks.SetBlockValue(key, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
protected override SaveFile CloneInternal()
|
||||
{
|
||||
var blockCopy = new SCBlock[AllBlocks.Count];
|
||||
for (int i = 0; i < AllBlocks.Count; i++)
|
||||
blockCopy[i] = AllBlocks[i].Clone();
|
||||
return new SAV8LA(State.BAK, blockCopy);
|
||||
}
|
||||
|
||||
public override int MaxMoveID => Legal.MaxMoveID_8a;
|
||||
public override int MaxSpeciesID => Legal.MaxSpeciesID_8a;
|
||||
public override int MaxItemID => Legal.MaxItemID_8a;
|
||||
public override int MaxBallID => Legal.MaxBallID_8a;
|
||||
public override int MaxGameID => Legal.MaxGameID_8a;
|
||||
public override int MaxAbilityID => Legal.MaxAbilityID_8a;
|
||||
|
||||
public Box8 BoxInfo => Blocks.BoxInfo;
|
||||
public Party8a PartyInfo => Blocks.PartyInfo;
|
||||
public MyStatus8a MyStatus => Blocks.MyStatus;
|
||||
public PokedexSave8a PokedexSave => Blocks.PokedexSave;
|
||||
public BoxLayout8a BoxLayout => Blocks.BoxLayout;
|
||||
public MyItem8a Items => Blocks.Items;
|
||||
public AdventureStart8a AdventureStart => Blocks.AdventureStart;
|
||||
public LastSaved8a LastSaved => Blocks.LastSaved;
|
||||
public PlayTime8a Played => Blocks.Played;
|
||||
public override uint SecondsToStart { get => (uint)AdventureStart.Seconds; set => AdventureStart.Seconds = value; }
|
||||
public override uint Money { get => (uint)Blocks.GetBlockValue(SaveBlockAccessor8LA.KMoney); set => Blocks.SetBlockValue(SaveBlockAccessor8LA.KMoney, value); }
|
||||
public override int MaxMoney => 9_999_999;
|
||||
|
||||
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = (ushort)value; }
|
||||
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = (byte)value; }
|
||||
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = (byte)value; }
|
||||
|
||||
protected override byte[] BoxBuffer => BoxInfo.Data;
|
||||
protected override byte[] PartyBuffer => PartyInfo.Data;
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Box = 0;
|
||||
Party = 0;
|
||||
PokeDex = 0;
|
||||
}
|
||||
|
||||
public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot);
|
||||
public override int PartyCount
|
||||
{
|
||||
get => PartyInfo.PartyCount;
|
||||
protected set => PartyInfo.PartyCount = value;
|
||||
}
|
||||
|
||||
// Zukan
|
||||
protected override void SetDex(PKM pkm)
|
||||
{
|
||||
// TODO: Seen in wild?
|
||||
// Accessor.SetPokeSeenInWild(pkm);
|
||||
|
||||
// TODO: Should this update research? What research should it be updating?
|
||||
// TODO: Should this be passing "caught=true" to set caught flags and not just obtain flags?
|
||||
// For now, if we have never obtained the poke, treat this pkm as obtained-via-trade.
|
||||
PokedexSave.OnPokeGet_TradeWithoutEvolution(pkm);
|
||||
}
|
||||
|
||||
public override bool GetCaught(int species)
|
||||
{
|
||||
if (species > Personal.MaxSpeciesID)
|
||||
return false;
|
||||
|
||||
var formCount = Personal[species].FormCount;
|
||||
for (var form = 0; form < formCount; form++)
|
||||
{
|
||||
if (PokedexSave.HasAnyPokeObtainFlags(species, form))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool GetSeen(int species) => PokedexSave.HasPokeEverBeenUpdated(species);
|
||||
|
||||
// Inventory
|
||||
public override IReadOnlyList<InventoryPouch> Inventory { get => Items.Inventory; set => Items.Inventory = value; }
|
||||
|
||||
#region Boxes
|
||||
public override bool HasBoxWallpapers => false;
|
||||
public override bool HasNamableBoxes => true;
|
||||
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = value; }
|
||||
public override int BoxesUnlocked { get => (byte)Blocks.GetBlockValue(SaveBlockAccessor8LA.KBoxesUnlocked); set => Blocks.SetBlockValue(SaveBlockAccessor8LA.KBoxesUnlocked, (byte)value); }
|
||||
|
||||
public override byte[] BoxFlags
|
||||
{
|
||||
get => new[]
|
||||
{
|
||||
Convert.ToByte(Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox01).Type - 1),
|
||||
Convert.ToByte(Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox02).Type - 1),
|
||||
Convert.ToByte(Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox03).Type - 1),
|
||||
};
|
||||
set
|
||||
{
|
||||
if (value.Length != 1)
|
||||
return;
|
||||
|
||||
var blocks = new[]
|
||||
{
|
||||
Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox01),
|
||||
Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox02),
|
||||
Blocks.GetBlock(SaveBlockAccessor8LA.KUnlockedSecretBox03),
|
||||
};
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
block.ChangeBooleanType((SCTypeCode)(value[0] & 1) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetBoxOffset(int box) => Box + (SIZE_BOXSLOT * box * 30);
|
||||
public override string GetBoxName(int box) => BoxLayout.GetBoxName(box);
|
||||
public override void SetBoxName(int box, string value) => BoxLayout.SetBoxName(box, value);
|
||||
|
||||
public override int GetBoxWallpaper(int box)
|
||||
{
|
||||
if ((uint)box >= BoxCount)
|
||||
return box;
|
||||
var b = Blocks.GetBlock(SaveBlockAccessor8LA.KBoxWallpapersUnused);
|
||||
return b.Data[box];
|
||||
}
|
||||
|
||||
public override void SetBoxWallpaper(int box, int value)
|
||||
{
|
||||
if ((uint)box >= BoxCount)
|
||||
return;
|
||||
var b = Blocks.GetBlock(SaveBlockAccessor8LA.KBoxWallpapersUnused);
|
||||
b.Data[box] = (byte)value;
|
||||
}
|
||||
#endregion
|
||||
}
|
33
PKHeX.Core/Saves/Substructures/Gen8/LA/AdventureStart8a.cs
Normal file
33
PKHeX.Core/Saves/Substructures/Gen8/LA/AdventureStart8a.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the <see cref="Timestamp"/> when the player created their save data.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class AdventureStart8a : SaveBlock
|
||||
{
|
||||
public AdventureStart8a(SaveFile sav, SCBlock block) : base(sav, block.Data) { }
|
||||
|
||||
/// <summary>
|
||||
/// time_t (seconds since 1970 Epoch)
|
||||
/// </summary>
|
||||
public ulong Seconds
|
||||
{
|
||||
get => BinaryPrimitives.ReadUInt64LittleEndian(Data.AsSpan(Offset));
|
||||
set => BinaryPrimitives.WriteUInt64LittleEndian(Data.AsSpan(Offset), value);
|
||||
}
|
||||
|
||||
private static DateTime Epoch => new(1970, 1, 1);
|
||||
|
||||
public string AdventureStartTime => $"{Timestamp.Year:0000}-{Timestamp.Month:00}-{Timestamp.Day:00} {Timestamp.Hour:00}ː{Timestamp.Minute:00}ː{Timestamp.Second:00}"; // not :
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get => Epoch.AddSeconds(Seconds);
|
||||
set => Seconds = (ulong)value.Subtract(Epoch).TotalSeconds;
|
||||
}
|
||||
}
|
34
PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs
Normal file
34
PKHeX.Core/Saves/Substructures/Gen8/LA/BoxLayout8a.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes information about Box Names and which box is the first box to show when the UI is opened.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class BoxLayout8a : SaveBlock, IBoxDetailName
|
||||
{
|
||||
public const int BoxCount = 32;
|
||||
|
||||
private const int StringMaxLength = SAV6.LongStringLength / 2; // 0x22 bytes
|
||||
|
||||
public BoxLayout8a(SAV8LA sav, SCBlock block) : base(sav, block.Data) { }
|
||||
|
||||
private static int GetBoxNameOffset(int box) => SAV6.LongStringLength * box;
|
||||
private Span<byte> GetBoxNameSpan(int box) => Data.AsSpan(GetBoxNameOffset(box), SAV6.LongStringLength);
|
||||
public string GetBoxName(int box) => SAV.GetString(GetBoxNameSpan(box));
|
||||
public void SetBoxName(int box, string value) => SAV.SetString(GetBoxNameSpan(box), value.AsSpan(), StringMaxLength, StringConverterOption.ClearZero);
|
||||
|
||||
public string this[int i]
|
||||
{
|
||||
get => GetBoxName(i);
|
||||
set => SetBoxName(i, value);
|
||||
}
|
||||
|
||||
public int CurrentBox
|
||||
{
|
||||
get => ((SAV8LA)SAV).GetValue<byte>(SaveBlockAccessor8LA.KCurrentBox);
|
||||
set => ((SAV8LA)SAV).SetValue(SaveBlockAccessor8LA.KCurrentBox, (byte)value);
|
||||
}
|
||||
}
|
16
PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs
Normal file
16
PKHeX.Core/Saves/Substructures/Gen8/LA/Coordinates8a.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the position of the player.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class Coordinates8a : SaveBlock
|
||||
{
|
||||
public Coordinates8a(SaveFile sav, SCBlock block) : base(sav, block.Data) { }
|
||||
public float X { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x50), value); }
|
||||
public float Z { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x54)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x54), value); }
|
||||
public float Y { get => ReadSingleLittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteSingleLittleEndian(Data.AsSpan(Offset + 0x58), value); }
|
||||
}
|
54
PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs
Normal file
54
PKHeX.Core/Saves/Substructures/Gen8/LA/LastSaved8a.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the <see cref="LastSavedDate"/> of the player.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Year value is offset by -1900.
|
||||
/// Month value is offset by -1.
|
||||
/// </remarks>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class LastSaved8a : SaveBlock
|
||||
{
|
||||
public LastSaved8a(SaveFile sav, SCBlock block) : base(sav, block.Data) { }
|
||||
private uint LastSaved { get => ReadUInt32LittleEndian(Data.AsSpan(Offset + 0x0)); set => WriteUInt32LittleEndian(Data.AsSpan(Offset + 0x0), value); }
|
||||
private int LastSavedYear { get => (int)(LastSaved & 0xFFF); set => LastSaved = (LastSaved & 0xFFFFF000) | (uint)value; }
|
||||
private int LastSavedMonth { get => (int)(LastSaved >> 12 & 0xF); set => LastSaved = (LastSaved & 0xFFFF0FFF) | ((uint)value & 0xF) << 12; }
|
||||
private int LastSavedDay { get => (int)(LastSaved >> 16 & 0x1F); set => LastSaved = (LastSaved & 0xFFE0FFFF) | ((uint)value & 0x1F) << 16; }
|
||||
private int LastSavedHour { get => (int)(LastSaved >> 21 & 0x1F); set => LastSaved = (LastSaved & 0xFC1FFFFF) | ((uint)value & 0x1F) << 21; }
|
||||
private int LastSavedMinute { get => (int)(LastSaved >> 26 & 0x3F); set => LastSaved = (LastSaved & 0x03FFFFFF) | ((uint)value & 0x3F) << 26; }
|
||||
public string LastSavedTime => $"{LastSavedYear+1900:0000}-{LastSavedMonth+1:00}-{LastSavedDay:00} {LastSavedHour:00}ː{LastSavedMinute:00}"; // not :
|
||||
|
||||
public DateTime? LastSavedDate
|
||||
{
|
||||
get => !DateUtil.IsDateValid(LastSavedYear + 1900, LastSavedMonth + 1, LastSavedDay)
|
||||
? null
|
||||
: new DateTime(LastSavedYear + 1900, LastSavedMonth + 1, LastSavedDay, LastSavedHour, LastSavedMinute, 0);
|
||||
set
|
||||
{
|
||||
// Only update the properties if a value is provided.
|
||||
if (value.HasValue)
|
||||
{
|
||||
var dt = value.Value;
|
||||
LastSavedYear = dt.Year - 1900;
|
||||
LastSavedMonth = dt.Month - 1;
|
||||
LastSavedDay = dt.Day;
|
||||
LastSavedHour = dt.Hour;
|
||||
LastSavedMinute = dt.Minute;
|
||||
}
|
||||
else // Clear the date.
|
||||
{
|
||||
// If code tries to access MetDate again, null will be returned.
|
||||
LastSavedYear = 0;
|
||||
LastSavedMonth = 0;
|
||||
LastSavedDay = 0;
|
||||
LastSavedHour = 0;
|
||||
LastSavedMinute = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
94
PKHeX.Core/Saves/Substructures/Gen8/LA/MyItem8a.cs
Normal file
94
PKHeX.Core/Saves/Substructures/Gen8/LA/MyItem8a.cs
Normal file
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the inventory of items that the player has acquired.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Reads four separate pouch blobs: Items, Key Items, Storage, and Recipes.
|
||||
/// </remarks>
|
||||
public sealed class MyItem8a : MyItem
|
||||
{
|
||||
public MyItem8a(SAV8LA SAV, SCBlock block) : base(SAV, block.Data) { }
|
||||
|
||||
public override IReadOnlyList<InventoryPouch> Inventory
|
||||
{
|
||||
get
|
||||
{
|
||||
var access = ((SAV8LA)SAV).Accessor;
|
||||
var regular = new InventoryPouch8a(InventoryType.Items, Legal.Pouch_Items_LA , 999, 675);
|
||||
var key = new InventoryPouch8a(InventoryType.KeyItems, Legal.Pouch_Key_LA , 1, 100);
|
||||
var stored = new InventoryPouch8a(InventoryType.PCItems, Legal.Pouch_Items_LA , 999, 180);
|
||||
var recipe = new InventoryPouch8a(InventoryType.Treasure, Legal.Pouch_Recipe_LA, 1, 70);
|
||||
regular.GetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemRegular).Data);
|
||||
key .GetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemKey).Data);
|
||||
stored.GetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemStored).Data);
|
||||
recipe.GetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemRecipe).Data);
|
||||
|
||||
var result = new[] { regular, key, stored, recipe };
|
||||
LoadFavorites(result, access);
|
||||
return result;
|
||||
}
|
||||
set
|
||||
{
|
||||
var access = ((SAV8LA)SAV).Accessor;
|
||||
foreach (var p in value)
|
||||
((InventoryPouch8a)p).SanitizeCounts();
|
||||
|
||||
value[0].SetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemRegular).Data);
|
||||
value[1].SetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemKey).Data);
|
||||
value[2].SetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemStored).Data);
|
||||
value[3].SetPouch(access.GetBlock(SaveBlockAccessor8LA.KItemRecipe).Data);
|
||||
SaveFavorites(value, access);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadFavorites(IEnumerable<InventoryPouch8a> pouches, SCBlockAccessor access)
|
||||
{
|
||||
var favorites = access.GetBlock(SaveBlockAccessor8LA.KItemFavorite).Data.AsSpan();
|
||||
foreach (var arr in pouches)
|
||||
LoadFavorites(arr.Items, favorites);
|
||||
}
|
||||
|
||||
private static void SaveFavorites(IEnumerable<InventoryPouch> pouches, SCBlockAccessor access)
|
||||
{
|
||||
var favorites = access.GetBlock(SaveBlockAccessor8LA.KItemFavorite).Data.AsSpan();
|
||||
favorites.Clear();
|
||||
foreach (var arr in pouches)
|
||||
SaveFavorites(arr.Items, favorites);
|
||||
}
|
||||
|
||||
private static void LoadFavorites(IEnumerable<InventoryItem> items, Span<byte> favorites)
|
||||
{
|
||||
foreach (var z in items)
|
||||
{
|
||||
var item = (InventoryItem8a)z;
|
||||
var itemID = item.Index;
|
||||
var ofs = itemID >> 3;
|
||||
if ((uint)ofs >= favorites.Length)
|
||||
continue;
|
||||
|
||||
var bit = itemID & 7;
|
||||
item.IsFavorite = FlagUtil.GetFlag(favorites, ofs, bit);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SaveFavorites(IEnumerable<InventoryItem> items, Span<byte> favorites)
|
||||
{
|
||||
foreach (var z in items)
|
||||
{
|
||||
var item = (InventoryItem8a)z;
|
||||
var itemID = item.Index;
|
||||
var ofs = itemID >> 3;
|
||||
if ((uint)ofs >= favorites.Length)
|
||||
continue;
|
||||
|
||||
var bit = itemID & 7;
|
||||
var value = FlagUtil.GetFlag(favorites, ofs, bit);
|
||||
value |= item.IsFavorite;
|
||||
FlagUtil.SetFlag(favorites, ofs, bit, value);
|
||||
}
|
||||
}
|
||||
}
|
69
PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs
Normal file
69
PKHeX.Core/Saves/Substructures/Gen8/LA/MyStatus8a.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores data about the player.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class MyStatus8a : SaveBlock
|
||||
{
|
||||
public MyStatus8a(SAV8LA sav, SCBlock block) : base(sav, block.Data) { }
|
||||
|
||||
public int TID
|
||||
{
|
||||
get => BinaryPrimitives.ReadUInt16LittleEndian(Data.AsSpan(0x10));
|
||||
set => BinaryPrimitives.WriteUInt16LittleEndian(Data.AsSpan(0x10), (ushort)value);
|
||||
}
|
||||
|
||||
public int SID
|
||||
{
|
||||
get => BinaryPrimitives.ReadUInt16LittleEndian(Data.AsSpan(0x12));
|
||||
set => BinaryPrimitives.WriteUInt16LittleEndian(Data.AsSpan(0x12), (ushort)value);
|
||||
}
|
||||
|
||||
public int Game
|
||||
{
|
||||
get => Data[0x14];
|
||||
set => Data[0x14] = (byte)value;
|
||||
}
|
||||
|
||||
public int Gender
|
||||
{
|
||||
get => Data[0x15];
|
||||
set => Data[0x15] = (byte)value;
|
||||
}
|
||||
|
||||
// A6
|
||||
public int Language
|
||||
{
|
||||
get => Data[Offset + 0x17];
|
||||
set
|
||||
{
|
||||
if (value == Language)
|
||||
return;
|
||||
Data[Offset + 0x17] = (byte)value;
|
||||
|
||||
// For runtime language, the game shifts all languages above Language 6 (unused) down one.
|
||||
if (value >= 6)
|
||||
value--;
|
||||
((SAV8LA)SAV).SetValue(SaveBlockAccessor8LA.KGameLanguage, (uint)value);
|
||||
}
|
||||
}
|
||||
|
||||
private Span<byte> OT_Trash => Data.AsSpan(0x20, 0x1A);
|
||||
|
||||
public string OT
|
||||
{
|
||||
get => SAV.GetString(OT_Trash);
|
||||
set => SAV.SetString(OT_Trash, value.AsSpan(), SAV.OTLength, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
public byte Unk_0x50
|
||||
{
|
||||
get => Data[Offset + 0x50];
|
||||
set => Data[Offset + 0x50] = value;
|
||||
}
|
||||
}
|
12
PKHeX.Core/Saves/Substructures/Gen8/LA/Party8a.cs
Normal file
12
PKHeX.Core/Saves/Substructures/Gen8/LA/Party8a.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class Party8a : SaveBlock
|
||||
{
|
||||
public Party8a(SAV8LA sav, SCBlock block) : base(sav, block.Data) { }
|
||||
|
||||
public int PartyCount
|
||||
{
|
||||
get => Data[6 * PokeCrypto.SIZE_8APARTY];
|
||||
set => Data[6 * PokeCrypto.SIZE_8APARTY] = (byte)value;
|
||||
}
|
||||
}
|
24
PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs
Normal file
24
PKHeX.Core/Saves/Substructures/Gen8/LA/PlayTime8a.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the amount of time the save file has been played.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class PlayTime8a : SaveBlock
|
||||
{
|
||||
public PlayTime8a(SAV8LA sav, SCBlock block) : base(sav, block.Data) { }
|
||||
|
||||
public ushort PlayedHours
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(Offset));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(Offset), value);
|
||||
}
|
||||
|
||||
public byte PlayedMinutes { get => Data[Offset + 2]; set => Data[Offset + 2] = value; }
|
||||
public byte PlayedSeconds { get => Data[Offset + 3]; set => Data[Offset + 3] = value; }
|
||||
public string LastSavedTime => $"{PlayedHours:0000}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not :
|
||||
}
|
26
PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs
Normal file
26
PKHeX.Core/Saves/Substructures/Gen8/LA/PlayerFashion8a.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the selected appearance choices of the player.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public sealed class PlayerFashion8a : SaveBlock
|
||||
{
|
||||
public PlayerFashion8a(SaveFile sav, SCBlock block) : base(sav, block.Data) { }
|
||||
public ulong Hair { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x00)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x00), value); }
|
||||
public ulong Contacts { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x08)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x08), value); }
|
||||
public ulong Eyebrows { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x10)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x10), value); }
|
||||
public ulong Glasses { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x18)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x18), value); }
|
||||
public ulong Hat { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x20)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x20), value); }
|
||||
public ulong Top { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x28)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x28), value); }
|
||||
public ulong Bottoms { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x30)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x30), value); }
|
||||
public ulong _38 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x38)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x38), value); }
|
||||
public ulong Shoes { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x40)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x40), value); }
|
||||
public ulong _48 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x48)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x48), value); }
|
||||
public ulong _50 { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x50)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x50), value); }
|
||||
public ulong Skin { get => ReadUInt64LittleEndian(Data.AsSpan(Offset + 0x58)); set => WriteUInt64LittleEndian(Data.AsSpan(Offset + 0x58), value); }
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Thresholds and research tasks used for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public static class PokedexConstants8a
|
||||
{
|
||||
public const int MaxPokedexResearchPoints = 60000;
|
||||
|
||||
public static readonly int[] ResearchPointsForRank =
|
||||
{
|
||||
0, 500, 1800, 3500, 6000, 8500, 11000, 15000, 20000, 30000, 60000,
|
||||
};
|
||||
|
||||
public static readonly ushort[] PokemonInfoIds =
|
||||
{
|
||||
0, 25, 26, 35, 36, 37, 38, 41, 42, 46,
|
||||
47, 54, 55, 63, 64, 65, 66, 67, 68, 72,
|
||||
73, 74, 75, 76, 77, 78, 81, 82, 92, 93,
|
||||
94, 95, 108, 111, 112, 113, 114, 122, 123, 125,
|
||||
126, 129, 130, 133, 134, 135, 136, 137, 143, 155,
|
||||
156, 169, 172, 173, 175, 176, 185, 190, 193, 196,
|
||||
197, 198, 200, 201, 207, 208, 212, 214, 215, 216,
|
||||
217, 220, 221, 223, 224, 226, 233, 234, 239, 240,
|
||||
242, 265, 266, 267, 268, 269, 280, 281, 282, 299,
|
||||
315, 339, 340, 355, 356, 358, 361, 362, 363, 364,
|
||||
365, 387, 388, 389, 390, 391, 392, 393, 394, 395,
|
||||
396, 397, 398, 399, 400, 401, 402, 403, 404, 405,
|
||||
406, 407, 408, 409, 410, 411, 412, 413, 414, 415,
|
||||
416, 417, 418, 419, 420, 421, 422, 423, 424, 425,
|
||||
426, 427, 428, 429, 430, 431, 432, 433, 434, 435,
|
||||
436, 437, 438, 439, 440, 441, 442, 443, 444, 445,
|
||||
446, 447, 448, 449, 450, 451, 452, 453, 454, 455,
|
||||
456, 457, 458, 459, 460, 461, 462, 463, 464, 465,
|
||||
466, 467, 468, 469, 470, 471, 472, 473, 474, 475,
|
||||
476, 477, 478, 479, 480, 481, 482, 483, 484, 485,
|
||||
486, 487, 488, 489, 490, 491, 492, 493, 501, 502,
|
||||
548, 627, 641, 642, 645, 700, 704, 712, 722, 723,
|
||||
899, 900, 901, 902, 903, 904, 905, 2048, 2085, 2086,
|
||||
2106, 2107, 2148, 2149, 2205, 2249, 2259, 2263, 2460, 2461,
|
||||
2462, 2469, 2470, 2471, 2527, 2531, 2532, 2535, 2540, 2541,
|
||||
2551, 2597, 2618, 2619, 2676, 2689, 2690, 2693, 2753, 2754,
|
||||
2761, 2772, 2948, 2950, 2953, 4155, 4197, 4297, 4508, 4509,
|
||||
4510, 4575, 4589, 4645, 4646, 4809, 6345, 6623, 6637, 8393,
|
||||
8671, 8685, 10441, 10719, 10733, 12489, 12781, 14537, 14829, 16585,
|
||||
16877, 18633, 18925, 20681, 20973, 22729, 23021, 24777, 25069, 26825,
|
||||
27117, 28873, 29165, 30921, 31213, 32969, 33261, 35017, 35309, 37065,
|
||||
39113, 41161, 43209, 45257, 47305, 49353, 51401, 53449, 55497,
|
||||
};
|
||||
|
||||
public static readonly byte[] PokemonInfoGenders =
|
||||
{
|
||||
0x04, 0x03, 0x03, 0x08, 0x08, 0x08, 0x08, 0x03, 0x03, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x03, 0x03, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x04, 0x04, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x03, 0x03, 0x20, 0x08, 0x08, 0x03, 0x08,
|
||||
0x08, 0x03, 0x03, 0x03, 0x08, 0x08, 0x08, 0x04, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x03, 0x03, 0x08, 0x08,
|
||||
0x08, 0x03, 0x08, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x08,
|
||||
0x03, 0x08, 0x03, 0x08, 0x03, 0x08, 0x04, 0x08, 0x08, 0x08,
|
||||
0x20, 0x08, 0x08, 0x03, 0x08, 0x03, 0x08, 0x08, 0x08, 0x08,
|
||||
0x03, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
|
||||
0x08, 0x03, 0x08, 0x08, 0x08, 0x08, 0x08, 0x20, 0x10, 0x03,
|
||||
0x20, 0x03, 0x03, 0x03, 0x08, 0x08, 0x08, 0x08, 0x03, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x04, 0x04, 0x08, 0x08, 0x20, 0x08, 0x08, 0x03, 0x03, 0x03,
|
||||
0x08, 0x08, 0x08, 0x03, 0x03, 0x08, 0x08, 0x03, 0x03, 0x08,
|
||||
0x03, 0x03, 0x08, 0x03, 0x03, 0x03, 0x04, 0x08, 0x03, 0x03,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x03, 0x04, 0x10,
|
||||
0x08, 0x08, 0x20, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08,
|
||||
0x04, 0x04, 0x20, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x08,
|
||||
0x20, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x10, 0x08, 0x08, 0x20, 0x04, 0x08, 0x08,
|
||||
0x08, 0x08, 0x04, 0x04, 0x08, 0x04, 0x08, 0x03, 0x08, 0x20,
|
||||
0x10, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x08, 0x20, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x20, 0x20, 0x08, 0x04, 0x04, 0x08, 0x20,
|
||||
0x10, 0x04, 0x04, 0x20, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
};
|
||||
|
||||
public static readonly ushort[] FormStorageIndexIds =
|
||||
{
|
||||
0, 25, 26, 35, 36, 37, 38, 41, 42, 46, 47, 54, 55, 63, 64, 65,
|
||||
66, 67, 68, 72, 73, 74, 75, 76, 77, 78, 81, 82, 92, 93, 94, 95,
|
||||
108, 111, 112, 113, 114, 122, 123, 125, 126, 129, 130, 133, 134, 135, 136, 137,
|
||||
143, 155, 156, 169, 172, 173, 175, 176, 185, 190, 193, 196, 197, 198, 200, 201,
|
||||
207, 208, 212, 214, 215, 216, 217, 220, 221, 223, 224, 226, 233, 234, 239, 240,
|
||||
242, 265, 266, 267, 268, 269, 280, 281, 282, 299, 315, 339, 340, 355, 356, 358,
|
||||
361, 362, 363, 364, 365, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397,
|
||||
398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413,
|
||||
414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429,
|
||||
430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445,
|
||||
446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461,
|
||||
462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477,
|
||||
478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493,
|
||||
501, 502, 548, 627, 641, 642, 645, 700, 704, 712, 722, 723, 899, 900, 901, 902,
|
||||
903, 904, 905, 2085, 2086, 2106, 2107, 2148, 2149, 2205, 2249, 2259, 2263, 2460, 2461, 2462,
|
||||
2469, 2470, 2471, 2527, 2531, 2532, 2535, 2540, 2541, 2551, 2597, 2618, 2619, 2676, 2689, 2690,
|
||||
2693, 2753, 2754, 2761, 2772, 2948, 2950, 2953, 4155, 4197, 4297, 4508, 4509, 4510, 4575, 4589,
|
||||
4645, 4646, 4809, 6345, 6623, 6637, 8393, 8671, 8685, 10441, 10719, 10733, 12489, 12781, 14537, 14829,
|
||||
16585, 16877, 18633, 18925, 20681, 20973, 22729, 23021, 24777, 25069, 26825, 27117, 28873, 29165, 30921, 31213,
|
||||
32969, 33261, 35017, 35309, 37065, 39113, 41161, 43209, 45257, 47305, 49353, 51401, 53449, 55497,
|
||||
};
|
||||
|
||||
public static readonly ushort[] FormStorageIndexValues =
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 14, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
||||
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
|
||||
56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
100, 101, 103, 104, 105, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
|
||||
118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
|
||||
134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
|
||||
150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 167,
|
||||
170, 173, 174, 175, 176, 177, 178, 179, 181, 183, 185, 186, 187, 188, 189, 190,
|
||||
191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206,
|
||||
207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
|
||||
223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238,
|
||||
239, 240, 246, 247, 248, 249, 251, 253, 254, 255, 257, 258, 259, 260, 261, 263,
|
||||
281, 282, 284, 290, 292, 294, 296, 298, 299, 302, 305, 306, 308, 309, 311, 312,
|
||||
314, 315, 316, 6, 8, 15, 16, 37, 38, 59, 73, 102, 106, 165, 168, 171,
|
||||
180, 182, 184, 241, 250, 252, 256, 262, 264, 283, 285, 288, 289, 291, 293, 295,
|
||||
297, 300, 301, 303, 307, 310, 313, 317, 17, 39, 74, 166, 169, 172, 242, 265,
|
||||
286, 287, 304, 75, 243, 266, 76, 244, 267, 77, 245, 268, 78, 269, 79, 270,
|
||||
80, 271, 81, 272, 82, 273, 83, 274, 84, 275, 85, 276, 86, 277, 87, 278,
|
||||
88, 279, 89, 280, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
|
||||
};
|
||||
|
||||
public static readonly PokedexResearchTask8a[][] ResearchTasks = DeserializeResearchTasks(BinLinkerAccessor.Get(Util.GetBinaryResource("researchtask_la.pkl"), "la"));
|
||||
|
||||
private static PokedexResearchTask8a[][] DeserializeResearchTasks(BinLinkerAccessor accessor)
|
||||
{
|
||||
var result = new PokedexResearchTask8a[accessor.Length][];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
result[i] = PokedexResearchTask8a.DeserializeFrom(accessor[i]);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
using static PKHeX.Core.PokedexResearchTaskType8a;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Research Task definition for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public sealed class PokedexResearchTask8a
|
||||
{
|
||||
public readonly PokedexResearchTaskType8a Task;
|
||||
public readonly int Threshold;
|
||||
public readonly int Move;
|
||||
public readonly MoveType Type;
|
||||
public readonly PokedexTimeOfDay8a TimeOfDay;
|
||||
public readonly ulong Hash_06;
|
||||
public readonly ulong Hash_07;
|
||||
public readonly ulong Hash_08;
|
||||
public readonly byte[] TaskThresholds;
|
||||
public readonly int PointsSingle;
|
||||
public readonly int PointsBonus;
|
||||
public readonly bool RequiredForCompletion;
|
||||
public readonly int Index;
|
||||
|
||||
private const int SIZE = 0x28;
|
||||
|
||||
public PokedexResearchTask8a() : this(stackalloc byte[SIZE]) { }
|
||||
|
||||
private PokedexResearchTask8a(ReadOnlySpan<byte> data)
|
||||
{
|
||||
Task = (PokedexResearchTaskType8a)data[0x00];
|
||||
PointsSingle = data[0x01];
|
||||
PointsBonus = data[0x02];
|
||||
Threshold = data[0x03];
|
||||
Move = ReadUInt16LittleEndian(data[0x04..]);
|
||||
Type = (MoveType)data[0x06];
|
||||
TimeOfDay = (PokedexTimeOfDay8a)data[0x07];
|
||||
Hash_06 = ReadUInt64LittleEndian(data[0x08..]);
|
||||
Hash_07 = ReadUInt64LittleEndian(data[0x10..]);
|
||||
Hash_08 = ReadUInt64LittleEndian(data[0x18..]);
|
||||
TaskThresholds = data.Slice(0x21, data[0x20]).ToArray();
|
||||
RequiredForCompletion = data[0x26] != 0;
|
||||
|
||||
Index = Task is UseMove or DefeatWithMoveType ? data[0x27] : -1;
|
||||
}
|
||||
|
||||
public static PokedexResearchTask8a[] DeserializeFrom(ReadOnlySpan<byte> data)
|
||||
{
|
||||
// 00: u8 task
|
||||
// 01: u8 points_single
|
||||
// 02: u8 points_bonus
|
||||
// 03: u8 threshold
|
||||
// 04: u16 move
|
||||
// 06: u8 type
|
||||
// 07: u8 time of day
|
||||
// 08: u64 hash_06
|
||||
// 10: u64 hash_07
|
||||
// 18: u64 hash_08
|
||||
// 20: u8 num_thresholds
|
||||
// 21: u8 thresholds[5]
|
||||
// 26: u8 required
|
||||
// 27: u8 multi_index
|
||||
|
||||
var result = new PokedexResearchTask8a[data.Length / SIZE];
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
result[i] = new PokedexResearchTask8a(data.Slice(SIZE * i, SIZE));
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.PokedexResearchTaskType8a;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Research Task types for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public enum PokedexResearchTaskType8a : byte
|
||||
{
|
||||
Catch = 0,
|
||||
UseMove = 1,
|
||||
DefeatWithMoveType = 2,
|
||||
Defeat = 3,
|
||||
Evolve = 4,
|
||||
CatchAlpha = 5,
|
||||
CatchLarge = 6,
|
||||
CatchSmall = 7,
|
||||
CatchHeavy = 8,
|
||||
CatchLight = 9,
|
||||
CatchAtTime = 10,
|
||||
CatchSleeping = 11,
|
||||
CatchInAir = 12,
|
||||
CatchNotSpotted = 13,
|
||||
GiveFood = 14,
|
||||
StunWithItems = 15,
|
||||
ScareWithScatterBang = 16,
|
||||
LureWithPokeshiDoll = 17,
|
||||
UseStrongStyleMove = 18,
|
||||
UseAgileStyleMove = 19,
|
||||
LeapFromTrees = 20,
|
||||
LeapFromLeaves = 21,
|
||||
LeapFromSnow = 22,
|
||||
LeapFromOre = 23,
|
||||
LeapFromTussocks = 24,
|
||||
ObtainForms = 25,
|
||||
PartOfArceus = 26,
|
||||
SpeciesQuest = 27,
|
||||
}
|
||||
|
||||
public static class PokedexResearchTask8aExtensions
|
||||
{
|
||||
public static bool CanSetCurrentValue(this PokedexResearchTaskType8a task) => task switch
|
||||
{
|
||||
ObtainForms => false,
|
||||
PartOfArceus => false,
|
||||
SpeciesQuest => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
public static string GetTaskLabelString(this PokedexResearchTask8a task, IReadOnlyList<string> TaskDescriptions, IReadOnlyList<string> TimeTaskDescriptions, IReadOnlyList<string> SpeciesQuests) => task.Task switch
|
||||
{
|
||||
SpeciesQuest => GetSpeciesQuestLabel(task.Hash_06, SpeciesQuests),
|
||||
UseMove => GetGenericTaskLabelString(task.Task, task.Index, task.Move, TaskDescriptions, TimeTaskDescriptions),
|
||||
DefeatWithMoveType => GetGenericTaskLabelString(task.Task, task.Index, (int)task.Type, TaskDescriptions, TimeTaskDescriptions),
|
||||
CatchAtTime => GetGenericTaskLabelString(task.Task, task.Index, (int)task.TimeOfDay, TaskDescriptions, TimeTaskDescriptions),
|
||||
_ => GetGenericTaskLabelString(task.Task, task.Index, (int)task.TimeOfDay, TaskDescriptions, TimeTaskDescriptions),
|
||||
};
|
||||
|
||||
public static string GetGenericTaskLabelString(PokedexResearchTaskType8a task, int idx, int param, IReadOnlyList<string> TaskDescriptions, IReadOnlyList<string> TimeTaskDescriptions) => task switch
|
||||
{
|
||||
UseMove => string.Format(TaskDescriptions[(int)task], param >= 0 ? GameInfo.Strings.Move[param] : $"(idx={idx})"),
|
||||
DefeatWithMoveType => string.Format(TaskDescriptions[(int)task], param >= 0 ? GameInfo.Strings.Types[param] : $"(idx={idx})"),
|
||||
CatchAtTime => TimeTaskDescriptions[param],
|
||||
_ => TaskDescriptions[(int)task],
|
||||
};
|
||||
|
||||
private static string GetSpeciesQuestLabel(ulong hash, IReadOnlyList<string> labels) => hash switch
|
||||
{
|
||||
0xE68C0D2852AF9068 => labels[0],
|
||||
0xE68C0E2852AF921B => labels[1],
|
||||
0xE68F7B2852B28129 => labels[2],
|
||||
0xE68F7C2852B282DC => labels[3],
|
||||
0xE68F7E2852B28642 => labels[4],
|
||||
0xE68F812852B28B5B => labels[5],
|
||||
0xE68F802852B289A8 => labels[6],
|
||||
0xE6850D2852A971BA => labels[7],
|
||||
0xE6888B2852AC7DAB => labels[8],
|
||||
0xE6888F2852AC8477 => labels[9],
|
||||
0xE688842852AC71C6 => labels[10],
|
||||
0xE69D122852BE0C1A => labels[12],
|
||||
0xE69D092852BDFCCF => labels[13],
|
||||
0xE6927D2852B4BA66 => labels[14],
|
||||
0xE696022852B7D23C => labels[16],
|
||||
0xE67080285297D919 => labels[17],
|
||||
0xE6708C285297ED7D => labels[18],
|
||||
0xE6740B28529AFB21 => labels[19],
|
||||
0xE6740D28529AFE87 => labels[20],
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(hash)),
|
||||
};
|
||||
}
|
1363
PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSave8a.cs
Normal file
1363
PKHeX.Core/Saves/Substructures/Gen8/LA/Pokedex/PokedexSave8a.cs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,135 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents direct manipulation and access for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public class PokedexSaveData
|
||||
{
|
||||
private readonly PokedexSaveGlobalData GlobalData;
|
||||
private readonly PokedexSaveLocalData[] LocalData;
|
||||
private readonly PokedexSaveResearchEntry[] ResearchEntries;
|
||||
private readonly PokedexSaveStatisticsEntry[] StatisticsEntries;
|
||||
|
||||
public const int POKEDEX_SAVE_DATA_SIZE = 0x1E460;
|
||||
|
||||
public const int STATISTICS_ENTRIES_MAX = 1480;
|
||||
|
||||
public PokedexSaveData(byte[] data)
|
||||
{
|
||||
if (data.Length != POKEDEX_SAVE_DATA_SIZE)
|
||||
throw new ArgumentException($"Unexpected {nameof(PokedexSaveData)} block size!");
|
||||
|
||||
GlobalData = new PokedexSaveGlobalData(data, 0);
|
||||
|
||||
LocalData = new PokedexSaveLocalData[5];
|
||||
for (var i = 0; i < LocalData.Length; i++)
|
||||
LocalData[i] = new PokedexSaveLocalData(data, 0x10 + (0x10 * i));
|
||||
|
||||
ResearchEntries = new PokedexSaveResearchEntry[PokedexSave8a.MAX_SPECIES];
|
||||
for (var i = 0; i < ResearchEntries.Length; i++)
|
||||
ResearchEntries[i] = new PokedexSaveResearchEntry(data, 0x70 + (0x58 * i));
|
||||
|
||||
StatisticsEntries = new PokedexSaveStatisticsEntry[STATISTICS_ENTRIES_MAX];
|
||||
for (var i = 0; i < StatisticsEntries.Length; i++)
|
||||
StatisticsEntries[i] = new PokedexSaveStatisticsEntry(data, 0x151A8 + (0x18 * i));
|
||||
}
|
||||
|
||||
public bool IsPokedexCompleted(PokedexType8a which) => (GlobalData.Flags & (which < PokedexType8a.Count ? (1 << (int)which) : 1)) != 0;
|
||||
public bool IsPokedexPerfect(PokedexType8a which) => (GlobalData.Flags & ((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6)) != 0;
|
||||
|
||||
public void SetPokedexCompleted(PokedexType8a which) => GlobalData.Flags |= (uint)(which < PokedexType8a.Count ? (1 << (int)which) : 1);
|
||||
public void SetPokedexPerfect(PokedexType8a which) => GlobalData.Flags |= (uint)((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6);
|
||||
|
||||
public PokedexSaveResearchEntry GetResearchEntry(int species) => ResearchEntries[species];
|
||||
|
||||
public bool TryGetStatisticsEntry(int species, int form, out PokedexSaveStatisticsEntry entry)
|
||||
{
|
||||
var fstIdIndex = Array.BinarySearch(PokedexConstants8a.FormStorageIndexIds, (ushort)(species | (form << 11)));
|
||||
if (fstIdIndex >= 0)
|
||||
{
|
||||
entry = StatisticsEntries[PokedexConstants8a.FormStorageIndexValues[fstIdIndex]];
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new PokedexSaveStatisticsEntry(new byte[0x18], 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetStatisticsEntry(PKM pk, out PokedexSaveStatisticsEntry entry, out int shift)
|
||||
{
|
||||
shift = 0;
|
||||
if (pk.IsShiny)
|
||||
shift += 4;
|
||||
if (((IAlpha)pk).IsAlpha)
|
||||
shift += 2;
|
||||
if ((pk.Gender & ~2) != 0)
|
||||
shift++;
|
||||
|
||||
return TryGetStatisticsEntry(pk.Species, pk.Form, out entry);
|
||||
}
|
||||
|
||||
public int GetPokeGetCount(int species) => GetResearchEntry(species).NumObtained;
|
||||
public int GetPokeResearchRate(int species) => GetResearchEntry(species).ResearchRate;
|
||||
|
||||
public ushort NextUpdateCounter()
|
||||
{
|
||||
var curCounter = GlobalData.UpdateCounter;
|
||||
if (curCounter < PokedexConstants8a.MaxPokedexResearchPoints)
|
||||
GlobalData.UpdateCounter = (ushort)(curCounter + 1);
|
||||
|
||||
return curCounter;
|
||||
}
|
||||
|
||||
public void IncrementReportCounter()
|
||||
{
|
||||
var reportCounter = GlobalData.ReportCounter;
|
||||
|
||||
// If we need to re-set the counter, reset all species counters
|
||||
if (reportCounter >= PokedexConstants8a.MaxPokedexResearchPoints)
|
||||
{
|
||||
foreach (var entry in ResearchEntries)
|
||||
{
|
||||
if (entry.LastUpdatedReportCounter != 0)
|
||||
entry.LastUpdatedReportCounter = 1;
|
||||
}
|
||||
|
||||
reportCounter = 1;
|
||||
}
|
||||
|
||||
GlobalData.ReportCounter = (ushort)(reportCounter + 1);
|
||||
}
|
||||
|
||||
public ushort GetReportCounter() => GlobalData.ReportCounter;
|
||||
|
||||
public int GetTotalResearchPoint() => GlobalData.TotalResearchPoints;
|
||||
|
||||
public void SetTotalResearchPoint(int tp) => GlobalData.TotalResearchPoints = tp;
|
||||
|
||||
public bool HasAnyReport(int species) => GetResearchEntry(species).HasAnyReport;
|
||||
public bool IsPerfect(int species) => GetResearchEntry(species).IsPerfect;
|
||||
|
||||
public void SetGlobalField04(byte v) => GlobalData.Field_04 = v;
|
||||
public byte GetGlobalField04() => GlobalData.Field_04;
|
||||
|
||||
public void SetLocalField03(int idx, byte v) => LocalData[idx].Field_03 = v;
|
||||
|
||||
public byte GetLocalField03(int idx) => LocalData[idx].Field_03;
|
||||
|
||||
public void GetLocalParameters(int idx, out byte outField02, out ushort outField00, out uint outField04, out uint outField08, out ushort outField0C)
|
||||
{
|
||||
var local = LocalData[idx];
|
||||
|
||||
outField02 = local.Field_02;
|
||||
outField00 = local.Field_00;
|
||||
outField04 = local.Field_04;
|
||||
outField08 = local.Field_08;
|
||||
outField0C = local.Field_0C;
|
||||
}
|
||||
|
||||
public void SetGlobalFormField(int form) => GlobalData.FormField = (byte)form;
|
||||
public int GetGlobalFormField() => GlobalData.FormField;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Revision details for the <see cref="GameVersion.PLA"/> Pokédex.
|
||||
/// </summary>
|
||||
public class PokedexSaveGlobalData
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly int Offset;
|
||||
|
||||
public PokedexSaveGlobalData(byte[] data, int offset) => (_data, Offset) = (data, offset);
|
||||
private Span<byte> Data => _data.AsSpan(Offset);
|
||||
|
||||
public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public byte Field_04 { get => Data[4]; set => Data[4] = value; }
|
||||
public byte FormField { get => Data[5]; set => Data[5] = value; }
|
||||
public ushort UpdateCounter { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); }
|
||||
public ushort ReportCounter { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], value); }
|
||||
public ushort Field_0A { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], value); }
|
||||
public int TotalResearchPoints { get => ReadInt32LittleEndian(Data[0x0C..]); set => WriteInt32LittleEndian(Data[0x0C..], value); }
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Local region (within Hisui) <see cref="GameVersion.PLA"/> Pokédex structure.
|
||||
/// </summary>
|
||||
public class PokedexSaveLocalData
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly int Offset;
|
||||
|
||||
public PokedexSaveLocalData(byte[] data, int offset) => (_data, Offset) = (data, offset);
|
||||
private Span<byte> Data => _data.AsSpan(Offset);
|
||||
|
||||
public ushort Field_00 { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); }
|
||||
public byte Field_02 { get => Data[2]; set => Data[2] = value; }
|
||||
public byte Field_03 { get => Data[3]; set => Data[3] = value; }
|
||||
public uint Field_04 { get => ReadUInt32LittleEndian(Data[0x04..]); set => WriteUInt32LittleEndian(Data[0x04..], value); }
|
||||
public uint Field_08 { get => ReadUInt32LittleEndian(Data[0x08..]); set => WriteUInt32LittleEndian(Data[0x08..], value); }
|
||||
public ushort Field_0C { get => ReadUInt16LittleEndian(Data[0x0C..]); set => WriteUInt16LittleEndian(Data[0x0C..], value); }
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
using static PKHeX.Core.PokedexResearchTaskType8a;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Per-species research logs used for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public class PokedexSaveResearchEntry
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly int Offset;
|
||||
|
||||
public PokedexSaveResearchEntry(byte[] data, int offset) => (_data, Offset) = (data, offset);
|
||||
private Span<byte> Data => _data.AsSpan(Offset);
|
||||
|
||||
public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public bool HasEverBeenUpdated { get => (Flags & (1u << 0)) != 0; set => Flags = (Flags & ~(1u << 0)) | ((value ? 1u : 0u) << 0); }
|
||||
public bool HasAnyReport { get => (Flags & (1u << 1)) != 0; set => Flags = (Flags & ~(1u << 1)) | ((value ? 1u : 0u) << 1); }
|
||||
public bool IsPerfect { get => (Flags & (1u << 2)) != 0; set => Flags = (Flags & ~(1u << 2)) | ((value ? 1u : 0u) << 2); }
|
||||
public bool SelectedGender1 { get => (Flags & (1u << 3)) != 0; set => Flags = (Flags & ~(1u << 3)) | ((value ? 1u : 0u) << 3); }
|
||||
public bool SelectedShiny { get => (Flags & (1u << 4)) != 0; set => Flags = (Flags & ~(1u << 4)) | ((value ? 1u : 0u) << 4); }
|
||||
public bool SelectedAlpha { get => (Flags & (1u << 5)) != 0; set => Flags = (Flags & ~(1u << 5)) | ((value ? 1u : 0u) << 5); }
|
||||
|
||||
private uint ReportedResearchProgress { get => ReadUInt32LittleEndian(Data[0x04..]); set => WriteUInt32LittleEndian(Data[0x04..], value); }
|
||||
|
||||
public int GetReportedResearchProgress(int idx) => (int)((ReportedResearchProgress >> (3 * idx)) & 7);
|
||||
public void SetReportedResearchProgress(int idx, int level)
|
||||
{
|
||||
var prog = ReportedResearchProgress;
|
||||
prog &= ~(7u << (3 * idx));
|
||||
prog |= (uint)((level & 7u) << (3 * idx));
|
||||
ReportedResearchProgress = prog;
|
||||
}
|
||||
|
||||
public ushort ResearchRate { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], value); }
|
||||
public ushort NumObtained { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], value); }
|
||||
public ushort UpdateCounter { get => ReadUInt16LittleEndian(Data[0x0C..]); set => WriteUInt16LittleEndian(Data[0x0C..], value); }
|
||||
public ushort LastUpdatedReportCounter { get => ReadUInt16LittleEndian(Data[0x0E..]); set => WriteUInt16LittleEndian(Data[0x0E..], value); }
|
||||
|
||||
public ushort GetMoveUseCount(int index)
|
||||
{
|
||||
if (index >= 4)
|
||||
return 0;
|
||||
|
||||
return ReadUInt16LittleEndian(Data[(0x10 + (0x2 * index))..]);
|
||||
}
|
||||
|
||||
public void SetMoveUseCount(int index, ushort newCount)
|
||||
{
|
||||
if (index >= 4)
|
||||
return;
|
||||
|
||||
WriteUInt16LittleEndian(Data[(0x10 + (0x2 * index))..], newCount);
|
||||
}
|
||||
|
||||
public ushort GetDefeatWithMoveTypeCount(int index)
|
||||
{
|
||||
if (index >= 3)
|
||||
return 0;
|
||||
|
||||
return ReadUInt16LittleEndian(Data[(0x18 + (0x2 * index))..]);
|
||||
}
|
||||
|
||||
public void SetDefeatWithMoveTypeCount(int index, ushort newCount)
|
||||
{
|
||||
if (index >= 3)
|
||||
return;
|
||||
|
||||
WriteUInt16LittleEndian(Data[(0x18 + (0x2 * index))..], newCount);
|
||||
}
|
||||
|
||||
public ushort NumDefeated { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], value); }
|
||||
public ushort NumEvolved { get => ReadUInt16LittleEndian(Data[0x20..]); set => WriteUInt16LittleEndian(Data[0x20..], value); }
|
||||
public ushort NumAlphaCaught { get => ReadUInt16LittleEndian(Data[0x22..]); set => WriteUInt16LittleEndian(Data[0x22..], value); }
|
||||
public ushort NumLargeCaught { get => ReadUInt16LittleEndian(Data[0x24..]); set => WriteUInt16LittleEndian(Data[0x24..], value); }
|
||||
public ushort NumSmallCaught { get => ReadUInt16LittleEndian(Data[0x26..]); set => WriteUInt16LittleEndian(Data[0x26..], value); }
|
||||
public ushort NumHeavyCaught { get => ReadUInt16LittleEndian(Data[0x28..]); set => WriteUInt16LittleEndian(Data[0x28..], value); }
|
||||
public ushort NumLightCaught { get => ReadUInt16LittleEndian(Data[0x2A..]); set => WriteUInt16LittleEndian(Data[0x2A..], value); }
|
||||
public ushort NumCaughtAtTime { get => ReadUInt16LittleEndian(Data[0x2C..]); set => WriteUInt16LittleEndian(Data[0x2C..], value); }
|
||||
public ushort NumCaughtSleeping { get => ReadUInt16LittleEndian(Data[0x2E..]); set => WriteUInt16LittleEndian(Data[0x2E..], value); }
|
||||
public ushort NumCaughtInAir { get => ReadUInt16LittleEndian(Data[0x30..]); set => WriteUInt16LittleEndian(Data[0x30..], value); }
|
||||
public ushort NumCaughtNotSpotted { get => ReadUInt16LittleEndian(Data[0x32..]); set => WriteUInt16LittleEndian(Data[0x32..], value); }
|
||||
public ushort NumGivenFood { get => ReadUInt16LittleEndian(Data[0x34..]); set => WriteUInt16LittleEndian(Data[0x34..], value); }
|
||||
public ushort NumStunnedWithItems { get => ReadUInt16LittleEndian(Data[0x36..]); set => WriteUInt16LittleEndian(Data[0x36..], value); }
|
||||
public ushort NumScared { get => ReadUInt16LittleEndian(Data[0x38..]); set => WriteUInt16LittleEndian(Data[0x38..], value); }
|
||||
public ushort NumLured { get => ReadUInt16LittleEndian(Data[0x3A..]); set => WriteUInt16LittleEndian(Data[0x3A..], value); }
|
||||
public ushort NumStrongStyleMovesUsed { get => ReadUInt16LittleEndian(Data[0x3C..]); set => WriteUInt16LittleEndian(Data[0x3C..], value); }
|
||||
public ushort NumAgileStyleMovesUsed { get => ReadUInt16LittleEndian(Data[0x3E..]); set => WriteUInt16LittleEndian(Data[0x3E..], value); }
|
||||
public ushort NumLeapFromTrees { get => ReadUInt16LittleEndian(Data[0x40..]); set => WriteUInt16LittleEndian(Data[0x40..], value); }
|
||||
public ushort NumLeapFromLeaves { get => ReadUInt16LittleEndian(Data[0x42..]); set => WriteUInt16LittleEndian(Data[0x42..], value); }
|
||||
public ushort NumLeapFromSnow { get => ReadUInt16LittleEndian(Data[0x44..]); set => WriteUInt16LittleEndian(Data[0x44..], value); }
|
||||
public ushort NumLeapFromOre { get => ReadUInt16LittleEndian(Data[0x46..]); set => WriteUInt16LittleEndian(Data[0x46..], value); }
|
||||
public ushort NumLeapFromTussocks { get => ReadUInt16LittleEndian(Data[0x48..]); set => WriteUInt16LittleEndian(Data[0x48..], value); }
|
||||
public ushort Field_4A { get => ReadUInt16LittleEndian(Data[0x4A..]); set => WriteUInt16LittleEndian(Data[0x4A..], value); }
|
||||
public uint Field_4C { get => ReadUInt32LittleEndian(Data[0x4C..]); set => WriteUInt32LittleEndian(Data[0x4C..], value); }
|
||||
public byte SelectedForm { get => Data[0x50]; set => Data[0x50] = value; }
|
||||
// 51-53 padding
|
||||
public uint Field_54 { get => ReadUInt32LittleEndian(Data[0x54..]); set => WriteUInt32LittleEndian(Data[0x54..], value); }
|
||||
|
||||
public void IncreaseCurrentResearchLevel(PokedexResearchTaskType8a task, int idx, ushort delta)
|
||||
{
|
||||
SetCurrentResearchLevel(task, idx, GetCurrentResearchLevel(task, idx) + delta);
|
||||
}
|
||||
|
||||
public int GetCurrentResearchLevel(PokedexResearchTaskType8a task, int idx) => task switch
|
||||
{
|
||||
Catch => NumObtained,
|
||||
UseMove => GetMoveUseCount(idx),
|
||||
DefeatWithMoveType => GetDefeatWithMoveTypeCount(idx),
|
||||
Defeat => NumDefeated,
|
||||
Evolve => NumEvolved,
|
||||
CatchAlpha => NumAlphaCaught,
|
||||
CatchLarge => NumLargeCaught,
|
||||
CatchSmall => NumSmallCaught,
|
||||
CatchHeavy => NumHeavyCaught,
|
||||
CatchLight => NumLightCaught,
|
||||
CatchAtTime => NumCaughtAtTime,
|
||||
CatchSleeping => NumCaughtSleeping,
|
||||
CatchInAir => NumCaughtInAir,
|
||||
CatchNotSpotted => NumCaughtNotSpotted,
|
||||
GiveFood => NumGivenFood,
|
||||
StunWithItems => NumStunnedWithItems,
|
||||
ScareWithScatterBang => NumScared,
|
||||
LureWithPokeshiDoll => NumLured,
|
||||
UseStrongStyleMove => NumStrongStyleMovesUsed,
|
||||
UseAgileStyleMove => NumAgileStyleMovesUsed,
|
||||
LeapFromTrees => NumLeapFromTrees,
|
||||
LeapFromLeaves => NumLeapFromLeaves,
|
||||
LeapFromSnow => NumLeapFromSnow,
|
||||
LeapFromOre => NumLeapFromOre,
|
||||
LeapFromTussocks => NumLeapFromTussocks,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(task)),
|
||||
};
|
||||
|
||||
public void SetCurrentResearchLevel(PokedexResearchTaskType8a task, int idx, int value)
|
||||
{
|
||||
// Bound values in [0, 60000]
|
||||
if (value < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
|
||||
var cappedValue = (ushort)Math.Min(value, PokedexConstants8a.MaxPokedexResearchPoints);
|
||||
switch (task)
|
||||
{
|
||||
case Catch: NumObtained = cappedValue; break;
|
||||
case UseMove: SetMoveUseCount(idx, cappedValue); break;
|
||||
case DefeatWithMoveType: SetDefeatWithMoveTypeCount(idx, cappedValue); break;
|
||||
case Defeat: NumDefeated = cappedValue; break;
|
||||
case Evolve: NumEvolved = cappedValue; break;
|
||||
case CatchAlpha: NumAlphaCaught = cappedValue; break;
|
||||
case CatchLarge: NumLargeCaught = cappedValue; break;
|
||||
case CatchSmall: NumSmallCaught = cappedValue; break;
|
||||
case CatchHeavy: NumHeavyCaught = cappedValue; break;
|
||||
case CatchLight: NumLightCaught = cappedValue; break;
|
||||
case CatchAtTime: NumCaughtAtTime = cappedValue; break;
|
||||
case CatchSleeping: NumCaughtSleeping = cappedValue; break;
|
||||
case CatchInAir: NumCaughtInAir = cappedValue; break;
|
||||
case CatchNotSpotted: NumCaughtNotSpotted = cappedValue; break;
|
||||
case GiveFood: NumGivenFood = cappedValue; break;
|
||||
case StunWithItems: NumStunnedWithItems = cappedValue; break;
|
||||
case ScareWithScatterBang: NumScared = cappedValue; break;
|
||||
case LureWithPokeshiDoll: NumLured = cappedValue; break;
|
||||
case UseStrongStyleMove: NumStrongStyleMovesUsed = cappedValue; break;
|
||||
case UseAgileStyleMove: NumAgileStyleMovesUsed = cappedValue; break;
|
||||
case LeapFromTrees: NumLeapFromTrees = cappedValue; break;
|
||||
case LeapFromLeaves: NumLeapFromLeaves = cappedValue; break;
|
||||
case LeapFromSnow: NumLeapFromSnow = cappedValue; break;
|
||||
case LeapFromOre: NumLeapFromOre = cappedValue; break;
|
||||
case LeapFromTussocks: NumLeapFromTussocks = cappedValue; break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(task));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Per-species/form research logs used for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public class PokedexSaveStatisticsEntry
|
||||
{
|
||||
private readonly byte[] _data;
|
||||
private readonly int Offset;
|
||||
|
||||
public PokedexSaveStatisticsEntry(byte[] data, int offset) => (_data, Offset) = (data, offset);
|
||||
private Span<byte> Data => _data.AsSpan(Offset);
|
||||
|
||||
public uint Flags { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public bool HasMaximumStatistics { get => (Flags & (1u << 0)) != 0; set => Flags = (Flags & ~(1u << 0)) | ((value ? 1u : 0u) << 0); }
|
||||
public byte SeenInWildFlags { get => Data[4]; set => Data[4] = value; }
|
||||
public byte ObtainFlags { get => Data[5]; set => Data[5] = value; }
|
||||
public byte CaughtInWildFlags { get => Data[6]; set => Data[6] = value; }
|
||||
// 7 padding alignment
|
||||
|
||||
public float MinHeight { get => ReadSingleLittleEndian(Data[0x08..]); set => WriteSingleLittleEndian(Data[0x08..], value); }
|
||||
public float MaxHeight { get => ReadSingleLittleEndian(Data[0x0C..]); set => WriteSingleLittleEndian(Data[0x0C..], value); }
|
||||
public float MinWeight { get => ReadSingleLittleEndian(Data[0x10..]); set => WriteSingleLittleEndian(Data[0x10..], value); }
|
||||
public float MaxWeight { get => ReadSingleLittleEndian(Data[0x14..]); set => WriteSingleLittleEndian(Data[0x14..], value); }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Time of Day enumeration used for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public enum PokedexTimeOfDay8a
|
||||
{
|
||||
Morning = 0,
|
||||
Midday = 1,
|
||||
Evening = 2,
|
||||
Night = 3,
|
||||
DaylightHours = 4,
|
||||
Invalid = 5,
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Sub-Area location enumeration used for <see cref="GameVersion.PLA"/> Pokédex entries.
|
||||
/// </summary>
|
||||
public enum PokedexType8a
|
||||
{
|
||||
Hisui = 0,
|
||||
Local1 = 1,
|
||||
Local2 = 2,
|
||||
Local3 = 3,
|
||||
Local4 = 4,
|
||||
Local5 = 5,
|
||||
|
||||
Count = 6,
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Program generated report used to summarize the <see cref="GameVersion.PLA"/> Pokédex state.
|
||||
/// </summary>
|
||||
public sealed record PokedexUpdateInfo8a
|
||||
{
|
||||
public int ProgressPokeNum { get; init; }
|
||||
public int ProgressNum { get; init; }
|
||||
public int PointsGainedFromProgressPoke { get; init; }
|
||||
public int NewCompleteResearchNum { get; init; }
|
||||
public int PointsGainedFromCompleteResearch { get; init; }
|
||||
public int TotalResearchPointBeforeUpdate { get; init; }
|
||||
public int TotalResearchPointAfterUpdate { get; init; }
|
||||
public int RankBeforeUpdate { get; init; }
|
||||
public int PointsNeededForNextRankBeforeUpdate { get; init; }
|
||||
public int PointsNeededForNextRankAfterUpdate { get; init; }
|
||||
public int ProgressPercentToNextRankBeforeUpdate { get; init; }
|
||||
public int ProgressPercentToNextRankAfterUpdate { get; init; }
|
||||
public int TotalResearchPointAfterUpdate_Duplicate { get; init; }
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace PKHeX.Core
|
|||
public static class Meta8
|
||||
{
|
||||
public static SCBlock[] GetBlankDataSWSH() => GetBlankBlockArray(DefaultChunkSizesSWSH);
|
||||
public static SCBlock[] GetBlankDataLA() => GetBlankBlockArray(DefaultChunkSizesLA);
|
||||
|
||||
/// <summary>
|
||||
/// Create a blank block array using the provided <see cref="arr"/> definition.
|
||||
|
@ -267,5 +268,118 @@ namespace PKHeX.Core
|
|||
0xFEB13D33, 0x00002, 0xFEB64141, 0x00001, 0xFEE68CE1, 0x00004, 0xFF4498B1, 0x00002,
|
||||
0xFFA1F3C8, 0x00004,
|
||||
};
|
||||
|
||||
private static readonly uint[] DefaultChunkSizesLA =
|
||||
{
|
||||
0x00EF4BAE, 0x00140, 0x017C3CBB, 0x00001, 0x02168706, 0x1E460, 0x022A2253, 0x00001,
|
||||
0x024C8CF3, 0x00004, 0x0493AF74, 0x00004, 0x04AE4181, 0x00001, 0x04EABECF, 0x00004,
|
||||
0x050E9D63, 0x00004, 0x05E7EBEB, 0x02EE0, 0x062AF6C2, 0x00020, 0x069EAD42, 0x00004,
|
||||
0x070F34AD, 0x00020, 0x0821C372, 0x00004, 0x0A40A680, 0x00004, 0x0A49F0EF, 0x01F40,
|
||||
0x0B3C5217, 0x00004, 0x0BFDEBA1, 0x00004, 0x0E6246BE, 0x00004, 0x0F3AD63C, 0x00004,
|
||||
0x10D148DE, 0x00004, 0x111933CD, 0x00004, 0x11B37EC9, 0x000C8, 0x13A0E18B, 0x00004,
|
||||
0x17EB7C4D, 0x00004, 0x180A4E9F, 0x00004, 0x19722C89, 0x00440, 0x1B1E3D8B, 0x00004,
|
||||
0x1B5E0528, 0x00001, 0x1B65ABFD, 0x00004, 0x1BF70FCB, 0x00004, 0x1D482A63, 0x00004,
|
||||
0x1E0F1BA3, 0x00190, 0x203A7F34, 0x00008, 0x2085565F, 0x00001, 0x20855812, 0x00001,
|
||||
0x208559C5, 0x00001, 0x208D511F, 0x00004, 0x2123FE4A, 0x00004, 0x2137FAFF, 0x00004,
|
||||
0x22540FF0, 0x00004, 0x22DEF108, 0x00004, 0x23AA6AE3, 0x00004, 0x24E0D195, 0x002F2,
|
||||
0x250F3C75, 0x00004, 0x267DD9DA, 0x00070, 0x27931A29, 0x00004, 0x27986623, 0x00004,
|
||||
0x279EA6CD, 0x00004, 0x27C9C8C2, 0x00100, 0x2846B7DB, 0x00004, 0x2969F5EB, 0x00004,
|
||||
0x296C9DB8, 0x00004, 0x2985FE5D, 0x008D4, 0x298D9297, 0x00004, 0x29FB8D78, 0x00004,
|
||||
0x2BBF9423, 0x00001, 0x2BBF9789, 0x00001, 0x2BBF9CA2, 0x00001, 0x2BBF9E55, 0x00001,
|
||||
0x2C0B9BF3, 0x00004, 0x2C24C5F2, 0x00020, 0x2DBE7204, 0x04B00, 0x2EB1B190, 0x00020,
|
||||
0x2F85E20D, 0x00004, 0x305FD79A, 0x00008, 0x30967638, 0x00001, 0x3096799E, 0x00001,
|
||||
0x30967B51, 0x00001, 0x3279D927, 0x00004, 0x35BDC76F, 0x00001, 0x35BDC922, 0x00001,
|
||||
0x3745DA43, 0x00004, 0x385F9860, 0x00004, 0x388E378D, 0x00004, 0x3AAF5E5E, 0x00004,
|
||||
0x3ADB8A98, 0x000C8, 0x3B4D705E, 0x00001, 0x3B956C1A, 0x00004, 0x3BFC2C3C, 0x000D0,
|
||||
0x3EBEE1A7, 0x00004, 0x3F7CC8A4, 0x00004, 0x3F8120BA, 0x00002, 0x402FAC1D, 0x00001,
|
||||
0x4033C7DB, 0x00001, 0x40892A39, 0x00004, 0x40CC5A21, 0x00002, 0x40E13871, 0x00004,
|
||||
0x41309084, 0x00001, 0x416A4820, 0x00004, 0x416A49D3, 0x00004, 0x416A4D39, 0x00004,
|
||||
0x416A5252, 0x00004, 0x416A5405, 0x00004, 0x431018F0, 0x00004, 0x43749288, 0x00010,
|
||||
0x444D8A2C, 0x00001, 0x451E1BAF, 0x00020, 0x457495AE, 0x00010, 0x45851092, 0x00064,
|
||||
0x46749741, 0x00010, 0x477498D4, 0x00010, 0x47E1CEAB, 0x54600, 0x48749A67, 0x00010,
|
||||
0x48CE01F7, 0x000FC, 0x48DDB755, 0x00006, 0x4918E303, 0x00001, 0x49749BFA, 0x00010,
|
||||
0x4A749D8D, 0x00010, 0x4AA3F543, 0x00004, 0x4AA3F6F6, 0x00004, 0x4AAF7FBE, 0x00004,
|
||||
0x4BD70B32, 0x041A0, 0x4C5C85AB, 0x00004, 0x4D7EADDD, 0x00004, 0x4DB28157, 0x00004,
|
||||
0x4EB3ECBB, 0x00004, 0x4EE2B115, 0x00008, 0x4FBDB5FF, 0x00008, 0x509A1AC8, 0x00004,
|
||||
0x509A1C7B, 0x00004, 0x509A1FE1, 0x00004, 0x50FE632A, 0x00004, 0x511622B3, 0x88040,
|
||||
0x5297D400, 0x00001, 0x5297D766, 0x00001, 0x5297D919, 0x00001, 0x5297DACC, 0x00001,
|
||||
0x5297DC7F, 0x00001, 0x5297EBCA, 0x00001, 0x5297ED7D, 0x00001, 0x529AE870, 0x00001,
|
||||
0x529AEA23, 0x00001, 0x529AF608, 0x00001, 0x529AF7BB, 0x00001, 0x529AF96E, 0x00001,
|
||||
0x529AFB21, 0x00001, 0x529AFCD4, 0x00001, 0x529AFE87, 0x00001, 0x529B003A, 0x00001,
|
||||
0x529B01ED, 0x00001, 0x52A96788, 0x00001, 0x52A9693B, 0x00001, 0x52A96AEE, 0x00001,
|
||||
0x52A96CA1, 0x00001, 0x52A96E54, 0x00001, 0x52A97007, 0x00001, 0x52A971BA, 0x00001,
|
||||
0x52A9736D, 0x00001, 0x52AC71C6, 0x00001, 0x52AC7379, 0x00001, 0x52AC7BF8, 0x00001,
|
||||
0x52AC7DAB, 0x00001, 0x52AC7F5E, 0x00001, 0x52AC8111, 0x00001, 0x52AC82C4, 0x00001,
|
||||
0x52AC8477, 0x00001, 0x52AC862A, 0x00001, 0x52AC87DD, 0x00001, 0x52AF8D02, 0x00001,
|
||||
0x52AF8EB5, 0x00001, 0x52AF9068, 0x00001, 0x52AF921B, 0x00001, 0x52AF93CE, 0x00001,
|
||||
0x52AF9581, 0x00001, 0x52AF9734, 0x00001, 0x52AF98E7, 0x00001, 0x52AF9C4D, 0x00001,
|
||||
0x52B27C10, 0x00001, 0x52B27DC3, 0x00001, 0x52B27F76, 0x00001, 0x52B28129, 0x00001,
|
||||
0x52B282DC, 0x00001, 0x52B2848F, 0x00001, 0x52B28642, 0x00001, 0x52B287F5, 0x00001,
|
||||
0x52B289A8, 0x00001, 0x52B28B5B, 0x00001, 0x52B4B700, 0x00001, 0x52B4B8B3, 0x00001,
|
||||
0x52B4BA66, 0x00001, 0x52B4BDCC, 0x00001, 0x52B4BF7F, 0x00001, 0x52B4C132, 0x00001,
|
||||
0x52B4C2E5, 0x00001, 0x52B4CB64, 0x00001, 0x52B4CD17, 0x00001, 0x52B7CB70, 0x00001,
|
||||
0x52B7CD23, 0x00001, 0x52B7D089, 0x00001, 0x52B7D23C, 0x00001, 0x52B7D5A2, 0x00001,
|
||||
0x52B7D755, 0x00001, 0x52BAE346, 0x00001, 0x52BAE4F9, 0x00001, 0x52BAED78, 0x00001,
|
||||
0x52BAEF2B, 0x00001, 0x52BAF291, 0x00001, 0x52BAF444, 0x00001, 0x52BAF5F7, 0x00001,
|
||||
0x52BAF7AA, 0x00001, 0x52BAF95D, 0x00001, 0x52BDFB1C, 0x00001, 0x52BDFCCF, 0x00001,
|
||||
0x52BE039B, 0x00001, 0x52BE054E, 0x00001, 0x52BE0701, 0x00001, 0x52BE08B4, 0x00001,
|
||||
0x52BE0A67, 0x00001, 0x52BE0C1A, 0x00001, 0x53DB799F, 0x00004, 0x5423DAA0, 0x00004,
|
||||
0x549B6033, 0x03000, 0x54DAE9C5, 0x00004, 0x567F1330, 0x00001, 0x567F14E3, 0x00001,
|
||||
0x567F1696, 0x00001, 0x567F1849, 0x00001, 0x567F19FC, 0x00001, 0x56823385, 0x00001,
|
||||
0x56878ECA, 0x00001, 0x5687907D, 0x00001, 0x57A07D08, 0x00004, 0x58AB6233, 0x00064,
|
||||
0x590CD38E, 0x00004, 0x5979158E, 0x00004, 0x5988DF78, 0x00004, 0x59A4D0C3, 0x00190,
|
||||
0x5B1F53F3, 0x00004, 0x5C283C72, 0x00008, 0x5C283E25, 0x00008, 0x5C283FD8, 0x00008,
|
||||
0x5C28418B, 0x00008, 0x5C28433E, 0x00008, 0x5C2844F1, 0x00008, 0x5C2846A4, 0x00008,
|
||||
0x5C284857, 0x00008, 0x5C284BBD, 0x00008, 0x62E91A65, 0x00004, 0x62F05895, 0x00004,
|
||||
0x636A5ABD, 0x000C8, 0x64A1A5B0, 0x00004, 0x64A1A763, 0x00004, 0x64A1AE2F, 0x00004,
|
||||
0x64A1AFE2, 0x00004, 0x64A1B195, 0x00004, 0x6506EE96, 0x06D60, 0x651D61A2, 0x004B0,
|
||||
0x67692BB8, 0x00004, 0x67692D6B, 0x00004, 0x67692F1E, 0x00004, 0x676935EA, 0x00004,
|
||||
0x6769379D, 0x00004, 0x6960C6EF, 0x00004, 0x6AFB0A16, 0x00004, 0x6B35BADB, 0x00060,
|
||||
0x6B734EFD, 0x00004, 0x6C03D4A8, 0x00014, 0x6C99F9A0, 0x00002, 0x6EB3E8A0, 0x00004,
|
||||
0x6F36A3AC, 0x00004, 0x717DDAA3, 0x00008, 0x71825204, 0x00001, 0x72391B04, 0x00004,
|
||||
0x727AE2EE, 0x00004, 0x727AE4A1, 0x00004, 0x727AE654, 0x00004, 0x727AE807, 0x00004,
|
||||
0x727AE9BA, 0x00004, 0x74026290, 0x00004, 0x744447B4, 0x00004, 0x75931048, 0x00004,
|
||||
0x75931561, 0x00004, 0x75CE2CF6, 0x00004, 0x7659EC88, 0x00004, 0x76AB1B01, 0x00004,
|
||||
0x76ABB5CD, 0x00004, 0x77675FA0, 0x00320, 0x78848293, 0x00001, 0x78848446, 0x00001,
|
||||
0x788485F9, 0x00001, 0x78E0935E, 0x00004, 0x79448B5D, 0x00004, 0x79C56A5C, 0x00004,
|
||||
0x7B8CCB0B, 0x00180, 0x7CA9D9FA, 0x00004, 0x7D249649, 0x00004, 0x7D87DC83, 0x00004,
|
||||
0x7E82513F, 0x00004, 0x8048A7DC, 0x00004, 0x81EC3A78, 0x00004, 0x82AD5F84, 0x00004,
|
||||
0x82D57F17, 0x000C8, 0x8507839C, 0x00004, 0x85166DE2, 0x00004, 0x877CB98F, 0x009B0,
|
||||
0x885E5F53, 0x00010, 0x89C1C8DE, 0x00004, 0x8A0E9425, 0x004B0, 0x8B18A566, 0x00004,
|
||||
0x8B18A719, 0x00004, 0x8B18A8CC, 0x00004, 0x8B18AA7F, 0x00004, 0x8B18AC32, 0x00004,
|
||||
0x8B18ADE5, 0x00004, 0x8B8FB439, 0x00004, 0x8BDFF0F3, 0x00040, 0x8BEEF106, 0x00004,
|
||||
0x8C46768E, 0x00004, 0x8C5F59E8, 0x00004, 0x8D781241, 0x00008, 0x8E434F0D, 0x002D0,
|
||||
0x8F0D8720, 0x00004, 0x8FC0A045, 0x00004, 0x92EB0306, 0x00004, 0x92F697ED, 0x00004,
|
||||
0x95013114, 0x00008, 0x96993D83, 0x00008, 0x96F6F453, 0x00004, 0x9751BABE, 0x00400,
|
||||
0x98785EE4, 0x00008, 0x98786097, 0x00008, 0x987863FD, 0x00008, 0x99E1625E, 0x07EB0,
|
||||
0x9AB5F3D9, 0x00001, 0x9B986D2E, 0x00004, 0x9C41123A, 0x00004, 0x9C808BD3, 0x00004,
|
||||
0x9D5D1CA5, 0x00004, 0x9E45BE99, 0x00004, 0x9E4635BB, 0x00004, 0x9EC079DA, 0x00002,
|
||||
0x9FE2790A, 0x00A8C, 0xA00A8ABB, 0x00008, 0xA373DA53, 0x01900, 0xA4317061, 0x00004,
|
||||
0xA69E079B, 0x00004, 0xA94E1F5F, 0x00004, 0xA9F1368B, 0x00004, 0xAB48C136, 0x00004,
|
||||
0xAD319811, 0x00004, 0xAE7B3CA1, 0x00004, 0xAE89206E, 0x00001, 0xAEE903A2, 0x00008,
|
||||
0xB027F396, 0x00004, 0xB0B2A5AA, 0x00004, 0xB1EE7CF5, 0x00004, 0xB568265C, 0x00004,
|
||||
0xB79EF1FE, 0x00004, 0xB7AAB47E, 0x00004, 0xB8E961AD, 0x000FB, 0xB9075BC9, 0x00008,
|
||||
0xB9252862, 0x00110, 0xBA230941, 0x00004, 0xBB0B39CF, 0x00004, 0xBCC66014, 0x00004,
|
||||
0xBCC72306, 0x00004, 0xBCE74BFD, 0x00004, 0xC25B0D5A, 0x00004, 0xC2F1C1B9, 0x02580,
|
||||
0xC4C6417F, 0x000C0, 0xC4FA7C8C, 0x00004, 0xC5919BF6, 0x00004, 0xC5D7112B, 0x0DCA8,
|
||||
0xC69F66B6, 0x00FA0, 0xC74A3FE7, 0x00010, 0xC7652F1C, 0x00004, 0xC7F8E3BC, 0x00008,
|
||||
0xC7F8E56F, 0x00008, 0xC7F8EA88, 0x00008, 0xC7F8EC3B, 0x00008, 0xC7F8EDEE, 0x00008,
|
||||
0xC7F8EFA1, 0x00008, 0xC7F8F154, 0x00008, 0xC7F8F4BA, 0x00008, 0xC7F8F66D, 0x00008,
|
||||
0xC9541EB3, 0x00002, 0xC9A81578, 0x00010, 0xCC022CEC, 0x00008, 0xCC022E9F, 0x00008,
|
||||
0xCC023052, 0x00008, 0xCD8ADF1D, 0x00004, 0xCFC9AA03, 0x00004, 0xCFEB27B3, 0x009C4,
|
||||
0xCFED4C69, 0x00004, 0xD0068A74, 0x00004, 0xD03A595A, 0x009B0, 0xD06FD1EA, 0x00004,
|
||||
0xD11C1F59, 0x00004, 0xD3C06782, 0x00008, 0xD48FDC48, 0x00004, 0xD6546A02, 0x00038,
|
||||
0xD6C95683, 0x00960, 0xD6F1B724, 0x00004, 0xD8048E00, 0x00004, 0xD94D71D7, 0x00004,
|
||||
0xDD548BB1, 0x00B58, 0xDED70F11, 0x00004, 0xDFA7E2D8, 0x00004, 0xE0FB1EE7, 0x00008,
|
||||
0xE1DF0672, 0x005B8, 0xE2798DDE, 0x00001, 0xE36D5700, 0x00064, 0xE450FEF7, 0x0001A,
|
||||
0xE4E75089, 0x00040, 0xE5169DA1, 0x00004, 0xE668F1A6, 0x00001, 0xE668F359, 0x00001,
|
||||
0xE668F50C, 0x00001, 0xE668F6BF, 0x00001, 0xE668FA25, 0x00001, 0xE72BDCEC, 0x00004,
|
||||
0xE733FE4D, 0x00004, 0xE7425CFF, 0x00004, 0xE793EEAE, 0x00004, 0xEA7C87F4, 0x00020,
|
||||
0xEC13DFD7, 0x00004, 0xEE10F128, 0x00004, 0xEFB533F7, 0x00001, 0xF25C070E, 0x00080,
|
||||
0xF3CF94FF, 0x00004, 0xF41CFF61, 0x00200, 0xF45C3F59, 0x00004, 0xF4954B60, 0x00004,
|
||||
0xF5D9F4A5, 0x00118, 0xF626721E, 0x00004, 0xF62D79D3, 0x00004, 0xF8154AC9, 0x00004,
|
||||
0xFB58B1A7, 0x00064, 0xFB5AE87D, 0x00004, 0xFC374750, 0x00004, 0xFF400328, 0x00004,
|
||||
0xFFCC05C2, 0x00001,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace PKHeX.Core
|
|||
{
|
||||
public const int BEEF = 0x42454546;
|
||||
|
||||
public const int SIZE_G8LA = 0x136DDE;
|
||||
|
||||
public const int SIZE_G8BDSP = 0xE9828;
|
||||
public const int SIZE_G8BDSP_1 = 0xEDC20;
|
||||
|
||||
|
@ -97,7 +99,7 @@ namespace PKHeX.Core
|
|||
|
||||
private static readonly HashSet<int> Sizes = new(SizesGen2.Concat(SizesSWSH))
|
||||
{
|
||||
SIZE_G8BDSP, SIZE_G8BDSP_1,
|
||||
SIZE_G8LA, SIZE_G8BDSP, SIZE_G8BDSP_1,
|
||||
// SizesSWSH covers gen8 sizes since there's so many
|
||||
SIZE_G7SM, SIZE_G7USUM, SIZE_G7GG,
|
||||
SIZE_G6XY, SIZE_G6ORAS, SIZE_G6ORASDEMO,
|
||||
|
@ -161,6 +163,8 @@ namespace PKHeX.Core
|
|||
return ver;
|
||||
if ((ver = GetIsG8SAV_BDSP(data)) != Invalid)
|
||||
return ver;
|
||||
if ((ver = GetIsG8SAV_LA(data)) != Invalid)
|
||||
return ver;
|
||||
|
||||
return Invalid;
|
||||
}
|
||||
|
@ -503,6 +507,14 @@ namespace PKHeX.Core
|
|||
return BDSP;
|
||||
}
|
||||
|
||||
private static GameVersion GetIsG8SAV_LA(byte[] data)
|
||||
{
|
||||
if (data.Length is not SIZE_G8LA)
|
||||
return Invalid;
|
||||
|
||||
return SwishCrypto.GetIsHashValidLA(data) ? PLA : Invalid;
|
||||
}
|
||||
|
||||
private static bool GetIsBank7(ReadOnlySpan<byte> data) => data.Length == SIZE_G7BANK && data[0] != 0;
|
||||
private static bool GetIsBank4(ReadOnlySpan<byte> data) => data.Length == SIZE_G4BANK && ReadUInt32LittleEndian(data[0x3FC00..]) != 0; // box name present
|
||||
private static bool GetIsBank3(ReadOnlySpan<byte> data) => data.Length == SIZE_G4BANK && ReadUInt32LittleEndian(data[0x3FC00..]) == 0; // size collision with ^
|
||||
|
@ -601,6 +613,7 @@ namespace PKHeX.Core
|
|||
|
||||
SWSH => new SAV8SWSH(data),
|
||||
BDSP => new SAV8BS(data),
|
||||
PLA => new SAV8LA(data),
|
||||
|
||||
// Side Games
|
||||
COLO => new SAV3Colosseum(data),
|
||||
|
@ -746,6 +759,7 @@ namespace PKHeX.Core
|
|||
|
||||
SW or SH or SWSH => new SAV8SWSH(),
|
||||
BD or SP or BDSP => new SAV8BS(),
|
||||
PLA => new SAV8LA(),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(game)),
|
||||
};
|
||||
|
@ -783,7 +797,7 @@ namespace PKHeX.Core
|
|||
// force evaluation so that an invalid path will throw before we return true/false.
|
||||
// EnumerateFiles throws an exception while iterating, which won't be caught by the try-catch here.
|
||||
var files = Directory.GetFiles(folderPath, "*", searchOption);
|
||||
result = files.Where(f => IsSizeValid(FileUtil.GetFileSize(f)));
|
||||
result = files.Where(f => !IsBackup(f) && IsSizeValid(FileUtil.GetFileSize(f)));
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -798,6 +812,8 @@ namespace PKHeX.Core
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsBackup(string path) => Path.GetFileName(path) is "backup" || Path.GetExtension(path) is ".bak";
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the save data size is valid for automatically detecting saves.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue