mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Expand My Pokémon Ranch Support (#3595)
* Add RanchMii for SAV4Ranch * Add RanchToy * Add RanchTrainerMii * Add RanchLevel * Add RanchToy class; make existing RanchToy RanchToyType * Add RanchToy and RanchLevel to SAV4Ranch * Remove incorrect MaxPkmCount entry from RanchLevel * Move code to remove PtHGSS data to a function in G$PKM * Add RK4 for My Pokemon Ranch Pokemon * Add RanchPkOwnershipType * SAV4Ranch updates * Fix PK4/RK4 conversion logic to stop breaking nicknames/OTs * Fix EntityDetection.IsPresent() check tripping on the data end marker for SAV4Ranch * Add .rk4 to README translations * Minor tweaks Fix RK4 TID/SID endianness/order, split Ownership enum into two enums Condense mii classes to get/set properties Make RanchLevel a static class for logic Remove ClearFF for TrainerMii -- the FFFF is the string terminator char for gen4 Make Toy byte enum, with unused alignment bytes Co-authored-by: Kurt <kaphotics@gmail.com>
This commit is contained in:
parent
72410e1619
commit
b804557627
22 changed files with 755 additions and 55 deletions
2
.github/README-de.md
vendored
2
.github/README-de.md
vendored
|
@ -7,7 +7,7 @@ Save Editor für die Pokémon Hauptreihe, geschrieben in [C#](https://de.wikiped
|
||||||
Die folgenden Dateien werden unterstützt:
|
Die folgenden Dateien werden unterstützt:
|
||||||
* Spielstände ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
* Spielstände ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
||||||
* GameCube Memory Card Daten (\*.raw, \*.bin), die GC Pokémon Spielstände enthalten.
|
* GameCube Memory Card Daten (\*.raw, \*.bin), die GC Pokémon Spielstände enthalten.
|
||||||
* Einzelne Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4)
|
* Einzelne Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||||
* Wunderkarten (\*.pgt, \*.pcd, \*.pgf, .wc\*), inklusive Konvertierung zu .pk\*
|
* Wunderkarten (\*.pgt, \*.pcd, \*.pgf, .wc\*), inklusive Konvertierung zu .pk\*
|
||||||
* Import von GO Park Pokémon (\*.gp1) inklusive Konvertierung zu .pb7
|
* Import von GO Park Pokémon (\*.gp1) inklusive Konvertierung zu .pb7
|
||||||
* Import von Teams aus entschlüsselten 3DS Battle Videos.
|
* Import von Teams aus entschlüsselten 3DS Battle Videos.
|
||||||
|
|
2
.github/README-es.md
vendored
2
.github/README-es.md
vendored
|
@ -7,7 +7,7 @@ Editor de guardado de las series principales de Pokémon, programado en [C#](htt
|
||||||
Soporta los siguientes archivos:
|
Soporta los siguientes archivos:
|
||||||
* Archivos de guardado ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
* Archivos de guardado ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
||||||
* Archivos de Memory Card de GameCube (\*.raw, \*.bin) que contienen archivos de GC Pokémon.
|
* Archivos de Memory Card de GameCube (\*.raw, \*.bin) que contienen archivos de GC Pokémon.
|
||||||
* Archivos de entidades individuales de Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4)
|
* Archivos de entidades individuales de Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||||
* Archivos de Regalos Misteriosos (\*.pgt, \*.pcd, \*.pgf, .wc\*) incluyendo conversión a .pk\*
|
* Archivos de Regalos Misteriosos (\*.pgt, \*.pcd, \*.pgf, .wc\*) incluyendo conversión a .pk\*
|
||||||
* Importar archivos de entidades de GO Park (\*.gp1) incluyendo conversión a .pb7
|
* Importar archivos de entidades de GO Park (\*.gp1) incluyendo conversión a .pb7
|
||||||
* Importar equipos desde archivos Decrypted 3DS Battle Videos
|
* Importar equipos desde archivos Decrypted 3DS Battle Videos
|
||||||
|
|
2
.github/README-fr.md
vendored
2
.github/README-fr.md
vendored
|
@ -7,7 +7,7 @@ PKHeX
|
||||||
Prend en charge les fichiers suivants :
|
Prend en charge les fichiers suivants :
|
||||||
* Enregistrer les fichiers ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
* Enregistrer les fichiers ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
||||||
* Fichiers de carte mémoire GameCube (\*.raw, \*.bin) contenant des sauvegardes de Pokémon GC.
|
* Fichiers de carte mémoire GameCube (\*.raw, \*.bin) contenant des sauvegardes de Pokémon GC.
|
||||||
* Fichiers d'entités Pokémon individuels (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4)
|
* Fichiers d'entités Pokémon individuels (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||||
* Fichiers de cadeau mystère (\*.pgt, \*.pcd, \*.pgf, .wc\*) y compris la conversion en .pk\*
|
* Fichiers de cadeau mystère (\*.pgt, \*.pcd, \*.pgf, .wc\*) y compris la conversion en .pk\*
|
||||||
* Importation d'entités GO Park (\*.gp1) incluant la conversion en .pb7
|
* Importation d'entités GO Park (\*.gp1) incluant la conversion en .pb7
|
||||||
* Importation d'équipes à partir de 3DS Battle Videos
|
* Importation d'équipes à partir de 3DS Battle Videos
|
||||||
|
|
2
.github/README-it.md
vendored
2
.github/README-it.md
vendored
|
@ -7,7 +7,7 @@ Editor di Salvataggi Pokémon per la serie principale, programmato in [C#](https
|
||||||
Supporta i seguenti file:
|
Supporta i seguenti file:
|
||||||
* File di salvataggio ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
* File di salvataggio ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
||||||
* File di Memory Card GameCube (\*.raw, \*.bin) contenenti File di Salvataggio Pokémon.
|
* File di Memory Card GameCube (\*.raw, \*.bin) contenenti File di Salvataggio Pokémon.
|
||||||
* File di Entità Pokémon individuali (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4)
|
* File di Entità Pokémon individuali (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||||
* File di Dono Segreto (\*.pgt, \*.pcd, \*.pgf, .wc\*) inclusa conversione in .pk\*
|
* File di Dono Segreto (\*.pgt, \*.pcd, \*.pgf, .wc\*) inclusa conversione in .pk\*
|
||||||
* Importazione di Entità del Go Park (\*.gp1) inclusa conversione in .pb7
|
* Importazione di Entità del Go Park (\*.gp1) inclusa conversione in .pb7
|
||||||
* Importazione di squadre da Video Lotta del 3DS decriptati
|
* Importazione di squadre da Video Lotta del 3DS decriptati
|
||||||
|
|
|
@ -19,7 +19,7 @@ public static class BatchEditing
|
||||||
{
|
{
|
||||||
typeof (PK8), typeof (PA8), typeof (PB8),
|
typeof (PK8), typeof (PA8), typeof (PB8),
|
||||||
typeof (PB7),
|
typeof (PB7),
|
||||||
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4),
|
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4), typeof(RK4),
|
||||||
typeof (PK3), typeof (XK3), typeof (CK3),
|
typeof (PK3), typeof (XK3), typeof (CK3),
|
||||||
typeof (PK2), typeof (SK2), typeof (PK1),
|
typeof (PK2), typeof (SK2), typeof (PK1),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
@ -288,15 +289,24 @@ public sealed class PK4 : G4PKM
|
||||||
{
|
{
|
||||||
BK4 bk4 = ConvertTo<BK4>();
|
BK4 bk4 = ConvertTo<BK4>();
|
||||||
|
|
||||||
// Enforce DP content only (no PtHGSS)
|
StripPtHGSSContent(bk4);
|
||||||
if (Form != 0 && !PersonalTable.DP[Species].HasForms && Species != 201)
|
|
||||||
bk4.Form = 0;
|
|
||||||
if (HeldItem > Legal.MaxItemID_4_DP)
|
|
||||||
bk4.HeldItem = 0;
|
|
||||||
bk4.RefreshChecksum();
|
bk4.RefreshChecksum();
|
||||||
return bk4;
|
return bk4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RK4 ConvertToRK4()
|
||||||
|
{
|
||||||
|
byte[] data = Data.AsSpan(0, PokeCrypto.SIZE_4RSTORED).ToArray();
|
||||||
|
for (int i = PokeCrypto.SIZE_4STORED; i < PokeCrypto.SIZE_4RSTORED; i++)
|
||||||
|
data[i] = 0;
|
||||||
|
|
||||||
|
RK4 rk4 = new RK4(data);
|
||||||
|
rk4.OwnershipType = RanchOwnershipType.Hayley;
|
||||||
|
|
||||||
|
rk4.RefreshChecksum();
|
||||||
|
return rk4;
|
||||||
|
}
|
||||||
|
|
||||||
public PK5 ConvertToPK5()
|
public PK5 ConvertToPK5()
|
||||||
{
|
{
|
||||||
// Double Check Location Data to see if we're already a PK5
|
// Double Check Location Data to see if we're already a PK5
|
||||||
|
|
345
PKHeX.Core/PKM/RK4.cs
Normal file
345
PKHeX.Core/PKM/RK4.cs
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary> Generation 4 <see cref="PKM"/> format for My Pokémon Ranch. </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// MPR-specific values are stored in Big Endian format rather than Little Endian. Beware.
|
||||||
|
/// </remarks>
|
||||||
|
public sealed class RK4 : G4PKM
|
||||||
|
{
|
||||||
|
private static readonly ushort[] Unused =
|
||||||
|
{
|
||||||
|
0x42, 0x43, 0x5E, 0x63, 0x64, 0x65, 0x66, 0x67, 0x87,
|
||||||
|
};
|
||||||
|
|
||||||
|
public override IReadOnlyList<ushort> ExtraBytes => Unused;
|
||||||
|
|
||||||
|
public override int SIZE_PARTY => PokeCrypto.SIZE_4RSTORED;
|
||||||
|
public override int SIZE_STORED => PokeCrypto.SIZE_4RSTORED;
|
||||||
|
public override EntityContext Context => EntityContext.Gen4;
|
||||||
|
public override PersonalInfo PersonalInfo => PersonalTable.Pt.GetFormEntry(Species, Form);
|
||||||
|
|
||||||
|
public RK4() : base(PokeCrypto.SIZE_4RSTORED) { }
|
||||||
|
public RK4(byte[] data) : base(Decrypt(data)) { }
|
||||||
|
|
||||||
|
private static byte[] Decrypt(byte[] data)
|
||||||
|
{
|
||||||
|
byte[] pkData = data.Slice(0, PokeCrypto.SIZE_4STORED);
|
||||||
|
PokeCrypto.DecryptIfEncrypted45(ref pkData);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PKM Clone() => new RK4((byte[])Data.Clone());
|
||||||
|
|
||||||
|
// Structure
|
||||||
|
public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); }
|
||||||
|
public override ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); }
|
||||||
|
public override ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); }
|
||||||
|
|
||||||
|
#region Block A
|
||||||
|
public override ushort Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), value); }
|
||||||
|
public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
|
||||||
|
public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); }
|
||||||
|
public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); }
|
||||||
|
public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); }
|
||||||
|
public override int OT_Friendship { get => Data[0x14]; set => Data[0x14] = (byte)value; }
|
||||||
|
public override int Ability { get => Data[0x15]; set => Data[0x15] = (byte)value; }
|
||||||
|
public override int MarkValue { get => Data[0x16]; set => Data[0x16] = (byte)value; }
|
||||||
|
public override int Language { get => Data[0x17]; set => Data[0x17] = (byte)value; }
|
||||||
|
public override int EV_HP { get => Data[0x18]; set => Data[0x18] = (byte)value; }
|
||||||
|
public override int EV_ATK { get => Data[0x19]; set => Data[0x19] = (byte)value; }
|
||||||
|
public override int EV_DEF { get => Data[0x1A]; set => Data[0x1A] = (byte)value; }
|
||||||
|
public override int EV_SPE { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
|
||||||
|
public override int EV_SPA { get => Data[0x1C]; set => Data[0x1C] = (byte)value; }
|
||||||
|
public override int EV_SPD { get => Data[0x1D]; set => Data[0x1D] = (byte)value; }
|
||||||
|
public override byte CNT_Cool { get => Data[0x1E]; set => Data[0x1E] = value; }
|
||||||
|
public override byte CNT_Beauty { get => Data[0x1F]; set => Data[0x1F] = value; }
|
||||||
|
public override byte CNT_Cute { get => Data[0x20]; set => Data[0x20] = value; }
|
||||||
|
public override byte CNT_Smart { get => Data[0x21]; set => Data[0x21] = value; }
|
||||||
|
public override byte CNT_Tough { get => Data[0x22]; set => Data[0x22] = value; }
|
||||||
|
public override byte CNT_Sheen { get => Data[0x23]; set => Data[0x23] = value; }
|
||||||
|
|
||||||
|
private byte RIB0 { get => Data[0x24]; set => Data[0x24] = value; } // Sinnoh 1
|
||||||
|
private byte RIB1 { get => Data[0x25]; set => Data[0x25] = value; } // Sinnoh 2
|
||||||
|
private byte RIB2 { get => Data[0x26]; set => Data[0x26] = value; } // Unova 1
|
||||||
|
private byte RIB3 { get => Data[0x27]; set => Data[0x27] = value; } // Unova 2
|
||||||
|
public override bool RibbonChampionSinnoh { get => (RIB0 & (1 << 0)) == 1 << 0; set => RIB0 = (byte)((RIB0 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonAbility { get => (RIB0 & (1 << 1)) == 1 << 1; set => RIB0 = (byte)((RIB0 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonAbilityGreat { get => (RIB0 & (1 << 2)) == 1 << 2; set => RIB0 = (byte)((RIB0 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonAbilityDouble { get => (RIB0 & (1 << 3)) == 1 << 3; set => RIB0 = (byte)((RIB0 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonAbilityMulti { get => (RIB0 & (1 << 4)) == 1 << 4; set => RIB0 = (byte)((RIB0 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonAbilityPair { get => (RIB0 & (1 << 5)) == 1 << 5; set => RIB0 = (byte)((RIB0 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonAbilityWorld { get => (RIB0 & (1 << 6)) == 1 << 6; set => RIB0 = (byte)((RIB0 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonAlert { get => (RIB0 & (1 << 7)) == 1 << 7; set => RIB0 = (byte)((RIB0 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonShock { get => (RIB1 & (1 << 0)) == 1 << 0; set => RIB1 = (byte)((RIB1 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonDowncast { get => (RIB1 & (1 << 1)) == 1 << 1; set => RIB1 = (byte)((RIB1 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonCareless { get => (RIB1 & (1 << 2)) == 1 << 2; set => RIB1 = (byte)((RIB1 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonRelax { get => (RIB1 & (1 << 3)) == 1 << 3; set => RIB1 = (byte)((RIB1 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonSnooze { get => (RIB1 & (1 << 4)) == 1 << 4; set => RIB1 = (byte)((RIB1 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonSmile { get => (RIB1 & (1 << 5)) == 1 << 5; set => RIB1 = (byte)((RIB1 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonGorgeous { get => (RIB1 & (1 << 6)) == 1 << 6; set => RIB1 = (byte)((RIB1 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonRoyal { get => (RIB1 & (1 << 7)) == 1 << 7; set => RIB1 = (byte)((RIB1 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonGorgeousRoyal { get => (RIB2 & (1 << 0)) == 1 << 0; set => RIB2 = (byte)((RIB2 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonFootprint { get => (RIB2 & (1 << 1)) == 1 << 1; set => RIB2 = (byte)((RIB2 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonRecord { get => (RIB2 & (1 << 2)) == 1 << 2; set => RIB2 = (byte)((RIB2 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonEvent { get => (RIB2 & (1 << 3)) == 1 << 3; set => RIB2 = (byte)((RIB2 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonLegend { get => (RIB2 & (1 << 4)) == 1 << 4; set => RIB2 = (byte)((RIB2 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonChampionWorld { get => (RIB2 & (1 << 5)) == 1 << 5; set => RIB2 = (byte)((RIB2 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonBirthday { get => (RIB2 & (1 << 6)) == 1 << 6; set => RIB2 = (byte)((RIB2 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonSpecial { get => (RIB2 & (1 << 7)) == 1 << 7; set => RIB2 = (byte)((RIB2 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonSouvenir { get => (RIB3 & (1 << 0)) == 1 << 0; set => RIB3 = (byte)((RIB3 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonWishing { get => (RIB3 & (1 << 1)) == 1 << 1; set => RIB3 = (byte)((RIB3 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonClassic { get => (RIB3 & (1 << 2)) == 1 << 2; set => RIB3 = (byte)((RIB3 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonPremier { get => (RIB3 & (1 << 3)) == 1 << 3; set => RIB3 = (byte)((RIB3 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RIB3_4 { get => (RIB3 & (1 << 4)) == 1 << 4; set => RIB3 = (byte)((RIB3 & ~(1 << 4)) | (value ? 1 << 4 : 0)); } // Unused
|
||||||
|
public override bool RIB3_5 { get => (RIB3 & (1 << 5)) == 1 << 5; set => RIB3 = (byte)((RIB3 & ~(1 << 5)) | (value ? 1 << 5 : 0)); } // Unused
|
||||||
|
public override bool RIB3_6 { get => (RIB3 & (1 << 6)) == 1 << 6; set => RIB3 = (byte)((RIB3 & ~(1 << 6)) | (value ? 1 << 6 : 0)); } // Unused
|
||||||
|
public override bool RIB3_7 { get => (RIB3 & (1 << 7)) == 1 << 7; set => RIB3 = (byte)((RIB3 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Block B
|
||||||
|
public override ushort Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x28)); set => WriteUInt16LittleEndian(Data.AsSpan(0x28), value); }
|
||||||
|
public override ushort Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2A), value); }
|
||||||
|
public override ushort Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C), value); }
|
||||||
|
public override ushort Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E), value); }
|
||||||
|
public override int Move1_PP { get => Data[0x30]; set => Data[0x30] = (byte)value; }
|
||||||
|
public override int Move2_PP { get => Data[0x31]; set => Data[0x31] = (byte)value; }
|
||||||
|
public override int Move3_PP { get => Data[0x32]; set => Data[0x32] = (byte)value; }
|
||||||
|
public override int Move4_PP { get => Data[0x33]; set => Data[0x33] = (byte)value; }
|
||||||
|
public override int Move1_PPUps { get => Data[0x34]; set => Data[0x34] = (byte)value; }
|
||||||
|
public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; }
|
||||||
|
public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; }
|
||||||
|
public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; }
|
||||||
|
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x38)); set => WriteUInt32LittleEndian(Data.AsSpan(0x38), value); }
|
||||||
|
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
|
||||||
|
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
|
||||||
|
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
|
||||||
|
public override int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | ((value > 31 ? 31u : (uint)value) << 15); }
|
||||||
|
public override int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | ((value > 31 ? 31u : (uint)value) << 20); }
|
||||||
|
public override int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | ((value > 31 ? 31u : (uint)value) << 25); }
|
||||||
|
public override bool IsEgg { get => ((IV32 >> 30) & 1) == 1; set => IV32 = (IV32 & ~0x40000000u) | (value ? 0x40000000u : 0u); }
|
||||||
|
public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); }
|
||||||
|
|
||||||
|
private byte RIB4 { get => Data[0x3C]; set => Data[0x3C] = value; } // Hoenn 1a
|
||||||
|
private byte RIB5 { get => Data[0x3D]; set => Data[0x3D] = value; } // Hoenn 1b
|
||||||
|
private byte RIB6 { get => Data[0x3E]; set => Data[0x3E] = value; } // Hoenn 2a
|
||||||
|
private byte RIB7 { get => Data[0x3F]; set => Data[0x3F] = value; } // Hoenn 2b
|
||||||
|
public override bool RibbonG3Cool { get => (RIB4 & (1 << 0)) == 1 << 0; set => RIB4 = (byte)((RIB4 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG3CoolSuper { get => (RIB4 & (1 << 1)) == 1 << 1; set => RIB4 = (byte)((RIB4 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG3CoolHyper { get => (RIB4 & (1 << 2)) == 1 << 2; set => RIB4 = (byte)((RIB4 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG3CoolMaster { get => (RIB4 & (1 << 3)) == 1 << 3; set => RIB4 = (byte)((RIB4 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonG3Beauty { get => (RIB4 & (1 << 4)) == 1 << 4; set => RIB4 = (byte)((RIB4 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonG3BeautySuper { get => (RIB4 & (1 << 5)) == 1 << 5; set => RIB4 = (byte)((RIB4 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonG3BeautyHyper { get => (RIB4 & (1 << 6)) == 1 << 6; set => RIB4 = (byte)((RIB4 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonG3BeautyMaster { get => (RIB4 & (1 << 7)) == 1 << 7; set => RIB4 = (byte)((RIB4 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonG3Cute { get => (RIB5 & (1 << 0)) == 1 << 0; set => RIB5 = (byte)((RIB5 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG3CuteSuper { get => (RIB5 & (1 << 1)) == 1 << 1; set => RIB5 = (byte)((RIB5 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG3CuteHyper { get => (RIB5 & (1 << 2)) == 1 << 2; set => RIB5 = (byte)((RIB5 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG3CuteMaster { get => (RIB5 & (1 << 3)) == 1 << 3; set => RIB5 = (byte)((RIB5 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonG3Smart { get => (RIB5 & (1 << 4)) == 1 << 4; set => RIB5 = (byte)((RIB5 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonG3SmartSuper { get => (RIB5 & (1 << 5)) == 1 << 5; set => RIB5 = (byte)((RIB5 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonG3SmartHyper { get => (RIB5 & (1 << 6)) == 1 << 6; set => RIB5 = (byte)((RIB5 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonG3SmartMaster { get => (RIB5 & (1 << 7)) == 1 << 7; set => RIB5 = (byte)((RIB5 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonG3Tough { get => (RIB6 & (1 << 0)) == 1 << 0; set => RIB6 = (byte)((RIB6 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG3ToughSuper { get => (RIB6 & (1 << 1)) == 1 << 1; set => RIB6 = (byte)((RIB6 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG3ToughHyper { get => (RIB6 & (1 << 2)) == 1 << 2; set => RIB6 = (byte)((RIB6 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG3ToughMaster { get => (RIB6 & (1 << 3)) == 1 << 3; set => RIB6 = (byte)((RIB6 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonChampionG3 { get => (RIB6 & (1 << 4)) == 1 << 4; set => RIB6 = (byte)((RIB6 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonWinning { get => (RIB6 & (1 << 5)) == 1 << 5; set => RIB6 = (byte)((RIB6 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonVictory { get => (RIB6 & (1 << 6)) == 1 << 6; set => RIB6 = (byte)((RIB6 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonArtist { get => (RIB6 & (1 << 7)) == 1 << 7; set => RIB6 = (byte)((RIB6 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonEffort { get => (RIB7 & (1 << 0)) == 1 << 0; set => RIB7 = (byte)((RIB7 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonChampionBattle { get => (RIB7 & (1 << 1)) == 1 << 1; set => RIB7 = (byte)((RIB7 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonChampionRegional { get => (RIB7 & (1 << 2)) == 1 << 2; set => RIB7 = (byte)((RIB7 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonChampionNational { get => (RIB7 & (1 << 3)) == 1 << 3; set => RIB7 = (byte)((RIB7 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonCountry { get => (RIB7 & (1 << 4)) == 1 << 4; set => RIB7 = (byte)((RIB7 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonNational { get => (RIB7 & (1 << 5)) == 1 << 5; set => RIB7 = (byte)((RIB7 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonEarth { get => (RIB7 & (1 << 6)) == 1 << 6; set => RIB7 = (byte)((RIB7 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonWorld { get => (RIB7 & (1 << 7)) == 1 << 7; set => RIB7 = (byte)((RIB7 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
|
||||||
|
public override bool FatefulEncounter { get => (Data[0x40] & 1) == 1; set => Data[0x40] = (byte)((Data[0x40] & ~0x01) | (value ? 1 : 0)); }
|
||||||
|
public override int Gender { get => (Data[0x40] >> 1) & 0x3; set => Data[0x40] = (byte)((Data[0x40] & ~0x06) | (value << 1)); }
|
||||||
|
public override byte Form { get => (byte)(Data[0x40] >> 3); set => Data[0x40] = (byte)((Data[0x40] & 0x07) | (value << 3)); }
|
||||||
|
public override int ShinyLeaf { get => Data[0x41]; set => Data[0x41] = (byte)value; }
|
||||||
|
// 0x42-0x43 Unused
|
||||||
|
public override ushort Egg_LocationExtended
|
||||||
|
{
|
||||||
|
get => ReadUInt16LittleEndian(Data.AsSpan(0x44));
|
||||||
|
set => WriteUInt16LittleEndian(Data.AsSpan(0x44), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ushort Met_LocationExtended
|
||||||
|
{
|
||||||
|
get => ReadUInt16LittleEndian(Data.AsSpan(0x46));
|
||||||
|
set => WriteUInt16LittleEndian(Data.AsSpan(0x46), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Block C
|
||||||
|
public override string Nickname
|
||||||
|
{
|
||||||
|
get => StringConverter4.GetString(Nickname_Trash);
|
||||||
|
set => StringConverter4.SetString(Nickname_Trash, value.AsSpan(), 10, StringConverterOption.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0x5E unused
|
||||||
|
public override int Version { get => Data[0x5F]; set => Data[0x5F] = (byte)value; }
|
||||||
|
private byte RIB8 { get => Data[0x60]; set => Data[0x60] = value; } // Sinnoh 3
|
||||||
|
private byte RIB9 { get => Data[0x61]; set => Data[0x61] = value; } // Sinnoh 4
|
||||||
|
private byte RIBA { get => Data[0x62]; set => Data[0x62] = value; } // Sinnoh 5
|
||||||
|
private byte RIBB { get => Data[0x63]; set => Data[0x63] = value; } // Sinnoh 6
|
||||||
|
public override bool RibbonG4Cool { get => (RIB8 & (1 << 0)) == 1 << 0; set => RIB8 = (byte)((RIB8 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG4CoolGreat { get => (RIB8 & (1 << 1)) == 1 << 1; set => RIB8 = (byte)((RIB8 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG4CoolUltra { get => (RIB8 & (1 << 2)) == 1 << 2; set => RIB8 = (byte)((RIB8 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG4CoolMaster { get => (RIB8 & (1 << 3)) == 1 << 3; set => RIB8 = (byte)((RIB8 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonG4Beauty { get => (RIB8 & (1 << 4)) == 1 << 4; set => RIB8 = (byte)((RIB8 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonG4BeautyGreat { get => (RIB8 & (1 << 5)) == 1 << 5; set => RIB8 = (byte)((RIB8 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonG4BeautyUltra { get => (RIB8 & (1 << 6)) == 1 << 6; set => RIB8 = (byte)((RIB8 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonG4BeautyMaster { get => (RIB8 & (1 << 7)) == 1 << 7; set => RIB8 = (byte)((RIB8 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonG4Cute { get => (RIB9 & (1 << 0)) == 1 << 0; set => RIB9 = (byte)((RIB9 & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG4CuteGreat { get => (RIB9 & (1 << 1)) == 1 << 1; set => RIB9 = (byte)((RIB9 & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG4CuteUltra { get => (RIB9 & (1 << 2)) == 1 << 2; set => RIB9 = (byte)((RIB9 & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG4CuteMaster { get => (RIB9 & (1 << 3)) == 1 << 3; set => RIB9 = (byte)((RIB9 & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RibbonG4Smart { get => (RIB9 & (1 << 4)) == 1 << 4; set => RIB9 = (byte)((RIB9 & ~(1 << 4)) | (value ? 1 << 4 : 0)); }
|
||||||
|
public override bool RibbonG4SmartGreat { get => (RIB9 & (1 << 5)) == 1 << 5; set => RIB9 = (byte)((RIB9 & ~(1 << 5)) | (value ? 1 << 5 : 0)); }
|
||||||
|
public override bool RibbonG4SmartUltra { get => (RIB9 & (1 << 6)) == 1 << 6; set => RIB9 = (byte)((RIB9 & ~(1 << 6)) | (value ? 1 << 6 : 0)); }
|
||||||
|
public override bool RibbonG4SmartMaster { get => (RIB9 & (1 << 7)) == 1 << 7; set => RIB9 = (byte)((RIB9 & ~(1 << 7)) | (value ? 1 << 7 : 0)); }
|
||||||
|
public override bool RibbonG4Tough { get => (RIBA & (1 << 0)) == 1 << 0; set => RIBA = (byte)((RIBA & ~(1 << 0)) | (value ? 1 << 0 : 0)); }
|
||||||
|
public override bool RibbonG4ToughGreat { get => (RIBA & (1 << 1)) == 1 << 1; set => RIBA = (byte)((RIBA & ~(1 << 1)) | (value ? 1 << 1 : 0)); }
|
||||||
|
public override bool RibbonG4ToughUltra { get => (RIBA & (1 << 2)) == 1 << 2; set => RIBA = (byte)((RIBA & ~(1 << 2)) | (value ? 1 << 2 : 0)); }
|
||||||
|
public override bool RibbonG4ToughMaster { get => (RIBA & (1 << 3)) == 1 << 3; set => RIBA = (byte)((RIBA & ~(1 << 3)) | (value ? 1 << 3 : 0)); }
|
||||||
|
public override bool RIBA_4 { get => (RIBA & (1 << 4)) == 1 << 4; set => RIBA = (byte)((RIBA & ~(1 << 4)) | (value ? 1 << 4 : 0)); } // Unused
|
||||||
|
public override bool RIBA_5 { get => (RIBA & (1 << 5)) == 1 << 5; set => RIBA = (byte)((RIBA & ~(1 << 5)) | (value ? 1 << 5 : 0)); } // Unused
|
||||||
|
public override bool RIBA_6 { get => (RIBA & (1 << 6)) == 1 << 6; set => RIBA = (byte)((RIBA & ~(1 << 6)) | (value ? 1 << 6 : 0)); } // Unused
|
||||||
|
public override bool RIBA_7 { get => (RIBA & (1 << 7)) == 1 << 7; set => RIBA = (byte)((RIBA & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused
|
||||||
|
public override bool RIBB_0 { get => (RIBB & (1 << 0)) == 1 << 0; set => RIBB = (byte)((RIBB & ~(1 << 0)) | (value ? 1 << 0 : 0)); } // Unused
|
||||||
|
public override bool RIBB_1 { get => (RIBB & (1 << 1)) == 1 << 1; set => RIBB = (byte)((RIBB & ~(1 << 1)) | (value ? 1 << 1 : 0)); } // Unused
|
||||||
|
public override bool RIBB_2 { get => (RIBB & (1 << 2)) == 1 << 2; set => RIBB = (byte)((RIBB & ~(1 << 2)) | (value ? 1 << 2 : 0)); } // Unused
|
||||||
|
public override bool RIBB_3 { get => (RIBB & (1 << 3)) == 1 << 3; set => RIBB = (byte)((RIBB & ~(1 << 3)) | (value ? 1 << 3 : 0)); } // Unused
|
||||||
|
public override bool RIBB_4 { get => (RIBB & (1 << 4)) == 1 << 4; set => RIBB = (byte)((RIBB & ~(1 << 4)) | (value ? 1 << 4 : 0)); } // Unused
|
||||||
|
public override bool RIBB_5 { get => (RIBB & (1 << 5)) == 1 << 5; set => RIBB = (byte)((RIBB & ~(1 << 5)) | (value ? 1 << 5 : 0)); } // Unused
|
||||||
|
public override bool RIBB_6 { get => (RIBB & (1 << 6)) == 1 << 6; set => RIBB = (byte)((RIBB & ~(1 << 6)) | (value ? 1 << 6 : 0)); } // Unused
|
||||||
|
public override bool RIBB_7 { get => (RIBB & (1 << 7)) == 1 << 7; set => RIBB = (byte)((RIBB & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused
|
||||||
|
// 0x64-0x67 Unused
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Block D
|
||||||
|
public override string OT_Name
|
||||||
|
{
|
||||||
|
get => StringConverter4.GetString(OT_Trash);
|
||||||
|
set => StringConverter4.SetString(OT_Trash, value.AsSpan(), 7, StringConverterOption.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Egg_Year { get => Data[0x78]; set => Data[0x78] = (byte)value; }
|
||||||
|
public override int Egg_Month { get => Data[0x79]; set => Data[0x79] = (byte)value; }
|
||||||
|
public override int Egg_Day { get => Data[0x7A]; set => Data[0x7A] = (byte)value; }
|
||||||
|
public override int Met_Year { get => Data[0x7B]; set => Data[0x7B] = (byte)value; }
|
||||||
|
public override int Met_Month { get => Data[0x7C]; set => Data[0x7C] = (byte)value; }
|
||||||
|
public override int Met_Day { get => Data[0x7D]; set => Data[0x7D] = (byte)value; }
|
||||||
|
|
||||||
|
public override ushort Egg_LocationDP
|
||||||
|
{
|
||||||
|
get => ReadUInt16LittleEndian(Data.AsSpan(0x7E));
|
||||||
|
set => WriteUInt16LittleEndian(Data.AsSpan(0x7E), value);
|
||||||
|
}
|
||||||
|
public override ushort Met_LocationDP
|
||||||
|
{
|
||||||
|
get => ReadUInt16LittleEndian(Data.AsSpan(0x80));
|
||||||
|
set => WriteUInt16LittleEndian(Data.AsSpan(0x80), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte PKRS { get => Data[0x82]; set => Data[0x82] = value; }
|
||||||
|
public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); }
|
||||||
|
public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | (value << 4)); }
|
||||||
|
public override byte BallDPPt { get => Data[0x83]; set => Data[0x83] = value; }
|
||||||
|
public override int Met_Level { get => Data[0x84] & ~0x80; set => Data[0x84] = (byte)((Data[0x84] & 0x80) | value); }
|
||||||
|
public override int OT_Gender { get => Data[0x84] >> 7; set => Data[0x84] = (byte)((Data[0x84] & ~0x80) | (value << 7)); }
|
||||||
|
public override GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; }
|
||||||
|
public override byte BallHGSS { get => Data[0x86]; set => Data[0x86] = value; }
|
||||||
|
public override byte PokeathlonStat { get => Data[0x87]; set => Data[0x87] = value; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Battle Stats
|
||||||
|
public override int Status_Condition { get => 0; set { } }
|
||||||
|
public override int Stat_Level { get => CurrentLevel; set { } }
|
||||||
|
public override int Stat_HPCurrent { get => PersonalInfo.HP; set { } }
|
||||||
|
public override int Stat_HPMax { get => PersonalInfo.HP; set { } }
|
||||||
|
public override int Stat_ATK { get => PersonalInfo.ATK; set { } }
|
||||||
|
public override int Stat_DEF { get => PersonalInfo.DEF; set { } }
|
||||||
|
public override int Stat_SPE { get => PersonalInfo.SPE; set { } }
|
||||||
|
public override int Stat_SPA { get => PersonalInfo.SPA; set { } }
|
||||||
|
public override int Stat_SPD { get => PersonalInfo.SPD; set { } }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region My Pokémon Ranch Data
|
||||||
|
|
||||||
|
/* ====Metadata====
|
||||||
|
* uint8_t poke_type;// 01 trainer, 04 hayley, 05 traded
|
||||||
|
* unused alignment byte
|
||||||
|
* uint16_t tradeable;// 02 is tradeable, normal 00
|
||||||
|
* uint16_t tid;
|
||||||
|
* uint16_t sid;
|
||||||
|
* uint32_t name1;
|
||||||
|
* uint32_t name2;
|
||||||
|
* uint32_t name3;
|
||||||
|
* uint32_t name4;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 4 bytes extra at the end of the metadata, unused/reserved; or, it's just extra for the Trainer Name.
|
||||||
|
|
||||||
|
public RanchOwnershipType OwnershipType
|
||||||
|
{
|
||||||
|
get => (RanchOwnershipType)Data[0x88];
|
||||||
|
set => Data[0x88] = (byte)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RanchOwnershipStatus OwnershipStatus
|
||||||
|
{
|
||||||
|
get => (RanchOwnershipStatus)ReadUInt16BigEndian(Data.AsSpan(0x8A));
|
||||||
|
set => WriteUInt16BigEndian(Data.AsSpan(0x8A), (ushort)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort HT_TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x8C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8C), value); }
|
||||||
|
public ushort HT_SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x8E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8E), value); }
|
||||||
|
|
||||||
|
public override Span<byte> HT_Trash => Data.AsSpan(0x90, 0x10);
|
||||||
|
public override string HT_Name
|
||||||
|
{
|
||||||
|
get => StringConverter4.GetString(HT_Trash);
|
||||||
|
set => StringConverter4.SetString(HT_Trash, value.AsSpan(), 7, StringConverterOption.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
public void SetSaveRevision(int ranchSaveRevision)
|
||||||
|
{
|
||||||
|
if (ranchSaveRevision == 0)
|
||||||
|
StripPtHGSSContent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override byte[] Encrypt()
|
||||||
|
{
|
||||||
|
RefreshChecksum();
|
||||||
|
|
||||||
|
byte[] data = (byte[])Data.Clone();
|
||||||
|
byte[] pkData = data.Slice(0, PokeCrypto.SIZE_4STORED);
|
||||||
|
pkData = PokeCrypto.EncryptArray45(pkData);
|
||||||
|
pkData.CopyTo(data, 0);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PK4 ConvertToPK4()
|
||||||
|
{
|
||||||
|
byte[] data = Data.AsSpan(0, PokeCrypto.SIZE_4STORED).ToArray();
|
||||||
|
var pk4 = new PK4(data);
|
||||||
|
pk4.ResetPartyStats();
|
||||||
|
pk4.RefreshChecksum();
|
||||||
|
return pk4;
|
||||||
|
}
|
||||||
|
}
|
|
@ -294,6 +294,15 @@ public abstract class G4PKM : PKM,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce DP content only (no PtHGSS)
|
||||||
|
protected void StripPtHGSSContent(PKM pk)
|
||||||
|
{
|
||||||
|
if (Form != 0 && !PersonalTable.DP[Species].HasForms && Species != 201)
|
||||||
|
pk.Form = 0;
|
||||||
|
if (HeldItem > Legal.MaxItemID_4_DP)
|
||||||
|
pk.HeldItem = 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected T ConvertTo<T>() where T : G4PKM, new()
|
protected T ConvertTo<T>() where T : G4PKM, new()
|
||||||
{
|
{
|
||||||
var pk = new T
|
var pk = new T
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using static PKHeX.Core.EntityConverterResult;
|
using static PKHeX.Core.EntityConverterResult;
|
||||||
|
|
||||||
|
@ -119,6 +119,7 @@ public static class EntityConverter
|
||||||
PK3 pk3 when destType == typeof(CK3) => pk3.ConvertToCK3(),
|
PK3 pk3 when destType == typeof(CK3) => pk3.ConvertToCK3(),
|
||||||
PK3 pk3 when destType == typeof(XK3) => pk3.ConvertToXK3(),
|
PK3 pk3 when destType == typeof(XK3) => pk3.ConvertToXK3(),
|
||||||
PK4 pk4 when destType == typeof(BK4) => pk4.ConvertToBK4(),
|
PK4 pk4 when destType == typeof(BK4) => pk4.ConvertToBK4(),
|
||||||
|
PK4 pk4 when destType == typeof(RK4) => pk4.ConvertToRK4(),
|
||||||
|
|
||||||
PB8 pb8 when destType == typeof(PK8) => pb8.ConvertToPK8(),
|
PB8 pb8 when destType == typeof(PK8) => pb8.ConvertToPK8(),
|
||||||
PK8 pk8 when destType == typeof(PB8) => pk8.ConvertToPB8(),
|
PK8 pk8 when destType == typeof(PB8) => pk8.ConvertToPB8(),
|
||||||
|
@ -141,6 +142,7 @@ public static class EntityConverter
|
||||||
CK3 ck3 => ck3.ConvertToPK3(),
|
CK3 ck3 => ck3.ConvertToPK3(),
|
||||||
XK3 xk3 => xk3.ConvertToPK3(),
|
XK3 xk3 => xk3.ConvertToPK3(),
|
||||||
BK4 bk4 => bk4.ConvertToPK4(),
|
BK4 bk4 => bk4.ConvertToPK4(),
|
||||||
|
RK4 rk4 => rk4.ConvertToPK4(),
|
||||||
|
|
||||||
_ => InvalidTransfer(out result, NoTransferRoute),
|
_ => InvalidTransfer(out result, NoTransferRoute),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ public static class EntityDetection
|
||||||
PokeCrypto.SIZE_2ULIST, PokeCrypto.SIZE_2JLIST, PokeCrypto.SIZE_2STADIUM,
|
PokeCrypto.SIZE_2ULIST, PokeCrypto.SIZE_2JLIST, PokeCrypto.SIZE_2STADIUM,
|
||||||
PokeCrypto.SIZE_3STORED, PokeCrypto.SIZE_3PARTY,
|
PokeCrypto.SIZE_3STORED, PokeCrypto.SIZE_3PARTY,
|
||||||
PokeCrypto.SIZE_3CSTORED, PokeCrypto.SIZE_3XSTORED,
|
PokeCrypto.SIZE_3CSTORED, PokeCrypto.SIZE_3XSTORED,
|
||||||
PokeCrypto.SIZE_4STORED, PokeCrypto.SIZE_4PARTY,
|
PokeCrypto.SIZE_4STORED, PokeCrypto.SIZE_4PARTY, PokeCrypto.SIZE_4RSTORED,
|
||||||
PokeCrypto.SIZE_5PARTY,
|
PokeCrypto.SIZE_5PARTY,
|
||||||
PokeCrypto.SIZE_6STORED, PokeCrypto.SIZE_6PARTY,
|
PokeCrypto.SIZE_6STORED, PokeCrypto.SIZE_6PARTY,
|
||||||
PokeCrypto.SIZE_8STORED, PokeCrypto.SIZE_8PARTY,
|
PokeCrypto.SIZE_8STORED, PokeCrypto.SIZE_8PARTY,
|
||||||
|
@ -29,6 +29,7 @@ public static class EntityDetection
|
||||||
public static bool IsPresentGB(ReadOnlySpan<byte> data) => data[0] != 0; // Species non-zero
|
public static bool IsPresentGB(ReadOnlySpan<byte> data) => data[0] != 0; // Species non-zero
|
||||||
public static bool IsPresentGC(ReadOnlySpan<byte> data) => ReadUInt16BigEndian(data) != 0; // Species non-zero
|
public static bool IsPresentGC(ReadOnlySpan<byte> data) => ReadUInt16BigEndian(data) != 0; // Species non-zero
|
||||||
public static bool IsPresentGBA(ReadOnlySpan<byte> data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies.
|
public static bool IsPresentGBA(ReadOnlySpan<byte> data) => (data[0x13] & 0xFB) == 2; // ignore egg flag, must be FlagHasSpecies.
|
||||||
|
public static bool IsPresentSAV4Ranch(ReadOnlySpan<byte> data) => ReadUInt32LittleEndian(data) != 0 && ReadUInt32BigEndian(data) != 0x28; // Species non-zero, ignore file end marker
|
||||||
|
|
||||||
public static bool IsPresent(ReadOnlySpan<byte> data)
|
public static bool IsPresent(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace PKHeX.Core;
|
namespace PKHeX.Core;
|
||||||
|
@ -30,7 +30,10 @@ public static class EntityFileExtension
|
||||||
result.Add("xk3"); // xd
|
result.Add("xk3"); // xd
|
||||||
}
|
}
|
||||||
if (maxGeneration >= 4)
|
if (maxGeneration >= 4)
|
||||||
|
{
|
||||||
result.Add("bk4"); // battle revolution
|
result.Add("bk4"); // battle revolution
|
||||||
|
result.Add("rk4"); // My Pokemon Ranch
|
||||||
|
}
|
||||||
if (maxGeneration >= 7)
|
if (maxGeneration >= 7)
|
||||||
result.Add(ExtensionPB7); // let's go
|
result.Add(ExtensionPB7); // let's go
|
||||||
if (maxGeneration >= 8)
|
if (maxGeneration >= 8)
|
||||||
|
|
|
@ -29,6 +29,7 @@ public static class EntityFormat
|
||||||
SIZE_3CSTORED => FormatCK3,
|
SIZE_3CSTORED => FormatCK3,
|
||||||
SIZE_3XSTORED => FormatXK3,
|
SIZE_3XSTORED => FormatXK3,
|
||||||
SIZE_4PARTY or SIZE_4STORED => GetFormat45(data),
|
SIZE_4PARTY or SIZE_4STORED => GetFormat45(data),
|
||||||
|
SIZE_4RSTORED => FormatRK4,
|
||||||
SIZE_5PARTY => FormatPK5,
|
SIZE_5PARTY => FormatPK5,
|
||||||
SIZE_6STORED => GetFormat67(data),
|
SIZE_6STORED => GetFormat67(data),
|
||||||
SIZE_6PARTY => GetFormat67_PGT(data),
|
SIZE_6PARTY => GetFormat67_PGT(data),
|
||||||
|
@ -65,6 +66,8 @@ public static class EntityFormat
|
||||||
// assumes decrypted state
|
// assumes decrypted state
|
||||||
private static EntityFormatDetected GetFormat45(ReadOnlySpan<byte> data)
|
private static EntityFormatDetected GetFormat45(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
|
if (data.Length == PokeCrypto.SIZE_4RSTORED)
|
||||||
|
return FormatRK4;
|
||||||
if (ReadUInt16LittleEndian(data[0x4..]) != 0)
|
if (ReadUInt16LittleEndian(data[0x4..]) != 0)
|
||||||
return FormatBK4; // BK4 non-zero sanity
|
return FormatBK4; // BK4 non-zero sanity
|
||||||
if (data[0x5F] < 0x10 && ReadUInt16LittleEndian(data[0x80..]) < 0x3333)
|
if (data[0x5F] < 0x10 && ReadUInt16LittleEndian(data[0x80..]) < 0x3333)
|
||||||
|
@ -114,6 +117,7 @@ public static class EntityFormat
|
||||||
FormatXK3 => new XK3(data),
|
FormatXK3 => new XK3(data),
|
||||||
FormatPK4 => new PK4(data),
|
FormatPK4 => new PK4(data),
|
||||||
FormatBK4 => new BK4(data),
|
FormatBK4 => new BK4(data),
|
||||||
|
FormatRK4 => new RK4(data),
|
||||||
FormatPK5 => new PK5(data),
|
FormatPK5 => new PK5(data),
|
||||||
FormatPK6 => new PK6(data),
|
FormatPK6 => new PK6(data),
|
||||||
FormatPK7 => new PK7(data),
|
FormatPK7 => new PK7(data),
|
||||||
|
@ -202,7 +206,7 @@ public enum EntityFormatDetected
|
||||||
FormatPK1,
|
FormatPK1,
|
||||||
FormatPK2, FormatSK2,
|
FormatPK2, FormatSK2,
|
||||||
FormatPK3, FormatCK3, FormatXK3,
|
FormatPK3, FormatCK3, FormatXK3,
|
||||||
FormatPK4, FormatBK4, FormatPK5,
|
FormatPK4, FormatBK4, FormatRK4, FormatPK5,
|
||||||
FormatPK6, FormatPK7, FormatPB7,
|
FormatPK6, FormatPK7, FormatPB7,
|
||||||
FormatPK8, FormatPA8, FormatPB8,
|
FormatPK8, FormatPA8, FormatPB8,
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ public static class PokeCrypto
|
||||||
|
|
||||||
internal const int SIZE_4PARTY = 236;
|
internal const int SIZE_4PARTY = 236;
|
||||||
internal const int SIZE_4STORED = 136;
|
internal const int SIZE_4STORED = 136;
|
||||||
|
internal const int SIZE_4RSTORED = 164; // 4STORED + 0x1C bytes of extra data
|
||||||
private const int SIZE_4BLOCK = 32;
|
private const int SIZE_4BLOCK = 32;
|
||||||
|
|
||||||
internal const int SIZE_5PARTY = 220;
|
internal const int SIZE_5PARTY = 220;
|
||||||
|
|
|
@ -534,7 +534,7 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa
|
||||||
public int NextOpenBoxSlot(int lastKnownOccupied = -1)
|
public int NextOpenBoxSlot(int lastKnownOccupied = -1)
|
||||||
{
|
{
|
||||||
var storage = BoxBuffer.AsSpan();
|
var storage = BoxBuffer.AsSpan();
|
||||||
int count = BoxSlotCount * BoxCount;
|
int count = SlotCount;
|
||||||
for (int i = lastKnownOccupied + 1; i < count; i++)
|
for (int i = lastKnownOccupied + 1; i < count; i++)
|
||||||
{
|
{
|
||||||
int offset = GetBoxSlotOffset(i);
|
int offset = GetBoxSlotOffset(i);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using static System.Buffers.Binary.BinaryPrimitives;
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
@ -10,75 +11,145 @@ namespace PKHeX.Core;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
|
public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
|
||||||
{
|
{
|
||||||
protected override int SIZE_STORED => 0x88 + 0x1C;
|
protected override int SIZE_STORED => PokeCrypto.SIZE_4RSTORED;
|
||||||
protected override int SIZE_PARTY => SIZE_STORED;
|
protected override int SIZE_PARTY => PokeCrypto.SIZE_4RSTORED;
|
||||||
|
|
||||||
public int SaveRevision => Version == GameVersion.DP ? 0 : 1;
|
public int SaveRevision => Version == GameVersion.DP ? 0 : 1;
|
||||||
public string SaveRevisionString => Version == GameVersion.DP ? "-DP" : "-Pt";
|
public string SaveRevisionString => Version == GameVersion.DP ? "-DP" : "-Pt";
|
||||||
|
|
||||||
public override int BoxCount { get; }
|
// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable
|
||||||
public override int SlotCount { get; }
|
private readonly int DataEndMarker;
|
||||||
|
private int DataEndMarkerOffset;
|
||||||
|
private readonly int MiiDataOffset;
|
||||||
|
private readonly int MiiCountOffset;
|
||||||
|
private readonly int TrainerMiiDataOffset;
|
||||||
|
private readonly int TrainerMiiCountOffset;
|
||||||
|
private readonly int PokemonCountOffset;
|
||||||
|
|
||||||
|
public override int SlotCount => RanchLevel.GetSlotCount(CurrentRanchLevelIndex);
|
||||||
|
public override int BoxCount => (int)Math.Ceiling((decimal)SlotCount / SlotsPerBox);
|
||||||
|
public int MiiCount { get; }
|
||||||
|
public int TrainerMiiCount { get; }
|
||||||
|
public int MaxToys => RanchLevel.GetMaxToys(CurrentRanchLevelIndex);
|
||||||
|
public int MaxMiiCount => RanchLevel.GetMaxMiis(CurrentRanchLevelIndex);
|
||||||
|
|
||||||
public override IPersonalTable Personal => PersonalTable.Pt;
|
public override IPersonalTable Personal => PersonalTable.Pt;
|
||||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_Pt;
|
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_Pt;
|
||||||
protected override SaveFile CloneInternal() => new SAV4Ranch((byte[])Data.Clone());
|
protected override SaveFile CloneInternal() => new SAV4Ranch((byte[])Data.Clone());
|
||||||
public override string PlayTimeString => $"{Checksums.CRC16Invert(Data):X4}";
|
|
||||||
protected internal override string ShortSummary => $"{OT} {PlayTimeString}";
|
protected internal override string ShortSummary => $"{OT} {PlayTimeString}";
|
||||||
public override string Extension => ".bin";
|
public override string Extension => ".bin";
|
||||||
|
|
||||||
protected override PKM GetPKM(byte[] data) => new PK4(data);
|
protected override PKM GetPKM(byte[] data) => new RK4(data);
|
||||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data);
|
|
||||||
public override StorageSlotSource GetSlotFlags(int index) => index >= SlotCount ? StorageSlotSource.Locked : StorageSlotSource.None;
|
public override StorageSlotSource GetSlotFlags(int index) => index >= SlotCount ? StorageSlotSource.Locked : StorageSlotSource.None;
|
||||||
protected override bool IsSlotSwapProtected(int box, int slot) => IsSlotOverwriteProtected(box, slot);
|
protected override bool IsSlotSwapProtected(int box, int slot) => IsSlotOverwriteProtected(box, slot);
|
||||||
|
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentSAV4Ranch(data);
|
||||||
|
|
||||||
public SAV4Ranch(byte[] data) : base(data, typeof(PK4), 0)
|
public SAV4Ranch(byte[] data) : base(data, typeof(RK4), 0)
|
||||||
{
|
{
|
||||||
Version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP;
|
Version = Data.Length == SaveUtil.SIZE_G4RANCH_PLAT ? GameVersion.Pt : GameVersion.DP;
|
||||||
|
|
||||||
OT = GetString(0x770, 0x12);
|
OT = GetString(0x770, 0x12);
|
||||||
|
|
||||||
// 0x18 starts the header table
|
// 0x18 starts the header table: [u32 BlockID, u32 Offset]
|
||||||
// Block 00, Offset = ???
|
// Block 00, Offset = Metadata object
|
||||||
// Block 01, Offset = Mii Data
|
// Block 01, Offset = Mii Data Array object
|
||||||
// Block 02, Offset = Mii Link Data
|
// Block 02, Offset = Mii Link Data Array object
|
||||||
// Block 03, Offset = Pokemon Data
|
// Block 03, Offset = Pokemon Data Array object
|
||||||
// Block 04, Offset = ??
|
// Block 04, Offset = reserved object
|
||||||
|
|
||||||
// Unpack the binary a little:
|
// Unpack the binary a little:
|
||||||
// size, count, Mii data[count]
|
// 00: size, ???
|
||||||
// size, count, Mii Link data[count]
|
// 01: size, count, Mii data[count]
|
||||||
// size, count, Pokemon (PK4 + metadata)[count]
|
// 02: size, count, Mii Link data[count]
|
||||||
// size, count, ???
|
// 03: size, count, Pokemon (PK4 + metadata)[count]
|
||||||
|
// 04: size, count, ???
|
||||||
|
|
||||||
/* ====Metadata====
|
MiiCountOffset = ReadInt32BigEndian(Data.AsSpan(0x24)) + 4;
|
||||||
* uint8_t poke_type;// 01 trainer, 04 hayley, 05 traded
|
TrainerMiiCountOffset = ReadInt32BigEndian(Data.AsSpan(0x2C)) + 4;
|
||||||
* uint8_t tradeable;// 02 is tradeable, normal 00
|
MiiCount = ReadInt32BigEndian(Data.AsSpan(MiiCountOffset));
|
||||||
* uint16_t tid;
|
TrainerMiiCount = ReadInt32BigEndian(Data.AsSpan(TrainerMiiCountOffset));
|
||||||
* uint16_t sid;
|
|
||||||
* uint32_t name1;
|
|
||||||
* uint32_t name2;
|
|
||||||
* uint32_t name3;
|
|
||||||
* uint32_t name4;
|
|
||||||
*/
|
|
||||||
|
|
||||||
var pkCountOffset = ReadInt32BigEndian(Data.AsSpan(0x34)) + 4;
|
MiiDataOffset = MiiCountOffset + 4;
|
||||||
SlotCount = ReadInt32BigEndian(Data.AsSpan(pkCountOffset));
|
TrainerMiiDataOffset = TrainerMiiCountOffset + 4;
|
||||||
BoxCount = (int)Math.Ceiling((decimal)SlotCount / SlotsPerBox);
|
|
||||||
|
|
||||||
Box = pkCountOffset + 4;
|
PokemonCountOffset = ReadInt32BigEndian(Data.AsSpan(0x34)) + 4;
|
||||||
|
Box = PokemonCountOffset + 4;
|
||||||
|
|
||||||
FinalCountOffset = ReadInt32BigEndian(Data.AsSpan(0x3C));
|
DataEndMarkerOffset = ReadInt32BigEndian(Data.AsSpan(0x3C));
|
||||||
FinalCount = ReadInt32BigEndian(Data.AsSpan(FinalCountOffset));
|
DataEndMarker = ReadInt32BigEndian(Data.AsSpan(DataEndMarkerOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly int FinalCount;
|
private const int ToyBaseOffset = 0x227B;
|
||||||
private readonly int FinalCountOffset;
|
|
||||||
|
public byte CurrentRanchLevelIndex { get => Data[0x5A]; set => Data[0x5A] = value; }
|
||||||
|
public byte PlannedRanchLevelIndex { get => Data[0x5B]; set => Data[0x5B] = value; } // tomorrow's level
|
||||||
|
|
||||||
|
public uint SecondsSince2000 { get => ReadUInt32BigEndian(Data.AsSpan(0x5C)); set => WriteUInt32BigEndian(Data.AsSpan(0x5C), value); }
|
||||||
|
public uint TotalSeconds { get => ReadUInt32BigEndian(Data.AsSpan(0x60)); set => WriteUInt32BigEndian(Data.AsSpan(0x60), value); }
|
||||||
|
public ushort NextHayleyBringNationalDex { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), value); }
|
||||||
|
|
||||||
|
public RanchToy GetRanchToy(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= MaxToys)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int toyOffset = ToyBaseOffset + (RanchToy.SIZE * index);
|
||||||
|
var data = Data.Slice(toyOffset, RanchToy.SIZE);
|
||||||
|
return new RanchToy(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRanchToy(RanchToy toy, int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= MaxToys)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int toyOffset = ToyBaseOffset + (RanchToy.SIZE * index);
|
||||||
|
SetData(Data, toy.Data, toyOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RanchMii GetRanchMii(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= MiiCount)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int offset = MiiDataOffset + (RanchMii.SIZE * index);
|
||||||
|
var data = Data.Slice(offset, RanchMii.SIZE);
|
||||||
|
return new RanchMii(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRanchMii(RanchMii trainer, int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= MiiCount)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int offset = MiiDataOffset + (RanchMii.SIZE * index);
|
||||||
|
SetData(Data, trainer.Data, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RanchTrainerMii GetRanchTrainerMii(int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= TrainerMiiCount)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int offset = TrainerMiiDataOffset + (RanchTrainerMii.SIZE * index);
|
||||||
|
var data = Data.Slice(offset, RanchTrainerMii.SIZE);
|
||||||
|
return new RanchTrainerMii(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRanchTrainerMii(RanchTrainerMii mii, int index)
|
||||||
|
{
|
||||||
|
if ((uint)index >= TrainerMiiCount)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
int offset = TrainerMiiDataOffset + (RanchTrainerMii.SIZE * index);
|
||||||
|
SetData(Data, mii.Data, offset);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void SetChecksums()
|
protected override void SetChecksums()
|
||||||
{
|
{
|
||||||
// ensure the final data is written if the user screws stuff up
|
// ensure the final data is written if the user screws stuff up
|
||||||
WriteInt32BigEndian(Data.AsSpan(FinalCountOffset), FinalCount);
|
WriteInt32BigEndian(Data.AsSpan(DataEndMarkerOffset), DataEndMarker);
|
||||||
var goodlen = (FinalCountOffset + 4);
|
var goodlen = (DataEndMarkerOffset + 4);
|
||||||
Array.Clear(Data, goodlen, Data.Length - goodlen);
|
Array.Clear(Data, goodlen, Data.Length - goodlen);
|
||||||
|
|
||||||
// 20 byte SHA checksum at the top of the file, which covers all data that follows.
|
// 20 byte SHA checksum at the top of the file, which covers all data that follows.
|
||||||
|
@ -87,6 +158,78 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
|
||||||
SetData(result, 0);
|
SetData(result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override byte[] DecryptPKM(byte[] data)
|
||||||
|
{
|
||||||
|
var pokeData = PokeCrypto.DecryptArray45(data.Slice(0, PokeCrypto.SIZE_4STORED));
|
||||||
|
var ranchData = data.AsSpan(PokeCrypto.SIZE_4STORED, 0x1C);
|
||||||
|
var finalData = new byte[SIZE_STORED];
|
||||||
|
|
||||||
|
pokeData.CopyTo(finalData, 0);
|
||||||
|
ranchData.CopyTo(finalData.AsSpan(PokeCrypto.SIZE_4STORED));
|
||||||
|
return finalData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteBoxSlotInternal(PKM pk, Span<byte> data, int offset, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley)
|
||||||
|
{
|
||||||
|
RK4 rk = (RK4)this.GetCompatiblePKM(pk);
|
||||||
|
rk.OwnershipType = type;
|
||||||
|
rk.HT_TID = htTID;
|
||||||
|
rk.HT_SID = htSID;
|
||||||
|
rk.HT_Name = htName;
|
||||||
|
|
||||||
|
WriteBoxSlot(rk, data, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteBoxSlot(PKM pk, Span<byte> data, int offset)
|
||||||
|
{
|
||||||
|
bool isBlank = pk.Data.SequenceEqual(BlankPKM.Data);
|
||||||
|
if (pk is not RK4 rk4)
|
||||||
|
{
|
||||||
|
WriteBoxSlotInternal(pk, data, offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBlank && rk4.OwnershipType == RanchOwnershipType.None)
|
||||||
|
rk4.OwnershipType = RanchOwnershipType.Hayley; // Pokemon without an Ownership type get erased when the save is loaded. Hayley is considered 'default'.
|
||||||
|
|
||||||
|
base.WriteBoxSlot(rk4, data, offset);
|
||||||
|
if ((offset + SIZE_STORED) > DataEndMarkerOffset)
|
||||||
|
{
|
||||||
|
DataEndMarkerOffset = (offset + SIZE_STORED);
|
||||||
|
WriteInt32BigEndian(Data.AsSpan(0x3C), DataEndMarkerOffset);
|
||||||
|
WriteInt32BigEndian(Data.AsSpan(DataEndMarkerOffset), DataEndMarker);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pkStart = PokemonCountOffset + 4;
|
||||||
|
int pkEnd = DataEndMarkerOffset;
|
||||||
|
int pkCount = (pkEnd - pkStart) / SIZE_STORED;
|
||||||
|
WriteInt32BigEndian(Data.AsSpan(PokemonCountOffset), pkCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeSpan PlayedSpan
|
||||||
|
{
|
||||||
|
get => TimeSpan.FromSeconds(TotalSeconds);
|
||||||
|
set => TotalSeconds = (uint)value.TotalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int PlayedHours
|
||||||
|
{
|
||||||
|
get => (ushort)PlayedSpan.TotalHours;
|
||||||
|
set { var time = PlayedSpan; PlayedSpan = time - TimeSpan.FromHours(time.TotalHours) + TimeSpan.FromHours(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int PlayedMinutes
|
||||||
|
{
|
||||||
|
get => (byte)PlayedSpan.Minutes;
|
||||||
|
set { var time = PlayedSpan; PlayedSpan = time - TimeSpan.FromMinutes(time.Minutes) + TimeSpan.FromMinutes(value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int PlayedSeconds
|
||||||
|
{
|
||||||
|
get => (byte)PlayedSpan.Seconds;
|
||||||
|
set { var time = PlayedSpan; PlayedSpan = time - TimeSpan.FromSeconds(time.Seconds) + TimeSpan.FromSeconds(value); }
|
||||||
|
}
|
||||||
|
|
||||||
public override string GetString(ReadOnlySpan<byte> data) => StringConverter4GC.GetStringUnicode(data);
|
public override string GetString(ReadOnlySpan<byte> data) => StringConverter4GC.GetStringUnicode(data);
|
||||||
|
|
||||||
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
|
public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, int maxLength, StringConverterOption option)
|
||||||
|
|
61
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchLevel.cs
Normal file
61
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchLevel.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logic to calculate max counts denoted by the ranch level in My Pokemon Ranch
|
||||||
|
/// </summary>
|
||||||
|
public static class RanchLevel
|
||||||
|
{
|
||||||
|
public static int GetLevel(byte levelIndex) => levelIndex + 1;
|
||||||
|
|
||||||
|
public static int GetMaxMiis(byte levelIndex) => levelIndex switch
|
||||||
|
{
|
||||||
|
>= 11 => 20,
|
||||||
|
>= 08 => 15,
|
||||||
|
>= 04 => 10,
|
||||||
|
_ => 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static int GetMaxToys(byte levelIndex) => levelIndex switch
|
||||||
|
{
|
||||||
|
>= 25 => 6,
|
||||||
|
>= 20 => 5,
|
||||||
|
>= 15 => 4,
|
||||||
|
>= 11 => 3,
|
||||||
|
>= 08 => 2,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static int GetSlotCount(byte levelIndex) => levelIndex switch
|
||||||
|
{
|
||||||
|
00 => 020,
|
||||||
|
01 => 025,
|
||||||
|
02 => 030,
|
||||||
|
03 => 040,
|
||||||
|
04 => 050,
|
||||||
|
05 => 060,
|
||||||
|
06 => 080,
|
||||||
|
|
||||||
|
07 => 100,
|
||||||
|
08 => 150,
|
||||||
|
09 => 200,
|
||||||
|
10 => 250,
|
||||||
|
11 => 300,
|
||||||
|
12 => 350,
|
||||||
|
|
||||||
|
13 => 400,
|
||||||
|
14 => 500,
|
||||||
|
15 => 600,
|
||||||
|
16 => 700,
|
||||||
|
17 => 800,
|
||||||
|
18 => 900,
|
||||||
|
19 => 1000,
|
||||||
|
|
||||||
|
20 => 1000,
|
||||||
|
21 => 1000,
|
||||||
|
22 => 1000,
|
||||||
|
23 => 1000,
|
||||||
|
24 => 1000,
|
||||||
|
|
||||||
|
_ => 1500,
|
||||||
|
};
|
||||||
|
}
|
22
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchMii.cs
Normal file
22
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchMii.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using System;
|
||||||
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public sealed class RanchMii
|
||||||
|
{
|
||||||
|
public const int SIZE = 0x28;
|
||||||
|
public readonly byte[] Data;
|
||||||
|
|
||||||
|
public RanchMii(byte[] data) => Data = data;
|
||||||
|
|
||||||
|
public uint MiiId { get => ReadUInt32BigEndian(Data); set => WriteUInt32BigEndian(Data, value); }
|
||||||
|
public uint SystemId { get => ReadUInt32BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); }
|
||||||
|
public Span<byte> Name_Trash => Data.AsSpan(0x10, 0x18);
|
||||||
|
|
||||||
|
public string MiiName
|
||||||
|
{
|
||||||
|
get => StringConverter4GC.GetStringUnicode(Name_Trash);
|
||||||
|
set => StringConverter4GC.SetStringUnicode(value.AsSpan(), Name_Trash, value.Length, StringConverterOption.None);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public enum RanchOwnershipType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Trainer = 1,
|
||||||
|
Hayley = 4,
|
||||||
|
Hayley_Traded = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
// this might actually be an index to which Mii trainer owns the pokemon
|
||||||
|
public enum RanchOwnershipStatus : ushort
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Traded = 2,
|
||||||
|
}
|
16
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchToy.cs
Normal file
16
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchToy.cs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public sealed class RanchToy
|
||||||
|
{
|
||||||
|
public const int SIZE = 0x08;
|
||||||
|
public readonly byte[] Data;
|
||||||
|
|
||||||
|
public RanchToy(byte[] ranchToyData) => Data = ranchToyData;
|
||||||
|
|
||||||
|
public RanchToyType ToyType { get => (RanchToyType)Data[0]; set => Data[0] = (byte)value; }
|
||||||
|
// 1,2,3 alignment
|
||||||
|
public uint ToyMetadata { get => ReadUInt32BigEndian(Data.AsSpan(4)); set => WriteUInt32BigEndian(Data.AsSpan(4), value); }
|
||||||
|
}
|
34
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchToyType.cs
Normal file
34
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchToyType.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toys used in My Pokemon Ranch save files.
|
||||||
|
/// </summary>
|
||||||
|
public enum RanchToyType : byte
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Poke_Balloons = 1,
|
||||||
|
Slippery_Peel = 2,
|
||||||
|
Poke_Bell = 3,
|
||||||
|
BounceBack_Ball = 4,
|
||||||
|
Poke_Rocket = 5,
|
||||||
|
Poke_Cushion = 6,
|
||||||
|
Parade_Drum = 7,
|
||||||
|
Bonfire = 8,
|
||||||
|
Leader_Flag = 9,
|
||||||
|
Fountain = 10,
|
||||||
|
Ice_Block = 11,
|
||||||
|
Poke_Microphone = 12,
|
||||||
|
Burst_Ball = 13,
|
||||||
|
Poke_Palette = 14,
|
||||||
|
Poke_Pendulum = 15,
|
||||||
|
Pitfall = 16,
|
||||||
|
Training_Bag = 17,
|
||||||
|
Stinky_Ball = 18,
|
||||||
|
Snowman = 19,
|
||||||
|
Round_Rock = 20,
|
||||||
|
Spin_Ride = 21,
|
||||||
|
Sprint_Stand = 22,
|
||||||
|
Attractor = 23,
|
||||||
|
Challenger = 24,
|
||||||
|
Toy_Box = 25,
|
||||||
|
}
|
33
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchTrainerMii.cs
Normal file
33
PKHeX.Core/Saves/Substructures/Gen4/Ranch/RanchTrainerMii.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using System;
|
||||||
|
using static System.Buffers.Binary.BinaryPrimitives;
|
||||||
|
|
||||||
|
namespace PKHeX.Core;
|
||||||
|
|
||||||
|
public sealed class RanchTrainerMii
|
||||||
|
{
|
||||||
|
public const int SIZE = 0x2C;
|
||||||
|
public readonly byte[] Data;
|
||||||
|
|
||||||
|
public RanchTrainerMii(byte[] data) => Data = data;
|
||||||
|
|
||||||
|
public uint MiiId { get => ReadUInt32BigEndian(Data.AsSpan(0x00)); set => WriteUInt32BigEndian(Data.AsSpan(0x00), value); }
|
||||||
|
public uint SystemId { get => ReadUInt32BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); }
|
||||||
|
|
||||||
|
public ushort TrainerId { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), value); }
|
||||||
|
public ushort SecretId { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), value); }
|
||||||
|
|
||||||
|
private Span<byte> Trainer_Trash => Data.AsSpan(0x10, 0x10);
|
||||||
|
|
||||||
|
// 0x20-23: ??
|
||||||
|
// 0x24: ??
|
||||||
|
// 0x25: ??
|
||||||
|
// 0x26-27: ??
|
||||||
|
// 0x28-29: ??
|
||||||
|
// 0x2A-2B: ??
|
||||||
|
|
||||||
|
public string TrainerName
|
||||||
|
{
|
||||||
|
get => StringConverter4.GetString(Trainer_Trash);
|
||||||
|
set => StringConverter4.SetString(Trainer_Trash, value.AsSpan(), 7);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ Pokémon core series save editor, programmed in [C#](https://en.wikipedia.org/wi
|
||||||
Supports the following files:
|
Supports the following files:
|
||||||
* Save files ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
* Save files ("main", \*.sav, \*.dsv, \*.dat, \*.gci, \*.bin)
|
||||||
* GameCube Memory Card files (\*.raw, \*.bin) containing GC Pokémon savegames.
|
* GameCube Memory Card files (\*.raw, \*.bin) containing GC Pokémon savegames.
|
||||||
* Individual Pokémon entity files (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4)
|
* Individual Pokémon entity files (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||||
* Mystery Gift files (\*.pgt, \*.pcd, \*.pgf, .wc\*) including conversion to .pk\*
|
* Mystery Gift files (\*.pgt, \*.pcd, \*.pgf, .wc\*) including conversion to .pk\*
|
||||||
* Importing GO Park entities (\*.gp1) including conversion to .pb7
|
* Importing GO Park entities (\*.gp1) including conversion to .pb7
|
||||||
* Importing teams from Decrypted 3DS Battle Videos
|
* Importing teams from Decrypted 3DS Battle Videos
|
||||||
|
|
Loading…
Reference in a new issue