mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-04-22 07:18:18 -05:00
PKM: Reduce allocation via in-place decryption logic (#4764)
This commit is contained in:
parent
793525875f
commit
5bf1e2cf45
|
|
@ -100,7 +100,13 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
|
|||
public string Relearn2 => Get(Strings.movelist, Entity.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, Entity.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, Entity.RelearnMove4);
|
||||
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data[Entity.SIZE_STORED..]);
|
||||
public ushort Checksum => Entity switch
|
||||
{
|
||||
ISanityChecksum s => s.Checksum,
|
||||
PK1 gb => gb.GetSingleListChecksum(),
|
||||
PK2 gb => gb.GetSingleListChecksum(),
|
||||
_ => Checksums.CRC16_CCITT(Entity.Data[Entity.SIZE_STORED..]),
|
||||
};
|
||||
public int Friendship => Entity.OriginalTrainerFriendship;
|
||||
public int EggYear => Entity.EggMetDate.GetValueOrDefault().Year;
|
||||
public int EggMonth => Entity.EggMetDate.GetValueOrDefault().Month;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ public static string GetMessage(PKM pk)
|
|||
return GetMessage(pk7);
|
||||
|
||||
var server = GetExploitURLPrefixPKM(pk.Format);
|
||||
var data = pk.EncryptedBoxData;
|
||||
Span<byte> data = stackalloc byte[pk.SIZE_STORED];
|
||||
pk.WriteEncryptedDataStored(data);
|
||||
return GetMessageBase64(data, server);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@ public sealed class FakeSaveFile : SaveFile
|
|||
protected override void SetChecksums() { }
|
||||
public override GameVersion Version { get => GameVersion.R; set { } }
|
||||
public override Type PKMType => typeof(PK3);
|
||||
protected override PK3 GetPKM(byte[] data) => BlankPKM;
|
||||
protected override byte[] DecryptPKM(byte[] data) => data;
|
||||
protected override PK3 GetPKM(Memory<byte> data) => BlankPKM;
|
||||
protected override void DecryptPKM(Span<byte> data) { }
|
||||
public override PK3 BlankPKM => new();
|
||||
public override EntityContext Context => EntityContext.Gen3;
|
||||
protected override int SIZE_STORED => 0;
|
||||
protected override int SIZE_PARTY => 0;
|
||||
public override int SIZE_STORED => 0;
|
||||
public override int SIZE_PARTY => 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public static int Export(SaveFile sav, string destPath, IFileNamer<PKM> namer, B
|
|||
int count = GetSlotCountForBox(boxSlotCount, box, total);
|
||||
int ctr = 0;
|
||||
// Export each slot in the box.
|
||||
Span<byte> data = stackalloc byte[sav.SIZE_STORED];
|
||||
for (int slot = 0; slot < count; slot++)
|
||||
{
|
||||
var pk = sav.GetBoxSlotAtIndex(box, slot);
|
||||
|
|
@ -98,7 +99,8 @@ public static int Export(SaveFile sav, string destPath, IFileNamer<PKM> namer, B
|
|||
|
||||
var fileName = GetFileName(pk, settings.FileIndexPrefix, namer, box, slot, boxSlotCount);
|
||||
var fn = Path.Combine(destPath, fileName);
|
||||
File.WriteAllBytes(fn, pk.DecryptedPartyData);
|
||||
pk.WriteDecryptedDataStored(data);
|
||||
File.WriteAllBytes(fn, data);
|
||||
ctr++;
|
||||
}
|
||||
return ctr;
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ private static List<SlotInfoMisc> GetExtraSlots9a(SAV9ZA sav)
|
|||
{
|
||||
const int size = 0x1F0;
|
||||
var ofs = (i * size) + 8;
|
||||
var entry = shinyCache.Raw.Slice(ofs, PokeCrypto.SIZE_9PARTY);
|
||||
var entry = shinyCache.Raw.Slice(ofs, PokeCrypto.SIZE_8PARTY);
|
||||
if (EntityDetection.IsPresent(entry.Span))
|
||||
list.Add(new(entry, i, true) { Type = StorageSlotType.Shiny, HideLegality = true }); // no OT info
|
||||
else
|
||||
|
|
@ -266,7 +266,7 @@ private static List<SlotInfoMisc> GetExtraSlots9a(SAV9ZA sav)
|
|||
{
|
||||
const int size = 0x1A8;
|
||||
var ofs = (i * size) + 8;
|
||||
var entry = giveAway.Raw.Slice(ofs, PokeCrypto.SIZE_9PARTY);
|
||||
var entry = giveAway.Raw.Slice(ofs, PokeCrypto.SIZE_8PARTY);
|
||||
if (EntityDetection.IsPresent(entry.Span))
|
||||
list.Add(new(entry, i, true, Mutable: true) { Type = StorageSlotType.Scripted });
|
||||
else
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public override ReadOnlySpan<byte> Write()
|
|||
{
|
||||
// Ensure PGT content is encrypted
|
||||
var clone = new PCD(Data.ToArray());
|
||||
clone.Gift.VerifyPKEncryption();
|
||||
clone.Gift.VerifyGiftEncryption();
|
||||
return clone.Data;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,17 +45,16 @@ public override byte Ball
|
|||
public int ItemSubID { get => ReadInt32LittleEndian(Data[0x8..]); set => WriteInt32LittleEndian(Data[0x8..], value); }
|
||||
public int PokewalkerCourseID { get => Data[0x4]; set => Data[0x4] = (byte)value; }
|
||||
|
||||
private Span<byte> DataGift => Data.Slice(8, PokeCrypto.SIZE_4PARTY);
|
||||
|
||||
public PK4 PK
|
||||
{
|
||||
get => field ??= new PK4(Data.Slice(8, PokeCrypto.SIZE_4PARTY).ToArray());
|
||||
get => field ??= new PK4(DataGift.ToArray());
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
var data = value.Data;
|
||||
bool zero = !data.ContainsAnyExcept<byte>(0); // all zero
|
||||
if (!zero)
|
||||
data = PokeCrypto.EncryptArray45(data);
|
||||
data.CopyTo(Data[8..]);
|
||||
field = value.Clone(); // cache the PK4 for future use
|
||||
value.Data.CopyTo(DataGift);
|
||||
VerifyGiftEncryption();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -63,29 +62,27 @@ public override ReadOnlySpan<byte> Write()
|
|||
{
|
||||
// Ensure PGT content is encrypted
|
||||
var clone = new PGT(Data.ToArray());
|
||||
clone.VerifyPKEncryption();
|
||||
clone.VerifyGiftEncryption();
|
||||
return clone.Data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Double-checks the encryption of the gift data for Pokémon data.
|
||||
/// Double-checks the encryption of the gift data.
|
||||
/// </summary>
|
||||
/// <returns>True if data was encrypted, false if the data was not modified.</returns>
|
||||
public bool VerifyPKEncryption()
|
||||
public bool VerifyGiftEncryption()
|
||||
{
|
||||
if (GiftType is not (Pokémon or PokémonEgg))
|
||||
return false; // not encrypted
|
||||
if (ReadUInt32LittleEndian(Data[(0x64 + 8)..]) != 0)
|
||||
return false; // already encrypted (unused PK4 field, zero)
|
||||
EncryptPK();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void EncryptPK()
|
||||
{
|
||||
var span = Data.Slice(8, PokeCrypto.SIZE_4PARTY);
|
||||
var ekdata = PokeCrypto.EncryptArray45(span);
|
||||
ekdata.CopyTo(span);
|
||||
var gift = DataGift;
|
||||
var isEmpty = !gift.ContainsAnyExcept<byte>(0); // all zero
|
||||
if (isEmpty) // shouldn't ever be empty, just return if so.
|
||||
return false;
|
||||
if (PokeCrypto.IsEncrypted45(gift)) // unused PK4 ribbon bits
|
||||
return false;
|
||||
PokeCrypto.Encrypt45(gift);
|
||||
return true;
|
||||
}
|
||||
|
||||
public GiftType4 GiftType { get => (GiftType4)CardType; set => CardType = (byte)value; }
|
||||
|
|
|
|||
|
|
@ -22,19 +22,11 @@ public sealed class BK4 : G4PKM
|
|||
public override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
public override EntityContext Context => EntityContext.Gen4;
|
||||
public override PersonalInfo4 PersonalInfo => PersonalTable.HGSS[Species];
|
||||
|
||||
public override byte[] DecryptedBoxData => EncryptedBoxData;
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt4BE(stored);
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
public override bool Valid => ChecksumValid || (Sanity == 0 && Species <= MaxSpeciesID);
|
||||
|
||||
public static BK4 ReadUnshuffle(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var unshuffled = PokeCrypto.DecryptArray4BE(data);
|
||||
var result = new BK4(unshuffled);
|
||||
result.RefreshChecksum();
|
||||
return result;
|
||||
}
|
||||
|
||||
public BK4(Memory<byte> data) : base(data)
|
||||
{
|
||||
Sanity = 0x4000;
|
||||
|
|
@ -302,12 +294,6 @@ public override ushort MetLocationDP
|
|||
// Methods
|
||||
protected override ushort CalculateChecksum() => Checksums.Add16BigEndian(Data[8..PokeCrypto.SIZE_4STORED]);
|
||||
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray4BE(Data);
|
||||
}
|
||||
|
||||
public PK4 ConvertToPK4()
|
||||
{
|
||||
PK4 pk4 = ConvertTo<PK4>();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ public sealed class CK3(Memory<byte> Raw) : G3PKM(Raw), IShadowCapture, ISeparat
|
|||
public override EntityContext Context => EntityContext.Gen3;
|
||||
public override PersonalInfo3 PersonalInfo => PersonalTable.RS[Species];
|
||||
public override CK3 Clone() => new(Data.ToArray());
|
||||
protected override void EncryptStored(Span<byte> stored) { }
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> OriginalTrainerTrash => Data.Slice(0x18, 22);
|
||||
|
|
@ -238,8 +240,6 @@ public bool IsFatefulValid(bool japanese)
|
|||
public const int Purified = -100;
|
||||
public bool IsShadow => ShadowID != 0 && Purification != Purified;
|
||||
|
||||
protected override byte[] Encrypt() => Data.ToArray();
|
||||
|
||||
public PK3 ConvertToPK3()
|
||||
{
|
||||
var pk = ConvertTo<PK3>();
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
|
|||
public GameDataPA9? DataPA9 { get; private set; }
|
||||
|
||||
public override EntityContext Context => EntityContext.None;
|
||||
public override int WriteDecryptedDataStored(Span<byte> destination) => Rebuild(destination);
|
||||
protected override void EncryptStored(Span<byte> stored) { }
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
public PKH(Memory<byte> data) : base(DecryptHome(data))
|
||||
{
|
||||
|
|
@ -255,24 +258,46 @@ private static Memory<byte> DecryptHome(Memory<byte> data)
|
|||
public override void RefreshChecksum() => Checksum = 0;
|
||||
public override bool ChecksumValid => true;
|
||||
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
var result = Rebuild();
|
||||
return HomeCrypto.Encrypt(result);
|
||||
}
|
||||
|
||||
public byte[] Rebuild()
|
||||
{
|
||||
var length = WriteLength;
|
||||
|
||||
// Handle PKCS7 manually
|
||||
var remainder = length & 0xF;
|
||||
var totalSize = GetPaddedSize(length, out var remainder);
|
||||
|
||||
var result = new byte[totalSize];
|
||||
WriteTo(result, length, remainder, totalSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Rebuild(Span<byte> dest)
|
||||
{
|
||||
var length = WriteLength;
|
||||
// Handle PKCS7 manually
|
||||
var totalSize = GetPaddedSize(length, out var remainder);
|
||||
|
||||
var result = dest[..totalSize];
|
||||
WriteTo(result, length, remainder, totalSize);
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
private void WriteTo(Span<byte> data, int innerLength, int remainder, int totalSize)
|
||||
{
|
||||
var payload = data[..innerLength];
|
||||
data[innerLength..].Fill((byte)remainder);
|
||||
WriteTo(payload, totalSize);
|
||||
}
|
||||
|
||||
public static int GetPaddedSize(int innerLength, out int remainder)
|
||||
{
|
||||
remainder = innerLength & 0xF;
|
||||
if (remainder != 0) // pad to nearest 0x10, fill remainder bytes with value.
|
||||
remainder = 0x10 - remainder;
|
||||
var result = new byte[length + remainder];
|
||||
var span = result.AsSpan(0, length);
|
||||
result.AsSpan(length).Fill((byte)remainder);
|
||||
var totalSize = innerLength + remainder;
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
private void WriteTo(Span<byte> span, int innerLength)
|
||||
{
|
||||
// Header and Core are already in the current byte array.
|
||||
// Write each part, starting with header and core.
|
||||
int ctr = HomeCrypto.SIZE_1HEADER + 2;
|
||||
|
|
@ -289,11 +314,9 @@ public byte[] Rebuild()
|
|||
|
||||
// Update metadata to ensure we're a valid object.
|
||||
DataVersion = HomeCrypto.VersionLatest;
|
||||
EncodedDataSize = (ushort)(result.Length - HomeCrypto.SIZE_1HEADER);
|
||||
EncodedDataSize = (ushort)(innerLength - HomeCrypto.SIZE_1HEADER);
|
||||
CoreDataSize = (ushort)Core.SerializedSize;
|
||||
Data[..(HomeCrypto.SIZE_1HEADER + 2)].CopyTo(span); // Copy updated header & CoreData length.
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int WriteLength
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
|||
public override EntityContext Context => EntityContext.Gen8a;
|
||||
public PA8() : base(PokeCrypto.SIZE_8APARTY) => AffixedRibbon = Core.AffixedRibbon.None;
|
||||
public PA8(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt8A(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
|
|
@ -43,7 +45,7 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted8A(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted8A(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_8APARTY)
|
||||
return data;
|
||||
|
||||
|
|
@ -86,11 +88,6 @@ public override byte CurrentFriendship
|
|||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInit0(EncryptionConstant, IV32);
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray8A(Data);
|
||||
}
|
||||
|
||||
public void FixRelearn()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,24 +21,26 @@ public sealed class PA9 : PKM, ISanityChecksum, ITechRecord, IObedienceLevel, IH
|
|||
public override PersonalInfo9ZA PersonalInfo => PersonalTable.ZA.GetFormEntry(Species, Form);
|
||||
public IPermitRecord Permit => PersonalInfo;
|
||||
public override EntityContext Context => EntityContext.Gen9a;
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt8(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
public PA9() : base(PokeCrypto.SIZE_9PARTY) => AffixedRibbon = PKHeX.Core.AffixedRibbon.None;
|
||||
public PA9() : base(PokeCrypto.SIZE_8PARTY) => AffixedRibbon = PKHeX.Core.AffixedRibbon.None;
|
||||
|
||||
public PA9(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
public override PA9 Clone() => new(Data.ToArray());
|
||||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted9(ref data);
|
||||
if (data.Length >= PokeCrypto.SIZE_9PARTY)
|
||||
PokeCrypto.DecryptIfEncrypted8(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_8PARTY)
|
||||
return data;
|
||||
|
||||
var result = new byte[PokeCrypto.SIZE_9PARTY];
|
||||
var result = new byte[PokeCrypto.SIZE_8PARTY];
|
||||
data.Span.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[8..PokeCrypto.SIZE_9STORED]);
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[8..PokeCrypto.SIZE_8STORED]);
|
||||
|
||||
// Simple Generated Attributes
|
||||
public override byte CurrentFriendship
|
||||
|
|
@ -47,8 +49,8 @@ public override byte CurrentFriendship
|
|||
set { if (CurrentHandler == 0) OriginalTrainerFriendship = value; else HandlingTrainerFriendship = value; }
|
||||
}
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_9PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_9STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
|
||||
public override bool ChecksumValid => CalculateChecksum() == Checksum;
|
||||
public override void RefreshChecksum() => Checksum = CalculateChecksum();
|
||||
|
|
@ -76,11 +78,6 @@ public override byte CurrentFriendship
|
|||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInit0(EncryptionConstant, IV32);
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray9(Data);
|
||||
}
|
||||
|
||||
public void FixRelearn()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted67(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted67(data.Span);
|
||||
if (data.Length >= SIZE)
|
||||
return data;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ public sealed class PK1 : GBPKML, IPersonalType
|
|||
|
||||
public override bool Valid => Species <= 151 && (Data[0] == 0 || Species != 0);
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_1PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_1STORED;
|
||||
public override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
||||
public override int SIZE_PARTY => SIZE_STORED;
|
||||
public override bool Korean => false;
|
||||
|
||||
public override EntityContext Context => EntityContext.Gen1;
|
||||
|
||||
public PK1(bool jp = false) : base(PokeCrypto.SIZE_1PARTY, jp) { }
|
||||
public PK1(byte[] decryptedData, bool jp = false) : base(EnsurePartySize(decryptedData), jp) { }
|
||||
public PK1(Memory<byte> decryptedData, bool jp = false) : base(EnsurePartySize(decryptedData), jp) { }
|
||||
|
||||
public PK1(ReadOnlySpan<byte> data, ReadOnlySpan<byte> ot, ReadOnlySpan<byte> nick)
|
||||
: this(ot.Length == StringLengthJapanese)
|
||||
|
|
@ -27,11 +27,13 @@ public PK1(ReadOnlySpan<byte> data, ReadOnlySpan<byte> ot, ReadOnlySpan<byte> ni
|
|||
nick.CopyTo(NicknameTrash);
|
||||
}
|
||||
|
||||
private static byte[] EnsurePartySize(byte[] data)
|
||||
private static Memory<byte> EnsurePartySize(Memory<byte> data)
|
||||
{
|
||||
if (data.Length != PokeCrypto.SIZE_1PARTY)
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_1PARTY);
|
||||
return data;
|
||||
if (data.Length == PokeCrypto.SIZE_1PARTY)
|
||||
return data;
|
||||
var result = new byte[PokeCrypto.SIZE_1PARTY];
|
||||
data.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override PK1 Clone()
|
||||
|
|
@ -42,7 +44,13 @@ public override PK1 Clone()
|
|||
return clone;
|
||||
}
|
||||
|
||||
protected override byte[] Encrypt() => PokeList1.WrapSingle(this);
|
||||
// We (PKHeX) internally manage as single-entry lists in temp buffers.
|
||||
public override int WriteDecryptedDataStored(Span<byte> destination) => PokeList1.WrapSingle(this, destination);
|
||||
public override void WriteEncryptedDataStored(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteDecryptedDataParty(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteEncryptedDataParty(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteDecryptedDataParty(Span<byte> stored, Span<byte> party) => WriteDecryptedDataStored(stored);
|
||||
public override void WriteEncryptedDataParty(Span<byte> stored, Span<byte> party) => WriteDecryptedDataStored(stored);
|
||||
|
||||
#region Stored Attributes
|
||||
public byte SpeciesInternal { get => Data[0]; set => Data[0] = value; } // raw access
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@ public sealed class PK2 : GBPKML, ICaughtData2
|
|||
|
||||
public override bool Valid => Species <= Legal.MaxSpeciesID_2;
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_2PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_2STORED;
|
||||
public override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST;
|
||||
public override int SIZE_PARTY => SIZE_STORED;
|
||||
public override bool Korean => !Japanese && OriginalTrainerTrash[0] <= 0xB;
|
||||
|
||||
public override EntityContext Context => EntityContext.Gen2;
|
||||
|
||||
public PK2(bool jp = false) : base(PokeCrypto.SIZE_2PARTY, jp) { }
|
||||
public PK2(byte[] decryptedData, bool jp = false) : base(EnsurePartySize(decryptedData), jp) { }
|
||||
public PK2(Memory<byte> decryptedData, bool jp = false) : base(EnsurePartySize(decryptedData), jp) { }
|
||||
|
||||
public PK2(ReadOnlySpan<byte> data, ReadOnlySpan<byte> ot, ReadOnlySpan<byte> nick)
|
||||
: this(ot.Length == StringLengthJapanese)
|
||||
|
|
@ -27,11 +27,13 @@ public PK2(ReadOnlySpan<byte> data, ReadOnlySpan<byte> ot, ReadOnlySpan<byte> ni
|
|||
nick.CopyTo(NicknameTrash);
|
||||
}
|
||||
|
||||
private static byte[] EnsurePartySize(byte[] data)
|
||||
private static Memory<byte> EnsurePartySize(Memory<byte> data)
|
||||
{
|
||||
if (data.Length != PokeCrypto.SIZE_2PARTY)
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_2PARTY);
|
||||
return data;
|
||||
if (data.Length == PokeCrypto.SIZE_2PARTY)
|
||||
return data;
|
||||
var result = new byte[PokeCrypto.SIZE_2PARTY];
|
||||
data.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override PK2 Clone()
|
||||
|
|
@ -42,7 +44,14 @@ public override PK2 Clone()
|
|||
return clone;
|
||||
}
|
||||
|
||||
protected override byte[] Encrypt() => PokeList2.WrapSingle(this);
|
||||
|
||||
// We (PKHeX) internally manage as single-entry lists in temp buffers.
|
||||
public override int WriteDecryptedDataStored(Span<byte> destination) => PokeList2.WrapSingle(this, destination);
|
||||
public override void WriteEncryptedDataStored(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteDecryptedDataParty(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteEncryptedDataParty(Span<byte> destination) => WriteDecryptedDataStored(destination);
|
||||
public override void WriteDecryptedDataParty(Span<byte> stored, Span<byte> party) => WriteDecryptedDataStored(stored);
|
||||
public override void WriteEncryptedDataParty(Span<byte> stored, Span<byte> party) => WriteDecryptedDataStored(stored);
|
||||
|
||||
#region Stored Attributes
|
||||
public override ushort Species { get => Data[0]; set => Data[0] = (byte)value; }
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ public sealed class PK3 : G3PKM, ISanityChecksum
|
|||
|
||||
public PK3() : base(PokeCrypto.SIZE_3PARTY) { }
|
||||
public PK3(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt3(stored);
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted3(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted3(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_3PARTY)
|
||||
return data;
|
||||
|
||||
|
|
@ -199,12 +201,6 @@ public override bool IsEgg
|
|||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data[0x62..]); set => WriteUInt16LittleEndian(Data[0x62..], (ushort)value); }
|
||||
#endregion
|
||||
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray3(Data);
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[0x20..PokeCrypto.SIZE_3STORED]);
|
||||
|
||||
public override void RefreshChecksum()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public sealed class PK4 : G4PKM
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted45(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted45(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_4PARTY)
|
||||
return data;
|
||||
|
||||
|
|
@ -292,11 +292,6 @@ public override ushort MetLocationDP
|
|||
#endregion
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray45(Data);
|
||||
}
|
||||
|
||||
public BK4 ConvertToBK4()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,13 +22,15 @@ public sealed class PK5 : PKM, ISanityChecksum,
|
|||
public override int SIZE_STORED => PokeCrypto.SIZE_5STORED;
|
||||
public override EntityContext Context => EntityContext.Gen5;
|
||||
public override PersonalInfo5B2W2 PersonalInfo => PersonalTable.B2W2.GetFormEntry(Species, Form);
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt45(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
public PK5() : base(PokeCrypto.SIZE_5PARTY) { }
|
||||
public PK5(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted45(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted45(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_5PARTY)
|
||||
return data;
|
||||
|
||||
|
|
@ -307,11 +309,6 @@ public override string Nickname
|
|||
public override int MaxStringLengthNickname => 10;
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray45(Data);
|
||||
}
|
||||
|
||||
// Synthetic Trading Logic
|
||||
public bool BelongsTo(ITrainerInfo tr)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted67(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted67(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_6PARTY)
|
||||
return data;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public sealed class PK7 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetC
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted67(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted67(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_6PARTY)
|
||||
return data;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public sealed class PK9 : PKM, ISanityChecksum, ITeraType, ITechRecord, IObedien
|
|||
public IPermitRecord Permit => PersonalInfo;
|
||||
public override EntityContext Context => EntityContext.Gen9;
|
||||
|
||||
public PK9() : base(PokeCrypto.SIZE_9PARTY)
|
||||
public PK9() : base(PokeCrypto.SIZE_8PARTY)
|
||||
{
|
||||
AffixedRibbon = PKHeX.Core.AffixedRibbon.None;
|
||||
TeraTypeOverride = (MoveType)TeraTypeUtil.OverrideNone;
|
||||
|
|
@ -34,16 +34,18 @@ public PK9() : base(PokeCrypto.SIZE_9PARTY)
|
|||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted9(ref data);
|
||||
if (data.Length >= PokeCrypto.SIZE_9PARTY)
|
||||
PokeCrypto.DecryptIfEncrypted8(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_8PARTY)
|
||||
return data;
|
||||
|
||||
var result = new byte[PokeCrypto.SIZE_9PARTY];
|
||||
var result = new byte[PokeCrypto.SIZE_8PARTY];
|
||||
data.Span.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[8..PokeCrypto.SIZE_9STORED]);
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[8..PokeCrypto.SIZE_8STORED]);
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt8(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
// Simple Generated Attributes
|
||||
public override byte CurrentFriendship
|
||||
|
|
@ -52,8 +54,8 @@ public override byte CurrentFriendship
|
|||
set { if (CurrentHandler == 0) OriginalTrainerFriendship = value; else HandlingTrainerFriendship = value; }
|
||||
}
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_9PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_9STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
|
||||
public override bool ChecksumValid => CalculateChecksum() == Checksum;
|
||||
public override void RefreshChecksum() => Checksum = CalculateChecksum();
|
||||
|
|
@ -81,11 +83,6 @@ public override byte CurrentFriendship
|
|||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInit0(EncryptionConstant, IV32);
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray9(Data);
|
||||
}
|
||||
|
||||
public void FixRelearn()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
|
|||
protected PKM(Memory<byte> data) => Raw = data;
|
||||
protected PKM([ConstantExpected] int size) => Raw = new byte[size];
|
||||
|
||||
public virtual byte[] EncryptedPartyData => Encrypt().AsSpan()[..SIZE_PARTY].ToArray();
|
||||
public virtual byte[] EncryptedBoxData => Encrypt().AsSpan()[..SIZE_STORED].ToArray();
|
||||
public virtual byte[] DecryptedPartyData => Write()[..SIZE_PARTY].ToArray();
|
||||
public virtual byte[] DecryptedBoxData => Write()[..SIZE_STORED].ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Rough indication if the data is junk or not.
|
||||
/// </summary>
|
||||
|
|
@ -49,17 +44,61 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
|
|||
/// </summary>
|
||||
public virtual void PrepareNickname() { }
|
||||
|
||||
protected abstract byte[] Encrypt();
|
||||
public abstract EntityContext Context { get; }
|
||||
public byte Format => Context.Generation;
|
||||
public TrainerIDFormat TrainerIDDisplayFormat => this.GetTrainerIDFormat();
|
||||
|
||||
private Span<byte> Write()
|
||||
/// <summary> Writes the entity data to a sequential (stored only, no party stats) buffer destination. </summary>
|
||||
public virtual int WriteDecryptedDataStored(Span<byte> destination)
|
||||
{
|
||||
RefreshChecksum();
|
||||
return Data;
|
||||
int length = SIZE_STORED;
|
||||
Data[..length].CopyTo(destination);
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary> Writes the entity data to a sequential (stored, party) buffer destination. </summary>
|
||||
public virtual void WriteDecryptedDataParty(Span<byte> destination)
|
||||
{
|
||||
var stored = destination[..SIZE_STORED];
|
||||
var party = destination[SIZE_STORED..SIZE_PARTY];
|
||||
WriteDecryptedDataParty(stored, party);
|
||||
}
|
||||
|
||||
/// <summary> Writes the entity data to a separate (stored, party) buffer destination. </summary>
|
||||
public virtual void WriteDecryptedDataParty(Span<byte> stored, Span<byte> party)
|
||||
{
|
||||
WriteDecryptedDataStored(stored);
|
||||
Data[SIZE_STORED..SIZE_PARTY].CopyTo(party);
|
||||
}
|
||||
|
||||
/// <summary> Writes the entity data to a sequential (stored only, no party stats) buffer destination and encrypts to the at-rest state. </summary>
|
||||
public virtual void WriteEncryptedDataStored(Span<byte> destination)
|
||||
{
|
||||
var stored = destination[..SIZE_STORED];
|
||||
WriteDecryptedDataStored(stored);
|
||||
EncryptStored(stored);
|
||||
}
|
||||
|
||||
/// <summary> Writes the entity data to a sequential (stored, party) buffer destination and encrypts to the at-rest state. </summary>
|
||||
public virtual void WriteEncryptedDataParty(Span<byte> destination)
|
||||
{
|
||||
var stored = destination[..SIZE_STORED];
|
||||
var party = destination[SIZE_STORED..SIZE_PARTY];
|
||||
WriteEncryptedDataParty(stored, party);
|
||||
}
|
||||
|
||||
/// <summary> Writes the entity data to a separate (stored, party) buffer destination and encrypts to the at-rest state. </summary>
|
||||
public virtual void WriteEncryptedDataParty(Span<byte> stored, Span<byte> party)
|
||||
{
|
||||
WriteDecryptedDataParty(stored, party);
|
||||
EncryptStored(stored);
|
||||
EncryptParty(party);
|
||||
}
|
||||
|
||||
protected abstract void EncryptStored(Span<byte> stored);
|
||||
protected abstract void EncryptParty(Span<byte> party);
|
||||
|
||||
// Surface Properties
|
||||
public abstract ushort Species { get; set; }
|
||||
public abstract string Nickname { get; set; }
|
||||
|
|
@ -1163,4 +1202,23 @@ public void ClearInvalidMoves()
|
|||
5 => IV_SPD,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index), index, "IV index must be between 0 and 5."),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current <see cref="PKM"/> has the same stored data as another <see cref="PKM"/>. This is used to check if a PKM has been modified from its original imported state.
|
||||
/// </summary>
|
||||
public virtual bool EqualsStored(PKM pk)
|
||||
{
|
||||
// Generally, the objects should be of the same derived type. Don't bother checking that explicitly.
|
||||
if (pk.PID != PID)
|
||||
return false;
|
||||
|
||||
var stored = pk.Data;
|
||||
if (stored.Length >= pk.SIZE_STORED)
|
||||
stored = stored[..SIZE_STORED];
|
||||
var self = Data;
|
||||
if (self.Length >= SIZE_STORED)
|
||||
self = self[..SIZE_STORED];
|
||||
|
||||
return stored.SequenceEqual(self);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public sealed class RK4 : G4PKM
|
|||
private static Memory<byte> Decrypt(Memory<byte> data)
|
||||
{
|
||||
data = data[..PokeCrypto.SIZE_4RSTORED];
|
||||
PokeCrypto.DecryptIfEncrypted45(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted45(data.Span);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -327,17 +327,6 @@ public override string HandlingTrainerName
|
|||
#endregion
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
|
||||
byte[] data = Data.ToArray();
|
||||
byte[] pkData = data[..PokeCrypto.SIZE_4STORED];
|
||||
pkData = PokeCrypto.EncryptArray45(pkData);
|
||||
pkData.CopyTo(data, 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
public PK4 ConvertToPK4()
|
||||
{
|
||||
byte[] data = Data[..PokeCrypto.SIZE_4STORED].ToArray();
|
||||
|
|
@ -358,4 +347,7 @@ public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
|
|||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytesUTF16.GetStringLength(data, StringConverter4.Terminator);
|
||||
public override int GetBytesPerChar() => 2;
|
||||
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt45(stored[..SIZE_STORED]);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party[..(SIZE_STORED-SIZE_PARTY)], PID);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@ public override SK2 Clone() => new(Data.ToArray(), Japanese)
|
|||
IsEgg = IsEgg,
|
||||
};
|
||||
|
||||
protected override byte[] Encrypt() => Data.ToArray();
|
||||
|
||||
#region Stored Attributes
|
||||
public override ushort Species { get => Data[0]; set => Data[0] = (byte)value; }
|
||||
public override int SpriteItem => ItemConverter.GetItemFuture2((byte)HeldItem);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public abstract class G4PKM : PKM, IHandlerUpdate,
|
|||
{
|
||||
protected G4PKM(Memory<byte> data) : base(data) { }
|
||||
protected G4PKM([ConstantExpected] int size) : base(size) { }
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt45(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
// Maximums
|
||||
public sealed override ushort MaxMoveID => Legal.MaxMoveID_4;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ public abstract class G6PKM : PKM, ISanityChecksum, IHandlerUpdate
|
|||
public override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
protected G6PKM(Memory<byte> data) : base(data) { }
|
||||
protected G6PKM([ConstantExpected] int size) : base(size) { }
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt67(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
// Trash Bytes
|
||||
public sealed override Span<byte> NicknameTrash => Data.Slice(0x40, 26);
|
||||
|
|
@ -48,11 +50,6 @@ public byte OppositeFriendship
|
|||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInit0(EncryptionConstant, IV32);
|
||||
|
||||
// Methods
|
||||
protected sealed override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray6(Data);
|
||||
}
|
||||
|
||||
// General User-error Fixes
|
||||
public void FixRelearn()
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ public abstract class G8PKM : PKM, ISanityChecksum,
|
|||
{
|
||||
protected G8PKM() : base(PokeCrypto.SIZE_8PARTY) { }
|
||||
protected G8PKM(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
protected override void EncryptStored(Span<byte> stored) => PokeCrypto.Encrypt8(stored);
|
||||
protected override void EncryptParty(Span<byte> party) => PokeCrypto.CryptArray(party, PID);
|
||||
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted8(ref data);
|
||||
PokeCrypto.DecryptIfEncrypted8(data.Span);
|
||||
if (data.Length >= PokeCrypto.SIZE_8PARTY)
|
||||
return data;
|
||||
|
||||
|
|
@ -62,11 +64,6 @@ public override byte CurrentFriendship
|
|||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInit0(EncryptionConstant, IV32);
|
||||
|
||||
// Methods
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
RefreshChecksum();
|
||||
return PokeCrypto.EncryptArray8(Data);
|
||||
}
|
||||
|
||||
public void FixRelearn()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,10 +23,8 @@ public abstract class GBPKM : PKM
|
|||
protected GBPKM([ConstantExpected] int size) : base(size) { }
|
||||
protected GBPKM(Memory<byte> data) : base(data) { }
|
||||
|
||||
public sealed override byte[] EncryptedPartyData => Encrypt();
|
||||
public sealed override byte[] EncryptedBoxData => Encrypt();
|
||||
public sealed override byte[] DecryptedBoxData => Encrypt();
|
||||
public sealed override byte[] DecryptedPartyData => Encrypt();
|
||||
protected override void EncryptStored(Span<byte> stored) { }
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
public override bool Valid { get => true; set { } }
|
||||
public sealed override void RefreshChecksum() { }
|
||||
|
|
|
|||
|
|
@ -91,4 +91,20 @@ private void SetStringKeepTerminatorStyle(ReadOnlySpan<char> value, Span<byte> e
|
|||
var option = zeroed ? StringConverterOption.ClearZero : StringConverterOption.Clear50;
|
||||
SetString(exist, value, value.Length, option);
|
||||
}
|
||||
|
||||
public override bool EqualsStored(PKM pk)
|
||||
{
|
||||
var storedSize = Format == 1 ? PokeCrypto.SIZE_1STORED : PokeCrypto.SIZE_2STORED;
|
||||
var self = Data[..storedSize];
|
||||
var other = pk.Data[..storedSize];
|
||||
if (!self.SequenceEqual(other))
|
||||
return false;
|
||||
|
||||
// Compare string buffers as well, since they are stored separately in Gen 1 & 2 formats.
|
||||
if (!NicknameTrash.SequenceEqual(pk.NicknameTrash))
|
||||
return false;
|
||||
if (!OriginalTrainerTrash.SequenceEqual(pk.OriginalTrainerTrash))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ public static int CountPresent(ReadOnlySpan<byte> input, int capacity, int lerp
|
|||
|
||||
private static bool IsJapaneseList(int length) => length == PokeCrypto.SIZE_1JLIST;
|
||||
private static bool IsJapaneseString(int length) => length == GBPKML.StringLengthJapanese;
|
||||
public static int GetListLength(int capacity, int sizeBody, int stringLength) => 1 + (capacity + 1) + (sizeBody * capacity) + (stringLength * capacity * 2);
|
||||
public static int GetListLength(int capacity, bool jp, bool party) => 1 + (capacity + 1) + (GetBodyLength(party) * capacity) + (GetStringLength(jp) * capacity * 2);
|
||||
public static int GetListLengthSingle(bool jp) => jp ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
||||
private static int GetBodyLength(bool party) => party ? PokeCrypto.SIZE_1PARTY : PokeCrypto.SIZE_1STORED;
|
||||
private static int GetStringLength(bool jp) => jp ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan;
|
||||
|
|
@ -111,6 +113,9 @@ public static PK1 ReadFromSingle(ReadOnlySpan<byte> input)
|
|||
/// <param name="index">Entity index to read</param>
|
||||
public static PK1 ReadFromList(ReadOnlySpan<byte> input, int stringLength, int capacity = 1, bool isParty = true, int index = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)capacity);
|
||||
|
||||
var start = 1 + (capacity + 1);
|
||||
int sizeBody = GetBodyLength(isParty);
|
||||
|
||||
|
|
@ -133,12 +138,15 @@ public static PK1 ReadFromList(ReadOnlySpan<byte> input, int stringLength, int c
|
|||
/// <param name="capacity">Count of slots allowed in the list</param>
|
||||
/// <param name="isParty">List stores party stats for each entity</param>
|
||||
/// <param name="index">Entity index to write</param>
|
||||
public static void WriteToList(Span<byte> output, PK1 pk, int capacity = 1, bool isParty = true, int index = 0)
|
||||
public static int WriteToList(Span<byte> output, PK1 pk, int capacity = 1, bool isParty = true, int index = 0)
|
||||
{
|
||||
var start = 1 + (capacity + 1);
|
||||
var sizeBody = GetBodyLength(isParty);
|
||||
var stringLength = pk.OriginalTrainerTrash.Length;
|
||||
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)capacity);
|
||||
|
||||
var ofsBody = start + (sizeBody * index);
|
||||
var ofsStr1 = start + (sizeBody * capacity) + (stringLength * index);
|
||||
var ofsStr2 = ofsStr1 + (capacity * stringLength);
|
||||
|
|
@ -154,6 +162,9 @@ public static void WriteToList(Span<byte> output, PK1 pk, int capacity = 1, bool
|
|||
output[1 + index] = GetHeaderIdentifierMark(pk);
|
||||
output[0] = (byte)CountPresent(output, capacity);
|
||||
output[1 + capacity] = SlotEmpty; // cap off the list
|
||||
|
||||
// indicate the byte length of the list for the given parameters
|
||||
return GetListLength(capacity, sizeBody, stringLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -166,6 +177,8 @@ public static void WriteToList(Span<byte> output, PK1 pk, int capacity = 1, bool
|
|||
/// <param name="isParty">List stores party stats for each entity</param>
|
||||
public static void Unpack(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, int capacity, bool isParty)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
|
||||
var lengthBody = GetBodyLength(isParty);
|
||||
var lengthParty = GetBodyLength(true);
|
||||
|
||||
|
|
@ -208,6 +221,8 @@ public static void Unpack(ReadOnlySpan<byte> input, Span<byte> output, int strin
|
|||
/// <param name="isDestInitialized">True if the destination list is initialized</param>
|
||||
public static bool MergeSingles(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, int capacity, bool isParty, bool isDestInitialized = true)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
|
||||
// Collect the count of set slots
|
||||
var jp = IsJapaneseString(stringLength);
|
||||
var size = GetListLengthSingle(jp);
|
||||
|
|
@ -270,7 +285,7 @@ public static byte[] WrapSingle(PK1 pk)
|
|||
/// </summary>
|
||||
/// <param name="pk">Entity to wrap</param>
|
||||
/// <param name="output">Destination to write the single-slot list</param>
|
||||
public static void WrapSingle(PK1 pk, Span<byte> output) => WriteToList(output, pk);
|
||||
public static int WrapSingle(PK1 pk, Span<byte> output) => WriteToList(output, pk);
|
||||
|
||||
public static void UnpackNOB(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, bool isParty = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ private static int CountPresent(ReadOnlySpan<byte> input, int capacity, int lerp
|
|||
|
||||
private static bool IsJapaneseList(int length) => length == PokeCrypto.SIZE_2JLIST;
|
||||
private static bool IsJapaneseString(int length) => length == GBPKML.StringLengthJapanese;
|
||||
public static int GetListLength(int capacity, int sizeBody, int stringLength) => 1 + (capacity + 1) + (sizeBody * capacity) + (stringLength * capacity * 2);
|
||||
public static int GetListLength(int capacity, bool jp, bool party) => 1 + (capacity + 1) + (GetBodyLength(party) * capacity) + (GetStringLength(jp) * capacity * 2);
|
||||
public static int GetListLengthSingle(bool jp) => jp ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST;
|
||||
private static int GetBodyLength(bool party) => party ? PokeCrypto.SIZE_2PARTY : PokeCrypto.SIZE_2STORED;
|
||||
private static int GetStringLength(bool jp) => jp ? GBPKML.StringLengthJapanese : GBPKML.StringLengthNotJapan;
|
||||
|
|
@ -115,6 +117,9 @@ public static PK2 ReadFromSingle(ReadOnlySpan<byte> input)
|
|||
/// <param name="index">Entity index to read</param>
|
||||
public static PK2 ReadFromList(ReadOnlySpan<byte> input, int stringLength, int capacity = 1, bool isParty = true, int index = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)capacity);
|
||||
|
||||
var start = 1 + (capacity + 1);
|
||||
var sizeBody = GetBodyLength(isParty);
|
||||
|
||||
|
|
@ -137,12 +142,15 @@ public static PK2 ReadFromList(ReadOnlySpan<byte> input, int stringLength, int c
|
|||
/// <param name="capacity">Count of slots allowed in the list</param>
|
||||
/// <param name="isParty">List stores party stats for each entity</param>
|
||||
/// <param name="index">Entity index to write</param>
|
||||
public static void WriteToList(Span<byte> output, PK2 pk, int capacity = 1, bool isParty = true, int index = 0)
|
||||
public static int WriteToList(Span<byte> output, PK2 pk, int capacity = 1, bool isParty = true, int index = 0)
|
||||
{
|
||||
var start = 1 + (capacity + 1);
|
||||
var sizeBody = GetBodyLength(isParty);
|
||||
var stringLength = pk.OriginalTrainerTrash.Length;
|
||||
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)capacity);
|
||||
|
||||
var ofsBody = start + (sizeBody * index);
|
||||
var ofsStr1 = start + (sizeBody * capacity) + (stringLength * index);
|
||||
var ofsStr2 = ofsStr1 + (capacity * stringLength);
|
||||
|
|
@ -158,6 +166,9 @@ public static void WriteToList(Span<byte> output, PK2 pk, int capacity = 1, bool
|
|||
output[1 + index] = GetHeaderIdentifierMark(pk);
|
||||
output[0] = (byte)CountPresent(output, capacity);
|
||||
output[1 + capacity] = SlotEmpty; // cap off the list
|
||||
|
||||
// indicate the byte length of the list for the given parameters
|
||||
return GetListLength(capacity, sizeBody, stringLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -170,6 +181,8 @@ public static void WriteToList(Span<byte> output, PK2 pk, int capacity = 1, bool
|
|||
/// <param name="isParty">List stores party stats for each entity</param>
|
||||
public static void Unpack(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, int capacity, bool isParty)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
|
||||
var lengthBody = GetBodyLength(isParty);
|
||||
var lengthParty = GetBodyLength(true);
|
||||
|
||||
|
|
@ -211,6 +224,8 @@ public static void Unpack(ReadOnlySpan<byte> input, Span<byte> output, int strin
|
|||
/// <param name="isParty">List stores party stats for each entity</param>
|
||||
public static bool MergeSingles(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, int capacity, bool isParty)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)capacity - 1, IsJapaneseString(stringLength) ? 30u : 20u);
|
||||
|
||||
// Collect the count of set slots
|
||||
var jp = IsJapaneseString(stringLength);
|
||||
var size = GetListLengthSingle(jp);
|
||||
|
|
@ -273,7 +288,7 @@ public static byte[] WrapSingle(PK2 pk)
|
|||
/// </summary>
|
||||
/// <param name="pk">Entity to wrap</param>
|
||||
/// <param name="output">Destination to write the single-slot list</param>
|
||||
public static void WrapSingle(PK2 pk, Span<byte> output) => WriteToList(output, pk);
|
||||
public static int WrapSingle(PK2 pk, Span<byte> output) => WriteToList(output, pk);
|
||||
|
||||
public static void UnpackNOB(ReadOnlySpan<byte> input, Span<byte> output, int stringLength, bool isParty = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ public static EntityFormatDetected GetFormat(ReadOnlySpan<byte> data)
|
|||
|
||||
private static EntityFormatDetected GetFormatInternal(ReadOnlySpan<byte> data) => data.Length switch
|
||||
{
|
||||
SIZE_1JLIST or SIZE_1ULIST => FormatPK1,
|
||||
SIZE_2JLIST or SIZE_2ULIST => FormatPK2,
|
||||
SIZE_1JLIST or SIZE_1ULIST => FormatPK1List,
|
||||
SIZE_2JLIST or SIZE_2ULIST => FormatPK2List,
|
||||
SIZE_1PARTY or SIZE_1STORED => FormatPK1,
|
||||
SIZE_2PARTY or SIZE_2STORED => FormatPK2,
|
||||
SIZE_2STADIUM => FormatSK2,
|
||||
SIZE_3PARTY or SIZE_3STORED => FormatPK3,
|
||||
SIZE_3CSTORED => FormatCK3,
|
||||
|
|
@ -120,8 +122,10 @@ private static bool IsFormatReally9(PK8 pk)
|
|||
|
||||
private static PKM? GetFromBytes(Memory<byte> data, EntityFormatDetected format, EntityContext prefer) => format switch
|
||||
{
|
||||
FormatPK1 => PokeList1.ReadFromSingle(data.Span),
|
||||
FormatPK2 => PokeList2.ReadFromSingle(data.Span),
|
||||
FormatPK1List => PokeList1.ReadFromSingle(data.Span),
|
||||
FormatPK2List => PokeList2.ReadFromSingle(data.Span),
|
||||
FormatPK1 => new PK1(data),
|
||||
FormatPK2 => new PK2(data),
|
||||
FormatSK2 => new SK2(data),
|
||||
FormatPK3 => new PK3(data),
|
||||
FormatCK3 => new CK3(data),
|
||||
|
|
@ -222,6 +226,8 @@ public enum EntityFormatDetected
|
|||
{
|
||||
None,
|
||||
|
||||
FormatPK1List, FormatPK2List,
|
||||
|
||||
FormatPK1,
|
||||
FormatPK2, FormatSK2,
|
||||
FormatPK3, FormatCK3, FormatXK3,
|
||||
|
|
|
|||
|
|
@ -53,10 +53,6 @@ public static class PokeCrypto
|
|||
internal const int SIZE_8APARTY = SIZE_8ASTORED + 0x10; // 0x178
|
||||
private const int SIZE_8ABLOCK = 88; // 0x58
|
||||
|
||||
internal const int SIZE_9STORED = SIZE_8STORED;
|
||||
internal const int SIZE_9PARTY = SIZE_8PARTY;
|
||||
private const int SIZE_9BLOCK = SIZE_8BLOCK;
|
||||
|
||||
private const int BlockCount = 4;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -64,40 +60,16 @@ public static class PokeCrypto
|
|||
/// </summary>
|
||||
private static ReadOnlySpan<byte> BlockPosition =>
|
||||
[
|
||||
0, 1, 2, 3,
|
||||
0, 1, 3, 2,
|
||||
0, 2, 1, 3,
|
||||
0, 3, 1, 2,
|
||||
0, 2, 3, 1,
|
||||
0, 3, 2, 1,
|
||||
1, 0, 2, 3,
|
||||
1, 0, 3, 2,
|
||||
2, 0, 1, 3,
|
||||
3, 0, 1, 2,
|
||||
2, 0, 3, 1,
|
||||
3, 0, 2, 1,
|
||||
1, 2, 0, 3,
|
||||
1, 3, 0, 2,
|
||||
2, 1, 0, 3,
|
||||
3, 1, 0, 2,
|
||||
2, 3, 0, 1,
|
||||
3, 2, 0, 1,
|
||||
1, 2, 3, 0,
|
||||
1, 3, 2, 0,
|
||||
2, 1, 3, 0,
|
||||
3, 1, 2, 0,
|
||||
2, 3, 1, 0,
|
||||
3, 2, 1, 0,
|
||||
0, 1, 2, 3, 0, 1, 3, 2, 0, 2, 1, 3, 0, 3, 1, 2,
|
||||
0, 2, 3, 1, 0, 3, 2, 1, 1, 0, 2, 3, 1, 0, 3, 2,
|
||||
2, 0, 1, 3, 3, 0, 1, 2, 2, 0, 3, 1, 3, 0, 2, 1,
|
||||
1, 2, 0, 3, 1, 3, 0, 2, 2, 1, 0, 3, 3, 1, 0, 2,
|
||||
2, 3, 0, 1, 3, 2, 0, 1, 1, 2, 3, 0, 1, 3, 2, 0,
|
||||
2, 1, 3, 0, 3, 1, 2, 0, 2, 3, 1, 0, 3, 2, 1, 0,
|
||||
|
||||
// duplicates of 0-7 to eliminate modulus
|
||||
0, 1, 2, 3,
|
||||
0, 1, 3, 2,
|
||||
0, 2, 1, 3,
|
||||
0, 3, 1, 2,
|
||||
0, 2, 3, 1,
|
||||
0, 3, 2, 1,
|
||||
1, 0, 2, 3,
|
||||
1, 0, 3, 2,
|
||||
// duplicates of 0-7 to eliminate modulus (32 => 24)
|
||||
0, 1, 2, 3, 0, 1, 3, 2, 0, 2, 1, 3, 0, 3, 1, 2,
|
||||
0, 2, 3, 1, 0, 3, 2, 1, 1, 0, 2, 3, 1, 0, 3, 2,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -105,232 +77,251 @@ public static class PokeCrypto
|
|||
/// </summary>
|
||||
private static ReadOnlySpan<byte> BlockPositionInvert =>
|
||||
[
|
||||
0, 1, 2, 4, 3, 5, 6, 7, 12, 18, 13, 19, 8, 10, 14, 20, 16, 22, 9, 11, 15, 21, 17, 23,
|
||||
0, 1, 2, 4, 3, 5, 6, 7, // duplicates of 0-7 to eliminate modulus
|
||||
00, 01, 02, 04,
|
||||
03, 05, 06, 07,
|
||||
12, 18, 13, 19,
|
||||
08, 10, 14, 20,
|
||||
16, 22, 09, 11,
|
||||
15, 21, 17, 23,
|
||||
|
||||
// duplicates of 0-7 to eliminate modulus (32 => 24)
|
||||
00, 01, 02, 04,
|
||||
03, 05, 06, 07,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles a 4-block byte array containing Pokémon data.
|
||||
/// Decrypts an 80 byte format Generation 3 Pokémon byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">Data to shuffle</param>
|
||||
/// <param name="sv">Block Shuffle order</param>
|
||||
/// <param name="blockSize">Size of shuffling chunks</param>
|
||||
/// <returns>Shuffled byte array</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static byte[] ShuffleArray(ReadOnlySpan<byte> data, uint sv, [ConstantExpected(Min = 0)] int blockSize)
|
||||
/// <param name="data">Encrypted data.</param>
|
||||
/// <returns>Decrypted data.</returns>
|
||||
public static void Decrypt3(Span<byte> data)
|
||||
{
|
||||
byte[] sdata = new byte[data.Length];
|
||||
ShuffleArray(data, sdata, sv, blockSize);
|
||||
return sdata;
|
||||
}
|
||||
uint PID = ReadUInt32LittleEndian(data);
|
||||
uint OID = ReadUInt32LittleEndian(data[4..]);
|
||||
uint seed = PID ^ OID;
|
||||
uint sv = PID % 24;
|
||||
|
||||
private static void ShuffleArray(ReadOnlySpan<byte> data, Span<byte> result, uint sv, [ConstantExpected(Min = 0)] int blockSize)
|
||||
{
|
||||
int index = (int)sv * BlockCount;
|
||||
const int start = 8;
|
||||
data[..start].CopyTo(result[..start]);
|
||||
var end = start + (blockSize * BlockCount);
|
||||
data[end..].CopyTo(result[end..]);
|
||||
for (int block = 0; block < BlockCount; block++)
|
||||
{
|
||||
var dest = result.Slice(start + (blockSize * block), blockSize);
|
||||
int ofs = BlockPosition[index + block];
|
||||
var src = data.Slice(start + (blockSize * ofs), blockSize);
|
||||
src.CopyTo(dest);
|
||||
}
|
||||
var shuffle = data[SIZE_3HEADER..SIZE_3STORED];
|
||||
CryptArray3(shuffle, seed);
|
||||
Shuffle3(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a Gen8 pk byte array.
|
||||
/// Encrypts an 80 byte format Generation 3 Pokémon byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <param name="data">Decrypted data.</param>
|
||||
/// <returns>Encrypted data.</returns>
|
||||
public static void Encrypt3(Span<byte> data)
|
||||
{
|
||||
uint PID = ReadUInt32LittleEndian(data);
|
||||
uint OID = ReadUInt32LittleEndian(data[4..]);
|
||||
uint seed = PID ^ OID;
|
||||
uint sv = PID % 24;
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
var shuffle = data[SIZE_3HEADER..SIZE_3STORED];
|
||||
Shuffle3(shuffle, sv);
|
||||
CryptArray3(shuffle, seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 136 byte array from Battle Revolution (Gen4).
|
||||
/// </summary>
|
||||
/// <param name="data">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray8(Span<byte> ekm)
|
||||
public static void Decrypt4BE(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
Debug.Assert(data.Length is SIZE_4STORED);
|
||||
uint pv = ReadUInt32BigEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
CryptPKM(ekm, pv, SIZE_8BLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_8BLOCK);
|
||||
var shuffle = data[8..SIZE_4STORED];
|
||||
// No encryption applied at rest.
|
||||
Shuffle45(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a Gen8 pk byte array.
|
||||
/// Encrypts a 136 byte array from Battle Revolution (Gen4).
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <param name="data">Decrypted Pokémon data.</param>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static void Encrypt4BE(Span<byte> data)
|
||||
{
|
||||
Debug.Assert(data.Length is SIZE_4STORED);
|
||||
uint pv = ReadUInt32BigEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
var shuffle = data[8..SIZE_4STORED];
|
||||
// No encryption applied at rest.
|
||||
Shuffle45(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 136 byte + party stat byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
public static void Decrypt45(Span<byte> data)
|
||||
{
|
||||
Debug.Assert(data.Length is SIZE_4STORED or SIZE_4PARTY or SIZE_5PARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint chk = ReadUInt16LittleEndian(data[6..]);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
var shuffle = data[8..SIZE_4STORED];
|
||||
CryptArray(shuffle, chk);
|
||||
if (data.Length > SIZE_4STORED) // Party Stats
|
||||
CryptArray(data[SIZE_4STORED..], pv);
|
||||
Shuffle45(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a 136 byte + party stat byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">Decrypted Pokémon data.</param>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray8A(Span<byte> ekm)
|
||||
public static void Encrypt45(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
Debug.Assert(data.Length is SIZE_4STORED or SIZE_4PARTY or SIZE_5PARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint chk = ReadUInt16LittleEndian(data[6..]);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
CryptPKM(ekm, pv, SIZE_8ABLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_8ABLOCK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a Gen9 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray9(Span<byte> ekm)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
CryptPKM(ekm, pv, SIZE_9BLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_9BLOCK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a Gen8 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
public static byte[] EncryptArray8(ReadOnlySpan<byte> pk)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pk);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
byte[] ekm = ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_8BLOCK);
|
||||
CryptPKM(ekm, pv, SIZE_8BLOCK);
|
||||
return ekm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a Gen8 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
public static byte[] EncryptArray8A(ReadOnlySpan<byte> pk)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pk);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
byte[] ekm = ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_8ABLOCK);
|
||||
CryptPKM(ekm, pv, SIZE_8ABLOCK);
|
||||
return ekm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a Gen9 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
public static byte[] EncryptArray9(ReadOnlySpan<byte> pk)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pk);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
byte[] ekm = ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_9BLOCK);
|
||||
CryptPKM(ekm, pv, SIZE_9BLOCK);
|
||||
return ekm;
|
||||
var shuffle = data[8..SIZE_4STORED];
|
||||
Shuffle45(shuffle, sv);
|
||||
CryptArray(shuffle, chk);
|
||||
if (data.Length > SIZE_4STORED) // Party Stats
|
||||
CryptArray(data[SIZE_4STORED..], pv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 232 byte + party stat byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <param name="data">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray6(Span<byte> ekm)
|
||||
public static void Decrypt67(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
Debug.Assert(data.Length is SIZE_6STORED or SIZE_6PARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
CryptPKM(ekm, pv, SIZE_6BLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_6BLOCK);
|
||||
var shuffle = data[8..SIZE_6STORED];
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_6STORED) // Party Stats
|
||||
CryptArray(data[SIZE_6STORED..], pv);
|
||||
Shuffle67(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a 232 byte + party stat byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
public static byte[] EncryptArray6(ReadOnlySpan<byte> pk)
|
||||
/// <param name="data">Decrypted Pokémon data.</param>
|
||||
public static void Encrypt67(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pk);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
Debug.Assert(data.Length is SIZE_6STORED or SIZE_6PARTY);
|
||||
|
||||
byte[] ekm = ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_6BLOCK);
|
||||
CryptPKM(ekm, pv, SIZE_6BLOCK);
|
||||
return ekm;
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
var shuffle = data[8..SIZE_6STORED];
|
||||
Shuffle67(shuffle, sv);
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_6STORED) // Party Stats
|
||||
CryptArray(data[SIZE_6STORED..], pv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 136 byte + party stat byte array.
|
||||
/// Decrypts a Gen8 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <param name="data">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray45(Span<byte> ekm)
|
||||
public static void Decrypt8(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
uint chk = ReadUInt16LittleEndian(ekm[6..]);
|
||||
Debug.Assert(data.Length is SIZE_8STORED or SIZE_8PARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
|
||||
CryptPKM45(ekm, pv, chk, SIZE_4BLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_4BLOCK);
|
||||
var shuffle = data[8..SIZE_8STORED];
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_8STORED) // Party Stats
|
||||
CryptArray(data[SIZE_8STORED..], pv); // Party Stats
|
||||
Shuffle8(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a 136 byte + party stat byte array.
|
||||
/// Encrypts a Gen8 pk byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] EncryptArray45(ReadOnlySpan<byte> pk)
|
||||
/// <param name="data">Decrypted Pokémon data.</param>
|
||||
public static void Encrypt8(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pk);
|
||||
uint chk = ReadUInt16LittleEndian(pk[6..]);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
Debug.Assert(data.Length is SIZE_8STORED or SIZE_8PARTY);
|
||||
|
||||
byte[] ekm = ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_4BLOCK);
|
||||
CryptPKM45(ekm, pv, chk, SIZE_4BLOCK);
|
||||
return ekm;
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
var shuffle = data[8..SIZE_8STORED];
|
||||
Shuffle8(shuffle, sv);
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_8STORED) // Party Stats
|
||||
CryptArray(data[SIZE_8STORED..], pv); // Party Stats
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 136 byte + party stat byte array.
|
||||
/// Decrypts a PA8 byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <param name="data">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray4BE(ReadOnlySpan<byte> ekm)
|
||||
public static void Decrypt8A(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32BigEndian(ekm);
|
||||
Debug.Assert(data.Length is SIZE_8ASTORED or SIZE_8APARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
return ShuffleArray(ekm, sv, SIZE_4BLOCK);
|
||||
|
||||
var shuffle = data[8..SIZE_8ASTORED];
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_8ASTORED)
|
||||
CryptArray(data[SIZE_8ASTORED..], pv); // Party Stats
|
||||
Shuffle8A(shuffle, sv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a 136 byte + party stat byte array.
|
||||
/// Encrypts a PA8 byte array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted Pokémon data.</param>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] EncryptArray4BE(ReadOnlySpan<byte> pk)
|
||||
/// <param name="data">Decrypted Pokémon data.</param>
|
||||
public static void Encrypt8A(Span<byte> data)
|
||||
{
|
||||
uint pv = ReadUInt32BigEndian(pk);
|
||||
Debug.Assert(data.Length is SIZE_8ASTORED or SIZE_8APARTY);
|
||||
|
||||
uint pv = ReadUInt32LittleEndian(data);
|
||||
uint sv = (pv >> 13) & 31;
|
||||
return ShuffleArray(pk, BlockPositionInvert[(int)sv], SIZE_4BLOCK);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CryptPKM(Span<byte> data, uint pv, [ConstantExpected(Min = 0)] int blockSize)
|
||||
{
|
||||
const int start = 8;
|
||||
int end = (BlockCount * blockSize) + start;
|
||||
CryptArray(data[start..end], pv); // Blocks
|
||||
if (data.Length > end)
|
||||
CryptArray(data[end..], pv); // Party Stats
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CryptPKM45(Span<byte> data, uint pv, uint chk, [ConstantExpected(Min = 0)] int blockSize)
|
||||
{
|
||||
const int start = 8;
|
||||
int end = (BlockCount * blockSize) + start;
|
||||
CryptArray(data[start..end], chk); // Blocks
|
||||
if (data.Length > end)
|
||||
CryptArray(data[end..], pv); // Party Stats
|
||||
sv = BlockPositionInvert[(int)sv];
|
||||
|
||||
var shuffle = data[8..SIZE_8ASTORED];
|
||||
Shuffle8A(shuffle, sv);
|
||||
CryptArray(shuffle, pv);
|
||||
if (data.Length > SIZE_8ASTORED) // Party Stats
|
||||
CryptArray(data[SIZE_8ASTORED..], pv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts or Decrypts the input <see cref="data"/> using the provided <see cref="seed"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">The byte array to be encrypted or decrypted. Must be a multiple of 2 bytes in length.</param>
|
||||
/// <param name="seed">
|
||||
/// The seed used for encryption or decryption.
|
||||
/// For Gen3 entities, this is the XOR of PID and OID.
|
||||
/// For Gen4/5 entities, this is the checksum for the main data, and the PV for the party stats encryption as well.
|
||||
/// For Gen6+ entities, this is the Personality Value (PV) used for both the main data and party stats encryption.
|
||||
/// </param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CryptArray(Span<byte> data, uint seed)
|
||||
{
|
||||
|
|
@ -344,141 +335,149 @@ public static void CryptArray(Span<byte> data, uint seed)
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts an 80 byte format Generation 3 Pokémon byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted data.</param>
|
||||
/// <returns>Decrypted data.</returns>
|
||||
public static byte[] DecryptArray3(Span<byte> ekm)
|
||||
{
|
||||
Debug.Assert(ekm.Length is SIZE_3PARTY or SIZE_3STORED);
|
||||
|
||||
uint PID = ReadUInt32LittleEndian(ekm);
|
||||
uint OID = ReadUInt32LittleEndian(ekm[4..]);
|
||||
uint seed = PID ^ OID;
|
||||
CryptArray3(ekm, seed);
|
||||
return ShuffleArray3(ekm, PID % 24);
|
||||
}
|
||||
|
||||
private static void CryptArray3(Span<byte> ekm, uint seed)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CryptArray3(Span<byte> data, uint seed)
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
seed = ReverseEndianness(seed);
|
||||
var toEncrypt = ekm[SIZE_3HEADER..SIZE_3STORED];
|
||||
foreach (ref var u32 in MemoryMarshal.Cast<byte, uint>(toEncrypt))
|
||||
foreach (ref var u32 in MemoryMarshal.Cast<byte, uint>(data))
|
||||
u32 ^= seed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles an 80 byte format Generation 3 Pokémon byte array.
|
||||
/// </summary>
|
||||
/// <param name="data">Un-shuffled data.</param>
|
||||
/// <param name="sv">Block order shuffle value</param>
|
||||
/// <returns>Un-shuffled data.</returns>
|
||||
private static byte[] ShuffleArray3(ReadOnlySpan<byte> data, uint sv)
|
||||
private static void Shuffle3(Span<byte> data, uint sv) => Shuffle<uint>(data, sv, SIZE_3BLOCK);
|
||||
private static void Shuffle45(Span<byte> data, uint sv) => Shuffle<ulong>(data, sv, SIZE_4BLOCK);
|
||||
private static void Shuffle67(Span<byte> data, uint sv) => Shuffle<ulong>(data, sv, SIZE_6BLOCK);
|
||||
private static void Shuffle8(Span<byte> data, uint sv) => Shuffle<ulong>(data, sv, SIZE_8BLOCK);
|
||||
private static void Shuffle8A(Span<byte> data, uint sv) => Shuffle<ulong>(data, sv, SIZE_8ABLOCK);
|
||||
|
||||
private static void Shuffle<T>(Span<byte> data, uint sv, [ConstantExpected(Min = 0)] int blockSizeBytes) where T : unmanaged
|
||||
{
|
||||
byte[] sdata = new byte[data.Length];
|
||||
ShuffleArray3(data, sdata, sv);
|
||||
return sdata;
|
||||
if (sv == 0)
|
||||
return;
|
||||
|
||||
var size = Unsafe.SizeOf<T>(); // JIT constant-folded
|
||||
var count = blockSizeBytes / size; // number of T-elements per block
|
||||
|
||||
Span<byte> perm = stackalloc byte[BlockCount];
|
||||
Span<byte> slotOf = stackalloc byte[BlockCount];
|
||||
|
||||
// build current layout and reverse map (identity)
|
||||
for (byte s = 0; s < BlockCount; s++)
|
||||
perm[s] = slotOf[s] = s;
|
||||
|
||||
var shuffle = BlockPosition.Slice((int)sv * BlockCount, BlockCount);
|
||||
|
||||
// Perform shuffle, let JIT unroll.
|
||||
var u = MemoryMarshal.Cast<byte, T>(data);
|
||||
for (byte i = 0; i < BlockCount - 1; i++)
|
||||
{
|
||||
var desired = shuffle[i];
|
||||
var j = slotOf[desired]; // O(1)
|
||||
if (j == i)
|
||||
continue;
|
||||
SwapBlocks(u, i * count, j * count, count);
|
||||
|
||||
// reflect permutation, update reverse map
|
||||
// no need to update processed indexes - they won't be used in future loops.
|
||||
// we don't care to book-keep the full state of the perm, just the location of unprocessed blocks
|
||||
var blockAtI = perm[i];
|
||||
// perm[i] = desired;
|
||||
perm[j] = blockAtI;
|
||||
// slotOf[desired] = i;
|
||||
slotOf[blockAtI] = j;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShuffleArray3(ReadOnlySpan<byte> data, Span<byte> result, uint sv)
|
||||
private static void SwapBlocks<T>(Span<T> u, int a, int b, int count) where T : unmanaged
|
||||
{
|
||||
int index = (int)sv * BlockCount;
|
||||
data[..SIZE_3HEADER].CopyTo(result[..SIZE_3HEADER]);
|
||||
data[SIZE_3STORED..].CopyTo(result[SIZE_3STORED..]);
|
||||
for (int block = 0; block < BlockCount; block++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var dest = result.Slice(SIZE_3HEADER + (SIZE_3BLOCK * block), SIZE_3BLOCK);
|
||||
int ofs = BlockPosition[index + block];
|
||||
var src = data.Slice(SIZE_3HEADER + (SIZE_3BLOCK * ofs), SIZE_3BLOCK);
|
||||
src.CopyTo(dest);
|
||||
ref var ra = ref u[a + i];
|
||||
ref var rb = ref u[b + i];
|
||||
(ra, rb) = (rb, ra);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts an 80 byte format Generation 3 Pokémon byte array.
|
||||
/// Checks if the Gen3 format entity is encrypted, when the checksum of the data does not match the expected value.
|
||||
/// </summary>
|
||||
/// <param name="pk">Decrypted data.</param>
|
||||
/// <returns>Encrypted data.</returns>
|
||||
public static byte[] EncryptArray3(ReadOnlySpan<byte> pk)
|
||||
public static bool IsEncrypted3(ReadOnlySpan<byte> data)
|
||||
{
|
||||
Debug.Assert(pk.Length is SIZE_3PARTY or SIZE_3STORED);
|
||||
|
||||
uint PID = ReadUInt32LittleEndian(pk);
|
||||
uint OID = ReadUInt32LittleEndian(pk[4..]);
|
||||
uint seed = PID ^ OID;
|
||||
|
||||
byte[] ekm = ShuffleArray3(pk, BlockPositionInvert[(int)(PID % 24)]);
|
||||
CryptArray3(ekm, seed);
|
||||
return ekm;
|
||||
var calc = Checksums.Add16(data[SIZE_3HEADER..SIZE_3STORED]);
|
||||
var expect = ReadUInt16LittleEndian(data[0x1C..]);
|
||||
return calc != expect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// Checks if the Gen4/5 format entity is encrypted, when the unused ribbon bits are not 0.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 3 Format encryption check which verifies the checksum</remarks>
|
||||
public static void DecryptIfEncrypted3(ref Memory<byte> pk)
|
||||
{
|
||||
ushort chk = Checksums.Add16(pk.Span.Slice(0x20, BlockCount * SIZE_3BLOCK));
|
||||
if (chk != ReadUInt16LittleEndian(pk.Span[0x1C..]))
|
||||
pk = DecryptArray3(pk.Span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 4 & 5 Format encryption check which checks for the unused bytes</remarks>
|
||||
public static void DecryptIfEncrypted45(ref Memory<byte> pk)
|
||||
{
|
||||
var span = pk.Span;
|
||||
if (IsEncrypted45(span))
|
||||
pk = DecryptArray45(span);
|
||||
}
|
||||
|
||||
public static bool IsEncrypted45(ReadOnlySpan<byte> data) => ReadUInt32LittleEndian(data[0x64..]) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// Checks if the Gen6/7 format entity is encrypted, when the final terminators of the text strings are not 0.
|
||||
/// </summary>
|
||||
/// <remarks> Checks Nickname and Original Trainer. </remarks>
|
||||
private static bool IsEncrypted67(ReadOnlySpan<byte> data) => ReadUInt16LittleEndian(data[0xC8..]) != 0 || ReadUInt16LittleEndian(data[0x58..]) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Gen8 format entity is encrypted, when the final terminators of the text strings are not 0.
|
||||
/// </summary>
|
||||
/// <remarks> Checks Nickname and Original Trainer. </remarks>
|
||||
public static bool IsEncrypted8(ReadOnlySpan<byte> data) => ReadUInt16LittleEndian(data[0x70..]) != 0 || ReadUInt16LittleEndian(data[0x110..]) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the Gen8a format entity is encrypted, when the final terminators of the text strings are not 0.
|
||||
/// </summary>
|
||||
/// <remarks> Checks Nickname and Original Trainer. </remarks>
|
||||
public static bool IsEncrypted8A(ReadOnlySpan<byte> data) => ReadUInt16LittleEndian(data[0x78..]) != 0 || ReadUInt16LittleEndian(data[0x128..]) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="data"/> if it is encrypted.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 3 Format encryption check which verifies the checksum</remarks>
|
||||
public static void DecryptIfEncrypted3(Span<byte> data)
|
||||
{
|
||||
if (IsEncrypted3(data))
|
||||
Decrypt3(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="data"/> if it is encrypted.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 4 & 5 Format encryption check which checks for the unused bytes</remarks>
|
||||
public static void DecryptIfEncrypted45(Span<byte> data)
|
||||
{
|
||||
if (IsEncrypted45(data))
|
||||
Decrypt45(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="data"/> if it is encrypted.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 6 & 7 Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted67(ref Memory<byte> pk)
|
||||
public static void DecryptIfEncrypted67(Span<byte> data)
|
||||
{
|
||||
var span = pk.Span;
|
||||
if (ReadUInt16LittleEndian(span[0xC8..]) != 0 || ReadUInt16LittleEndian(span[0x58..]) != 0)
|
||||
pk = DecryptArray6(span);
|
||||
if (IsEncrypted67(data))
|
||||
Decrypt67(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// Decrypts the input <see cref="data"/> if it is encrypted.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 8 Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted8(ref Memory<byte> pk)
|
||||
public static void DecryptIfEncrypted8(Span<byte> data)
|
||||
{
|
||||
var span = pk.Span;
|
||||
if (ReadUInt16LittleEndian(span[0x70..]) != 0 || ReadUInt16LittleEndian(span[0x110..]) != 0)
|
||||
pk = DecryptArray8(span);
|
||||
if (IsEncrypted8(data))
|
||||
Decrypt8(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// Decrypts the input <see cref="data"/> if it is encrypted.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 8 Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted8A(ref Memory<byte> pk)
|
||||
public static void DecryptIfEncrypted8A(Span<byte> data)
|
||||
{
|
||||
var span = pk.Span;
|
||||
if (ReadUInt16LittleEndian(span[0x78..]) != 0 || ReadUInt16LittleEndian(span[0x128..]) != 0)
|
||||
pk = DecryptArray8A(span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pk"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 9 Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted9(ref Memory<byte> pk)
|
||||
{
|
||||
var span = pk.Span;
|
||||
if (ReadUInt16LittleEndian(span[0x70..]) != 0 || ReadUInt16LittleEndian(span[0x110..]) != 0)
|
||||
pk = DecryptArray9(span);
|
||||
if (IsEncrypted8A(data))
|
||||
Decrypt8A(data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ public sealed class XK3 : G3PKM, IShadowCapture, ISeparateIVs, IGCRegion
|
|||
public override XK3 Clone() => new(Data.ToArray()) { Purification = Purification, IsShadow = IsShadow };
|
||||
public override void RefreshChecksum() => Valid = true;
|
||||
|
||||
protected override void EncryptStored(Span<byte> stored) { }
|
||||
protected override void EncryptParty(Span<byte> party) { }
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> OriginalTrainerTrash => Data.Slice(0x38, 22);
|
||||
public Span<byte> NicknameDisplayTrash => Data.Slice(0x4E, 22);
|
||||
|
|
@ -218,8 +221,6 @@ public override int EV_SPE
|
|||
|
||||
public bool IsShadow { get; internal set; } // determined by savedata, not written back to sav
|
||||
|
||||
protected override byte[] Encrypt() => Data.ToArray();
|
||||
|
||||
public PK3 ConvertToPK3()
|
||||
{
|
||||
var pk = ConvertTo<PK3>();
|
||||
|
|
|
|||
|
|
@ -216,8 +216,8 @@ private int GetBoxRawDataOffset(int box)
|
|||
// Configuration
|
||||
protected override SAV1 CloneInternal() => new(GetFinalData(), (LanguageID)Language, Version);
|
||||
|
||||
protected override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
||||
protected override int SIZE_PARTY => SIZE_STORED;
|
||||
public override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST;
|
||||
public override int SIZE_PARTY => SIZE_STORED;
|
||||
private int SIZE_BOX_AS_SINGLES => BoxSlotCount * SIZE_STORED;
|
||||
private int SIZE_BOX_LIST => (((StringLength * 2) + PokeCrypto.SIZE_1STORED + 1) * BoxSlotCount) + 2;
|
||||
private int SIZE_PARTY_LIST => (((StringLength * 2) + PokeCrypto.SIZE_1PARTY + 1) * 6) + 2;
|
||||
|
|
@ -472,17 +472,14 @@ public string GetBoxName(int box)
|
|||
return BoxDetailNameExtensions.GetDefaultBoxName(box);
|
||||
}
|
||||
|
||||
protected override PK1 GetPKM(byte[] data)
|
||||
protected override PK1 GetPKM(Memory<byte> data)
|
||||
{
|
||||
if (data.Length == SIZE_STORED)
|
||||
return PokeList1.ReadFromList(data, StringLength);
|
||||
return PokeList1.ReadFromList(data.Span, StringLength);
|
||||
return new(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
protected override void DecryptPKM(Span<byte> data) { }
|
||||
|
||||
// Pokédex
|
||||
protected override void SetDex(PKM pk)
|
||||
|
|
@ -525,20 +522,12 @@ private void SetDexFlag(int region, ushort species, bool value)
|
|||
SetFlag(region + ofs, bit & 7, value);
|
||||
}
|
||||
|
||||
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
|
||||
protected override void WriteSlotStored(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteSlotFormatStored(pk, data);
|
||||
}
|
||||
|
||||
public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteBoxSlot(pk, data);
|
||||
base.WriteSlotStored(pk, data);
|
||||
}
|
||||
|
||||
private const int SpawnFlagCount = 0xF0;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ public sealed class SAV1Stadium : SAV_STADIUM, IStorageCleanup
|
|||
public override PK1 BlankPKM => new(Japanese);
|
||||
private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLengthJ); // 0x2D
|
||||
private const int SIZE_PK1U = PokeCrypto.SIZE_1STORED + (2 * StringLengthU); // 0x37
|
||||
protected override int SIZE_STORED => Japanese ? SIZE_PK1J : SIZE_PK1U;
|
||||
protected override int SIZE_PARTY => Japanese ? SIZE_PK1J : SIZE_PK1U;
|
||||
public override int SIZE_STORED => Japanese ? SIZE_PK1J : SIZE_PK1U;
|
||||
public override int SIZE_PARTY => Japanese ? SIZE_PK1J : SIZE_PK1U;
|
||||
|
||||
private int ListHeaderSize => Japanese ? 0x0C : 0x10;
|
||||
private const int ListFooterSize = 6; // POKE + 2byte checksum
|
||||
|
|
@ -134,7 +134,7 @@ private void ConditionBoxes()
|
|||
var species = slice[0];
|
||||
if (species == 0) // don't bother converting from internal->national
|
||||
continue; // don't bother wiping already-empty slots.
|
||||
WriteBoxSlot(blank, slice);
|
||||
WriteSlotBox(blank, slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,34 +227,20 @@ public bool FixStoragePreWrite()
|
|||
return anyShifted;
|
||||
}
|
||||
|
||||
protected override PK1 GetPKM(byte[] data)
|
||||
protected override PK1 GetPKM(Memory<byte> data)
|
||||
{
|
||||
var inner = data[..PokeCrypto.SIZE_1STORED];
|
||||
var extra = data[PokeCrypto.SIZE_1STORED..].Span;
|
||||
var pk1 = new PK1(inner, Japanese);
|
||||
|
||||
int len = StringLength;
|
||||
var nick = data.AsSpan(PokeCrypto.SIZE_1STORED, len);
|
||||
var ot = data.AsSpan(PokeCrypto.SIZE_1STORED + len, len);
|
||||
var pk1 = new PK1(data[..PokeCrypto.SIZE_1STORED], Japanese);
|
||||
var nick = extra[..len];
|
||||
var ot = extra.Slice(len, len);
|
||||
nick.CopyTo(pk1.NicknameTrash);
|
||||
ot.CopyTo(pk1.OriginalTrainerTrash);
|
||||
return pk1;
|
||||
}
|
||||
|
||||
public override byte[] GetDataForFormatStored(PKM pk)
|
||||
{
|
||||
byte[] result = new byte[SIZE_STORED];
|
||||
var gb = (PK1)pk;
|
||||
|
||||
var data = pk.Data;
|
||||
int len = StringLength;
|
||||
data.CopyTo(result);
|
||||
gb.NicknameTrash.CopyTo(result.AsSpan(PokeCrypto.SIZE_1STORED));
|
||||
gb.OriginalTrainerTrash.CopyTo(result.AsSpan(PokeCrypto.SIZE_1STORED + len));
|
||||
return result;
|
||||
}
|
||||
|
||||
public override byte[] GetDataForFormatParty(PKM pk) => GetDataForFormatStored(pk);
|
||||
public override byte[] GetDataForParty(PKM pk) => GetDataForFormatStored(pk);
|
||||
public override byte[] GetDataForBox(PKM pk) => GetDataForFormatStored(pk);
|
||||
|
||||
public int GetTeamOffset(int team) => Japanese ? GetTeamOffsetJ(team) : GetTeamOffsetU(team);
|
||||
|
||||
private int GetTeamOffsetJ(int team)
|
||||
|
|
@ -372,20 +358,21 @@ public override SlotGroup GetTeam(int team)
|
|||
return new SlotGroup(name, members, StorageSlotType.Box);
|
||||
}
|
||||
|
||||
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteSlotFormatStored(pk, data);
|
||||
}
|
||||
// Only box data format, no list prefix.
|
||||
protected override void WriteSlotParty(PKM pk, Span<byte> data) => WriteSlotStored(pk, data);
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data) => WriteSlotStored(pk, data);
|
||||
|
||||
public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
||||
protected override void WriteSlotStored(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteBoxSlot(pk, data);
|
||||
var gb = (PK1)pk;
|
||||
gb.Stat_LevelBox = pk.CurrentLevel;
|
||||
|
||||
var self = pk.Data;
|
||||
self[..PokeCrypto.SIZE_1STORED].CopyTo(data);
|
||||
gb.NicknameTrash.CopyTo(data[PokeCrypto.SIZE_1STORED..]);
|
||||
gb.OriginalTrainerTrash.CopyTo(data[(PokeCrypto.SIZE_1STORED + StringLength)..]);
|
||||
}
|
||||
|
||||
public static bool IsStadium(ReadOnlySpan<byte> data)
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ public sealed class SAV1StadiumJ : SAV_STADIUM
|
|||
public override int MaxAbilityID => Legal.MaxAbilityID_1;
|
||||
public override int MaxItemID => Legal.MaxItemID_1;
|
||||
private const int SIZE_PK1J = PokeCrypto.SIZE_1STORED + (2 * StringLength); // 0x2D
|
||||
protected override int SIZE_STORED => SIZE_PK1J;
|
||||
protected override int SIZE_PARTY => SIZE_PK1J;
|
||||
public override int SIZE_STORED => SIZE_PK1J;
|
||||
public override int SIZE_PARTY => SIZE_PK1J;
|
||||
|
||||
public override Type PKMType => typeof(PK1);
|
||||
public override PK1 BlankPKM => new(true);
|
||||
|
|
@ -80,35 +80,20 @@ protected override void SetBoxMetadata(int box)
|
|||
// Not implemented
|
||||
}
|
||||
|
||||
protected override PK1 GetPKM(byte[] data)
|
||||
protected override PK1 GetPKM(Memory<byte> data)
|
||||
{
|
||||
var inner = data[..PokeCrypto.SIZE_1STORED];
|
||||
var extra = data[PokeCrypto.SIZE_1STORED..].Span;
|
||||
var pk1 = new PK1(inner, true);
|
||||
|
||||
const int len = StringLength;
|
||||
var nick = data.AsSpan(0x21, len);
|
||||
var ot = data.AsSpan(0x21 + len, len);
|
||||
data = data[..0x21];
|
||||
var pk1 = new PK1(data, true);
|
||||
var nick = extra[..len];
|
||||
var ot = extra.Slice(len, len);
|
||||
nick.CopyTo(pk1.NicknameTrash);
|
||||
ot.CopyTo(pk1.OriginalTrainerTrash);
|
||||
return pk1;
|
||||
}
|
||||
|
||||
public override byte[] GetDataForFormatStored(PKM pk)
|
||||
{
|
||||
byte[] result = new byte[SIZE_STORED];
|
||||
var gb = (PK1)pk;
|
||||
|
||||
var data = pk.Data;
|
||||
const int len = StringLength;
|
||||
data.CopyTo(result);
|
||||
gb.NicknameTrash.CopyTo(result.AsSpan(PokeCrypto.SIZE_1STORED));
|
||||
gb.OriginalTrainerTrash.CopyTo(result.AsSpan(PokeCrypto.SIZE_1STORED + len));
|
||||
return result;
|
||||
}
|
||||
|
||||
public override byte[] GetDataForFormatParty(PKM pk) => GetDataForFormatStored(pk);
|
||||
public override byte[] GetDataForParty(PKM pk) => GetDataForFormatStored(pk);
|
||||
public override byte[] GetDataForBox(PKM pk) => GetDataForFormatStored(pk);
|
||||
|
||||
public override int GetBoxOffset(int box) => Box + ListHeaderSize + (box * BoxSizeJ);
|
||||
public static int GetTeamOffset(int team) => 0 + (team * 2 * TeamSizeJ); // backups are after each team
|
||||
|
||||
|
|
@ -140,20 +125,12 @@ public override SlotGroup GetTeam(int team)
|
|||
return new SlotGroup(name, members, StorageSlotType.Box);
|
||||
}
|
||||
|
||||
public override void WriteSlotFormatStored(PKM pk, Span<byte> data)
|
||||
protected override void WriteSlotStored(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteSlotFormatStored(pk, data);
|
||||
}
|
||||
|
||||
public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
||||
{
|
||||
// pk that have never been boxed have yet to save the 'current level' for box indication
|
||||
// set this value at this time
|
||||
((PK1)pk).Stat_LevelBox = pk.CurrentLevel;
|
||||
base.WriteBoxSlot(pk, data);
|
||||
base.WriteSlotStored(pk, data);
|
||||
}
|
||||
|
||||
public static bool IsStadium(ReadOnlySpan<byte> data)
|
||||
|
|
|
|||
|
|
@ -242,8 +242,8 @@ private ushort GetKoreanChecksum()
|
|||
// Configuration
|
||||
protected override SAV2 CloneInternal() => new(GetFinalData(), (LanguageID)Language, Version);
|
||||
|
||||
protected override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST;
|
||||
protected override int SIZE_PARTY => SIZE_STORED;
|
||||
public override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST;
|
||||
public override int SIZE_PARTY => SIZE_STORED;
|
||||
public override PK2 BlankPKM => new(jp: Japanese);
|
||||
public override Type PKMType => typeof(PK2);
|
||||
|
||||
|
|
@ -611,17 +611,14 @@ public void SetBoxName(int box, ReadOnlySpan<char> value)
|
|||
SetString(span, deflated[..len], maxLen, StringConverterOption.Clear50);
|
||||
}
|
||||
|
||||
protected override PK2 GetPKM(byte[] data)
|
||||
protected override PK2 GetPKM(Memory<byte> data)
|
||||
{
|
||||
if (data.Length == SIZE_STORED)
|
||||
return PokeList2.ReadFromList(data, StringLength);
|
||||
return PokeList2.ReadFromList(data.Span, StringLength);
|
||||
return new(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
protected override void DecryptPKM(Span<byte> data) { }
|
||||
|
||||
// Pokédex
|
||||
protected override void SetDex(PKM pk)
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ public sealed class SAV2Stadium : SAV_STADIUM, IBoxDetailName
|
|||
|
||||
public override Type PKMType => typeof(SK2);
|
||||
public override SK2 BlankPKM => new(Japanese);
|
||||
protected override SK2 GetPKM(byte[] data) => new(data, Japanese);
|
||||
protected override SK2 GetPKM(Memory<byte> data) => new(data, Japanese);
|
||||
|
||||
private const int SIZE_SK2 = PokeCrypto.SIZE_2STADIUM; // 60
|
||||
protected override int SIZE_STORED => SIZE_SK2;
|
||||
protected override int SIZE_PARTY => SIZE_SK2;
|
||||
public override int SIZE_STORED => SIZE_SK2;
|
||||
public override int SIZE_PARTY => SIZE_SK2;
|
||||
|
||||
private const int ListHeaderSizeTeam = 0x10;
|
||||
private const int ListHeaderSizeBox = 0x20;
|
||||
|
|
|
|||
|
|
@ -177,8 +177,8 @@ public void WriteBothSaveSlots(Span<byte> data)
|
|||
SetSlotChecksums(data, 1);
|
||||
}
|
||||
|
||||
protected sealed override int SIZE_STORED => PokeCrypto.SIZE_3STORED;
|
||||
protected sealed override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY;
|
||||
public sealed override int SIZE_STORED => PokeCrypto.SIZE_3STORED;
|
||||
public sealed override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY;
|
||||
public sealed override PK3 BlankPKM => new();
|
||||
public sealed override Type PKMType => typeof(PK3);
|
||||
|
||||
|
|
@ -219,8 +219,8 @@ public void WriteBothSaveSlots(Span<byte> data)
|
|||
public sealed override int GetPartyOffset(int slot) => SIZE_PARTY * slot;
|
||||
|
||||
public sealed override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentGBA(data);
|
||||
protected sealed override PK3 GetPKM(byte[] data) => new(data);
|
||||
protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray3(data);
|
||||
protected sealed override PK3 GetPKM(Memory<byte> data) => new(data);
|
||||
protected sealed override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt3(data);
|
||||
|
||||
protected sealed override Span<byte> BoxBuffer => Storage;
|
||||
protected sealed override Span<byte> PartyBuffer => LargeBlock.PartyBuffer;
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ private byte[] GetInnerData()
|
|||
// Configuration
|
||||
protected override SAV3Colosseum CloneInternal() => new((SaveIndex, SaveCount), Container.ToArray(), false) { MemoryCard = MemoryCard };
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3CSTORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_3CSTORED; // unused
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_3CSTORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_3CSTORED; // unused
|
||||
public override CK3 BlankPKM => new();
|
||||
public override Type PKMType => typeof(CK3);
|
||||
|
||||
|
|
@ -182,14 +182,22 @@ public void SetBoxName(int box, ReadOnlySpan<char> value)
|
|||
SetString(GetBoxNameSpan(box), value, 8, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
protected override CK3 GetPKM(byte[] data)
|
||||
protected override CK3 GetPKM(Memory<byte> data)
|
||||
{
|
||||
if (data.Length != SIZE_STORED)
|
||||
Array.Resize(ref data, SIZE_STORED);
|
||||
data = EnsurePartySize(data);
|
||||
return new(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data) => data;
|
||||
private static Memory<byte> EnsurePartySize(Memory<byte> data)
|
||||
{
|
||||
if (data.Length == PokeCrypto.SIZE_3CSTORED)
|
||||
return data;
|
||||
var result = new byte[PokeCrypto.SIZE_3CSTORED];
|
||||
data.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void DecryptPKM(Span<byte> data) { }
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ public override void CopyChangesFrom(SaveFile sav)
|
|||
s.BoxBuffer.CopyTo(BoxBuffer);
|
||||
}
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3STORED + 4;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY; // unused
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_3STORED + 4; // tid-sid of depositor
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY; // unused
|
||||
public override PK3 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PK3);
|
||||
|
||||
|
|
@ -233,27 +233,14 @@ public void SetBoxName(int box, ReadOnlySpan<char> value)
|
|||
SetString(span, value, 8, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
protected override PK3 GetPKM(byte[] data)
|
||||
{
|
||||
if (data.Length != PokeCrypto.SIZE_3STORED)
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_3STORED);
|
||||
return new(data);
|
||||
}
|
||||
protected override PK3 GetPKM(Memory<byte> data) => new(data);
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data)
|
||||
{
|
||||
if (data.Length != PokeCrypto.SIZE_3STORED)
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_3STORED);
|
||||
return PokeCrypto.DecryptArray3(data);
|
||||
}
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt3(data);
|
||||
|
||||
protected override void SetDex(PKM pk) { /* No Pokédex for this game, do nothing */ }
|
||||
|
||||
public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data)
|
||||
{
|
||||
base.WriteBoxSlot(pk, data);
|
||||
WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED)..], pk.TID16);
|
||||
WriteUInt16LittleEndian(data[(PokeCrypto.SIZE_3STORED + 2)..], pk.SID16);
|
||||
base.WriteSlotBox(pk, data);
|
||||
WriteUInt32LittleEndian(data[PokeCrypto.SIZE_3STORED..], pk.ID32); // assume from OT
|
||||
}
|
||||
|
||||
public override string GetString(ReadOnlySpan<byte> data)
|
||||
|
|
|
|||
|
|
@ -140,8 +140,8 @@ private byte[] GetInnerData()
|
|||
// Configuration
|
||||
protected override SAV3XD CloneInternal() => new((SaveIndex, SaveCount), Container.ToArray(), false) { MemoryCard = MemoryCard };
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3XSTORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_3XSTORED; // unused
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_3XSTORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_3XSTORED; // unused
|
||||
public override XK3 BlankPKM => new();
|
||||
public override Type PKMType => typeof(XK3);
|
||||
|
||||
|
|
@ -264,14 +264,8 @@ public void SetBoxName(int box, ReadOnlySpan<char> value)
|
|||
SetString(Data.Slice(GetBoxInfoOffset(box), 20), value, 8, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
protected override XK3 GetPKM(byte[] data)
|
||||
{
|
||||
if (data.Length != SIZE_STORED)
|
||||
Array.Resize(ref data, SIZE_STORED);
|
||||
return new(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data) => data;
|
||||
protected override XK3 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) { }
|
||||
public override XK3 GetPartySlot(ReadOnlySpan<byte> data) => GetStoredSlot(data);
|
||||
|
||||
public override XK3 GetStoredSlot(ReadOnlySpan<byte> data)
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ public sealed override void CopyChangesFrom(SaveFile sav)
|
|||
SetData(Storage, s4.Storage);
|
||||
}
|
||||
|
||||
protected sealed override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
protected sealed override int SIZE_PARTY => PokeCrypto.SIZE_4PARTY;
|
||||
public sealed override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
public sealed override int SIZE_PARTY => PokeCrypto.SIZE_4PARTY;
|
||||
public sealed override PK4 BlankPKM => new();
|
||||
public sealed override Type PKMType => typeof(PK4);
|
||||
|
||||
|
|
@ -370,8 +370,8 @@ public int Region
|
|||
}
|
||||
#endregion
|
||||
|
||||
protected sealed override PK4 GetPKM(byte[] data) => new(data);
|
||||
protected sealed override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data);
|
||||
protected sealed override PK4 GetPKM(Memory<byte> data) => new(data);
|
||||
protected sealed override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt45(data);
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
@ -805,7 +805,7 @@ public virtual void SetMysteryGift(int index, PGT pgt)
|
|||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if (pgt.Data.Length != PGT.Size)
|
||||
throw new InvalidCastException(nameof(pgt));
|
||||
pgt.VerifyPKEncryption();
|
||||
pgt.VerifyGiftEncryption();
|
||||
SAV.SetData(GetCardSpanPGT(index), pgt.Data);
|
||||
}
|
||||
|
||||
|
|
@ -816,7 +816,7 @@ public virtual void SetMysteryGift(int index, PCD pcd)
|
|||
if (pcd.Data.Length != PCD.Size)
|
||||
throw new InvalidCastException(nameof(pcd));
|
||||
var gift = pcd.Gift;
|
||||
gift.VerifyPKEncryption();
|
||||
gift.VerifyGiftEncryption();
|
||||
SAV.SetData(GetCardSpanPCD(index), pcd.Data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ protected override Memory<byte> GetFinalData()
|
|||
// Configuration
|
||||
protected override SAV4BR CloneInternal() => new(this);
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_4STORED + 84;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_4STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_4STORED + 84;
|
||||
public override BK4 BlankPKM => new();
|
||||
public override Type PKMType => typeof(BK4);
|
||||
|
||||
|
|
@ -463,14 +463,8 @@ public void SetBoxName(int box, ReadOnlySpan<char> value)
|
|||
SetString(span, value, BoxNameLength / 2, StringConverterOption.ClearZero);
|
||||
}
|
||||
|
||||
protected override BK4 GetPKM(byte[] data)
|
||||
{
|
||||
if (data.Length != SIZE_STORED)
|
||||
Array.Resize(ref data, SIZE_STORED);
|
||||
return BK4.ReadUnshuffle(data);
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data) => data;
|
||||
protected override BK4 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt4BE(data);
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
@ -493,21 +487,19 @@ protected override void SetPartyValues(PKM pk, bool isParty)
|
|||
/// <returns>Where the PKM was found, or (255, 255) otherwise</returns>
|
||||
public (byte Box, byte Slot) FindSlot(PKM pk)
|
||||
{
|
||||
var party = PartyData;
|
||||
for (byte slot = 0; slot < PartyCount; slot++)
|
||||
{
|
||||
PKM other = party[slot];
|
||||
if (pk.PID == other.PID && pk.DecryptedBoxData.SequenceEqual(other.DecryptedBoxData))
|
||||
var other = GetPartySlotAtIndex(slot);
|
||||
if (pk.EqualsStored(other))
|
||||
return (0, slot);
|
||||
}
|
||||
|
||||
var boxes = BoxData;
|
||||
for (byte box = 0; box < BoxCount; box++)
|
||||
{
|
||||
for (byte slot = 0; slot < BoxSlotCount; slot++)
|
||||
{
|
||||
PKM other = boxes[(box * BoxSlotCount) + slot];
|
||||
if (pk.PID == other.PID && pk.DecryptedBoxData.SequenceEqual(other.DecryptedBoxData))
|
||||
var other = GetBoxSlotAtIndex(box, slot);
|
||||
if (pk.EqualsStored(other))
|
||||
return (++box, slot);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlagProvider37, IBoxDetailName, IBoxDetailWallpaper, IDaycareRandomState<ulong>, IDaycareStorage, IDaycareExperience, IDaycareEggState, IMysteryGiftStorageProvider
|
||||
{
|
||||
protected override PK5 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data);
|
||||
protected override PK5 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt45(data);
|
||||
|
||||
protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}";
|
||||
public override string Extension => ".sav";
|
||||
|
||||
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_BW;
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_5STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_5PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_5STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_5PARTY;
|
||||
public override PK5 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PK5);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
|
|||
protected SAV6([ConstantExpected] int size, [ConstantExpected] int biOffset) : base(size, biOffset) { }
|
||||
|
||||
// Configuration
|
||||
protected sealed override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
protected sealed override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public sealed override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
public sealed override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public sealed override PK6 BlankPKM => new();
|
||||
public sealed override Type PKMType => typeof(PK6);
|
||||
|
||||
|
|
@ -32,8 +32,8 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
|
|||
public override int MaxBallID => Legal.MaxBallID_6;
|
||||
public override GameVersion MaxGameID => Legal.MaxGameID_6; // OR
|
||||
|
||||
protected override PK6 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray6(data);
|
||||
protected override PK6 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt67(data);
|
||||
|
||||
protected int JPEG { get; set; } = int.MinValue;
|
||||
public int PSS { get; protected set; } = int.MinValue;
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ protected void ReloadBattleTeams()
|
|||
#endregion
|
||||
|
||||
// Configuration
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public override PK7 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PK7);
|
||||
|
||||
|
|
@ -76,8 +76,8 @@ protected void ReloadBattleTeams()
|
|||
|
||||
public override int MaxBallID => Legal.MaxBallID_7; // 26
|
||||
public override GameVersion MaxGameID => Legal.MaxGameID_7;
|
||||
protected override PK7 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray6(data);
|
||||
protected override PK7 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt67(data);
|
||||
|
||||
// Feature Overrides
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,15 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IMysteryGiftStora
|
|||
|
||||
public override Type PKMType => typeof(PB7);
|
||||
public override PB7 BlankPKM => new();
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_6STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_6PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_6PARTY;
|
||||
public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData;
|
||||
public override PB7 GetBoxSlot(int offset) => GetDecryptedPKM(Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PB7 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
protected override PB7 GetBoxSlot(int offset) => GetDecryptedPKM(Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PB7 GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
public override PersonalTable7GG Personal => PersonalTable.GG;
|
||||
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_GG;
|
||||
|
|
@ -106,8 +109,8 @@ protected override void SetPKM(PKM pk, bool isParty = false)
|
|||
public override bool GetCaught(ushort species) => Blocks.Zukan.GetCaught(species);
|
||||
public override bool GetSeen(ushort species) => Blocks.Zukan.GetSeen(species);
|
||||
|
||||
protected override PB7 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray6(data);
|
||||
protected override PB7 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt67(data);
|
||||
public override int GetBoxOffset(int box) => Box + (box * BoxSlotCount * SIZE_BOXSLOT);
|
||||
protected override IList<int>[] SlotPointers => [ Blocks.Storage.PokeListInfo ];
|
||||
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ private void Initialize()
|
|||
}
|
||||
|
||||
// Configuration
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8PARTY;
|
||||
public override PB8 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PB8);
|
||||
|
|
@ -197,8 +197,8 @@ public override bool ChecksumsValid
|
|||
|
||||
#endregion
|
||||
|
||||
protected override PB8 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray8(data);
|
||||
protected override PB8 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt8(data);
|
||||
|
||||
#region Blocks
|
||||
// public Box8 BoxInfo { get; }
|
||||
|
|
@ -276,7 +276,6 @@ public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, i
|
|||
public void SetBoxWallpaper(int box, int value) => BoxLayout.SetBoxWallpaper(box, value);
|
||||
public string GetBoxName(int box) => BoxLayout[box];
|
||||
public void SetBoxName(int box, ReadOnlySpan<char> value) => BoxLayout.SetBoxName(box, value);
|
||||
public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData;
|
||||
public override int CurrentBox { get => BoxLayout.CurrentBox; set => BoxLayout.CurrentBox = (byte)value; }
|
||||
public override int BoxesUnlocked { get => BoxLayout.BoxesUnlocked; set => BoxLayout.BoxesUnlocked = (byte)value; }
|
||||
|
||||
|
|
@ -333,8 +332,14 @@ public override int PartyCount
|
|||
protected set => PartyInfo.PartyCount = value;
|
||||
}
|
||||
|
||||
public override PB8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
public override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PB8 GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
protected override PB8 GetBoxSlot(int offset) => GetDecryptedPKM(Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data) => pk.WriteEncryptedDataParty(data);
|
||||
|
||||
public enum TopMenuItemType
|
||||
{
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ public override void CopyChangesFrom(SaveFile sav)
|
|||
State.Edited = true;
|
||||
}
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8ASTORED;
|
||||
protected override PA8 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray8A(data);
|
||||
protected override PA8 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt8A(data);
|
||||
|
||||
public override PA8 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PA8);
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ private void Initialize()
|
|||
public override IReadOnlyList<string> PKMExtensions => EntityFileExtension.GetExtensionsHOME();
|
||||
|
||||
// Configuration
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8PARTY;
|
||||
public override PK8 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PK8);
|
||||
|
|
@ -133,8 +133,8 @@ private void Initialize()
|
|||
public override EntityContext Context => EntityContext.Gen8;
|
||||
public override int MaxStringLengthTrainer => 12;
|
||||
public override int MaxStringLengthNickname => 12;
|
||||
protected override PK8 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray8(data);
|
||||
protected override PK8 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt8(data);
|
||||
|
||||
public override bool IsVersionValid() => Version is GameVersion.SW or GameVersion.SH;
|
||||
|
||||
|
|
@ -168,7 +168,6 @@ public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, i
|
|||
public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30);
|
||||
public string GetBoxName(int box) => BoxLayout[box];
|
||||
public void SetBoxName(int box, ReadOnlySpan<char> value) => BoxLayout.SetBoxName(box, value);
|
||||
public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData;
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
@ -228,8 +227,14 @@ public override int PartyCount
|
|||
|
||||
protected override Span<byte> BoxBuffer => BoxInfo.Data;
|
||||
protected override Span<byte> PartyBuffer => PartyInfo.Data;
|
||||
public override PK8 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
public override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PK8 GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
protected override PK8 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data) => pk.WriteEncryptedDataParty(data);
|
||||
|
||||
public int GetRecord(int recordID) => Records.GetRecord(recordID);
|
||||
public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value);
|
||||
|
|
|
|||
|
|
@ -123,9 +123,9 @@ private void Initialize()
|
|||
}
|
||||
|
||||
// Configuration
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_9STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_9PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_9PARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8PARTY;
|
||||
public override PK9 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PK9);
|
||||
|
||||
|
|
@ -135,8 +135,8 @@ private void Initialize()
|
|||
public override EntityContext Context => EntityContext.Gen9;
|
||||
public override int MaxStringLengthTrainer => 12;
|
||||
public override int MaxStringLengthNickname => 12;
|
||||
protected override PK9 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray9(data);
|
||||
protected override PK9 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt8(data);
|
||||
|
||||
public override bool IsVersionValid() => Version is GameVersion.SL or GameVersion.VL;
|
||||
|
||||
|
|
@ -171,7 +171,6 @@ public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, i
|
|||
public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30);
|
||||
public string GetBoxName(int box) => BoxLayout[box];
|
||||
public void SetBoxName(int box, ReadOnlySpan<char> value) => BoxLayout.SetBoxName(box, value);
|
||||
public override byte[] GetDataForBox(PKM pk) => pk.EncryptedPartyData;
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
@ -233,8 +232,14 @@ public override int PartyCount
|
|||
|
||||
protected override Span<byte> BoxBuffer => BoxInfo.Data;
|
||||
protected override Span<byte> PartyBuffer => PartyInfo.Data;
|
||||
public override PK9 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
public override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PK9 GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
protected override PK9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data) => pk.WriteEncryptedDataParty(data);
|
||||
|
||||
//public int GetRecord(int recordID) => Records.GetRecord(recordID);
|
||||
//public void SetRecord(int recordID, int value) => Records.SetRecord(recordID, value);
|
||||
|
|
|
|||
|
|
@ -128,9 +128,9 @@ private void Initialize()
|
|||
public override IReadOnlyList<string> PKMExtensions => EntityFileExtension.GetExtensionsHOME();
|
||||
|
||||
// Configuration
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_9STORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_9PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_9PARTY + GapBoxSlot;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
|
||||
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8PARTY + GapBoxSlot;
|
||||
public override PA9 BlankPKM => new();
|
||||
public override Type PKMType => typeof(PA9);
|
||||
|
||||
|
|
@ -140,8 +140,8 @@ private void Initialize()
|
|||
public override EntityContext Context => EntityContext.Gen9a;
|
||||
public override int MaxStringLengthTrainer => 12;
|
||||
public override int MaxStringLengthNickname => 12;
|
||||
protected override PA9 GetPKM(byte[] data) => new(data);
|
||||
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray9(data);
|
||||
protected override PA9 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt8(data);
|
||||
|
||||
public override bool IsVersionValid() => Version is GameVersion.ZA;
|
||||
|
||||
|
|
@ -176,7 +176,6 @@ public override int SetString(Span<byte> destBuffer, ReadOnlySpan<char> value, i
|
|||
public override int GetBoxOffset(int box) => Box + (SIZE_BOXSLOT * box * 30);
|
||||
public string GetBoxName(int box) => BoxLayout[box];
|
||||
public void SetBoxName(int box, ReadOnlySpan<char> value) => BoxLayout.SetBoxName(box, value);
|
||||
public override byte[] GetDataForBox(PKM pk) => [..pk.EncryptedPartyData, ..new byte[GapBoxSlot]];
|
||||
|
||||
protected override void SetPKM(PKM pk, bool isParty = false)
|
||||
{
|
||||
|
|
@ -217,8 +216,14 @@ public override int PartyCount
|
|||
|
||||
protected override Span<byte> BoxBuffer => BoxInfo.Data;
|
||||
protected override Span<byte> PartyBuffer => PartyInfo.Data;
|
||||
public override PA9 GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
public override PA9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
public override PA9 GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
protected override PA9 GetBoxSlot(int offset) => GetDecryptedPKM(BoxInfo.Data.Slice(offset, SIZE_PARTY).ToArray()); // party format in boxes!
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data) => pk.WriteEncryptedDataParty(data);
|
||||
|
||||
public override StorageSlotSource GetBoxSlotFlags(int index)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ protected SAV_STADIUM(bool japanese, [ConstantExpected] int size) : base(size)
|
|||
OT = BlankSaveFile.GetSafeTrainerName(this, (LanguageID)Language);
|
||||
}
|
||||
|
||||
protected sealed override byte[] DecryptPKM(byte[] data) => data;
|
||||
protected sealed override void DecryptPKM(Span<byte> data) { }
|
||||
public sealed override int GetPartyOffset(int slot) => -1;
|
||||
public sealed override bool ChecksumsValid => GetBoxChecksumsValid();
|
||||
public sealed override string ChecksumInfo => ChecksumsValid ? "Checksum valid." : "Checksum invalid";
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ public void SetSlotFormatParty(PKM pk, Span<byte> data, EntityImportSettings set
|
|||
|
||||
UpdatePKM(pk, isParty: true, settings);
|
||||
SetPartyValues(pk, isParty: true);
|
||||
WritePartySlot(pk, data);
|
||||
WriteSlotParty(pk, data);
|
||||
}
|
||||
|
||||
public void SetSlotFormatStored(PKM pk, Span<byte> data, EntityImportSettings settings = default)
|
||||
|
|
@ -249,7 +249,7 @@ public void SetSlotFormatStored(PKM pk, Span<byte> data, EntityImportSettings se
|
|||
|
||||
UpdatePKM(pk, isParty: false, settings);
|
||||
SetPartyValues(pk, isParty: false);
|
||||
WriteSlotFormatStored(pk, data);
|
||||
WriteSlotStored(pk, data);
|
||||
}
|
||||
|
||||
public void SetPartySlot(PKM pk, Span<byte> data, EntityImportSettings settings = default)
|
||||
|
|
@ -262,7 +262,7 @@ public void SetBoxSlot(PKM pk, Span<byte> data, EntityImportSettings settings =
|
|||
|
||||
UpdatePKM(pk, isParty: false, settings);
|
||||
SetPartyValues(pk, isParty: false);
|
||||
WriteBoxSlot(pk, data);
|
||||
WriteSlotBox(pk, data);
|
||||
}
|
||||
|
||||
public void DeletePartySlot(int slot)
|
||||
|
|
@ -287,11 +287,11 @@ public void DeletePartySlot(int slot)
|
|||
public static EntityImportSettings SetUpdateSettings => new(SetUpdatePKM, SetUpdateDex, SetUpdateRecords);
|
||||
|
||||
public abstract Type PKMType { get; }
|
||||
protected abstract PKM GetPKM(byte[] data);
|
||||
protected abstract byte[] DecryptPKM(byte[] data);
|
||||
protected abstract PKM GetPKM(Memory<byte> data);
|
||||
protected abstract void DecryptPKM(Span<byte> data);
|
||||
public abstract PKM BlankPKM { get; }
|
||||
protected abstract int SIZE_STORED { get; }
|
||||
protected abstract int SIZE_PARTY { get; }
|
||||
public abstract int SIZE_STORED { get; }
|
||||
public abstract int SIZE_PARTY { get; }
|
||||
public virtual int SIZE_BOXSLOT => SIZE_STORED;
|
||||
public abstract int MaxEV { get; }
|
||||
public virtual int MaxIV => 31;
|
||||
|
|
@ -299,20 +299,19 @@ public void DeletePartySlot(int slot)
|
|||
protected virtual Span<byte> BoxBuffer => Data;
|
||||
protected virtual Span<byte> PartyBuffer => Data;
|
||||
public virtual bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresent(data);
|
||||
public virtual PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
|
||||
public virtual PKM GetDecryptedPKM(Memory<byte> data)
|
||||
{
|
||||
DecryptPKM(data.Span);
|
||||
return GetPKM(data);
|
||||
}
|
||||
|
||||
public virtual PKM GetPartySlot(ReadOnlySpan<byte> data) => GetDecryptedPKM(data[..SIZE_PARTY].ToArray());
|
||||
public virtual PKM GetStoredSlot(ReadOnlySpan<byte> data) => GetDecryptedPKM(data[..SIZE_STORED].ToArray());
|
||||
public virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer[offset..]);
|
||||
protected virtual PKM GetBoxSlot(int offset) => GetStoredSlot(BoxBuffer[offset..]);
|
||||
|
||||
public virtual byte[] GetDataForFormatStored(PKM pk) => pk.EncryptedBoxData;
|
||||
public virtual byte[] GetDataForFormatParty(PKM pk) => pk.EncryptedPartyData;
|
||||
public virtual byte[] GetDataForParty(PKM pk) => pk.EncryptedPartyData;
|
||||
public virtual byte[] GetDataForBox(PKM pk) => pk.EncryptedBoxData;
|
||||
|
||||
public virtual void WriteSlotFormatStored(PKM pk, Span<byte> data) => SetData(data, GetDataForFormatStored(pk));
|
||||
public virtual void WriteSlotFormatParty(PKM pk, Span<byte> data) => SetData(data, GetDataForFormatParty(pk));
|
||||
public virtual void WritePartySlot(PKM pk, Span<byte> data) => SetData(data, GetDataForParty(pk));
|
||||
public virtual void WriteBoxSlot(PKM pk, Span<byte> data) => SetData(data, GetDataForBox(pk));
|
||||
protected virtual void WriteSlotStored(PKM pk, Span<byte> data) => pk.WriteEncryptedDataStored(data);
|
||||
protected virtual void WriteSlotParty(PKM pk, Span<byte> data) => pk.WriteEncryptedDataParty(data);
|
||||
protected virtual void WriteSlotBox(PKM pk, Span<byte> data) => WriteSlotStored(pk, data);
|
||||
|
||||
protected virtual void SetPartyValues(PKM pk, bool isParty)
|
||||
{
|
||||
|
|
@ -735,7 +734,11 @@ public int ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func<PKM, bool>? delete
|
|||
if ((uint)BoxEnd >= BoxCount)
|
||||
BoxEnd = BoxCount - 1;
|
||||
|
||||
var blank = GetDataForBox(BlankPKM);
|
||||
// Get the at-rest data for a blank slot in the box. Only need to do this once rather than every slot.
|
||||
var fake = BlankPKM;
|
||||
Span<byte> blank = stackalloc byte[SIZE_BOXSLOT];
|
||||
WriteSlotBox(fake, blank);
|
||||
|
||||
int deleted = 0;
|
||||
for (int i = BoxStart; i <= BoxEnd; i++)
|
||||
{
|
||||
|
|
@ -796,8 +799,31 @@ public int ModifyBoxes(Action<PKM> action, int BoxStart = 0, int BoxEnd = -1)
|
|||
#endregion
|
||||
|
||||
#region Box Binaries
|
||||
public byte[] GetPCBinary() => BoxData.SelectMany(GetDataForBox).ToArray();
|
||||
public byte[] GetBoxBinary(int box) => GetBoxData(box).SelectMany(GetDataForBox).ToArray();
|
||||
public byte[] GetPCBinary()
|
||||
{
|
||||
var size = SIZE_BOXSLOT;
|
||||
var result = new byte[size * SlotCount];
|
||||
for (int i = 0; i < SlotCount; i++)
|
||||
{
|
||||
var data = GetBoxSlotAtIndex(i);
|
||||
var dest = result.AsSpan(i * size, size);
|
||||
WriteSlotBox(data, dest);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public byte[] GetBoxBinary(int box)
|
||||
{
|
||||
var size = SIZE_BOXSLOT;
|
||||
var result = new byte[size * BoxSlotCount];
|
||||
for (int i = 0; i < BoxSlotCount; i++)
|
||||
{
|
||||
var data = GetBoxSlotAtIndex(box, i);
|
||||
var dest = result.AsSpan(i * size, size);
|
||||
WriteSlotBox(data, dest);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool SetPCBinary(ReadOnlySpan<byte> data)
|
||||
{
|
||||
|
|
@ -832,6 +858,7 @@ private bool SetConcatenatedBinary(ReadOnlySpan<byte> data, int expectLength, in
|
|||
continue;
|
||||
var src = data.Slice(i, entryLength);
|
||||
var arr = src.ToArray();
|
||||
DecryptPKM(arr);
|
||||
var pk = GetPKM(arr);
|
||||
SetBoxSlotAtIndex(pk, ctr++);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ public sealed class Bank3 : BulkStorage, IBoxDetailNameRead
|
|||
|
||||
public override GameVersion Version { get => GameVersion.RS; set { } }
|
||||
public override PersonalTable3 Personal => PersonalTable.RS;
|
||||
protected override PK3 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt3(data);
|
||||
|
||||
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
protected override Bank3 CloneInternal() => new(Data.ToArray());
|
||||
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public sealed class Bank4 : BulkStorage, IBoxDetailNameRead
|
|||
|
||||
public override GameVersion Version { get => GameVersion.HGSS; set { } }
|
||||
public override PersonalTable4 Personal => PersonalTable.HGSS;
|
||||
protected override PK4 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt45(data);
|
||||
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_HGSS;
|
||||
protected override Bank4 CloneInternal() => new(Data.ToArray());
|
||||
public override string PlayTimeString => Checksums.CRC16Invert(Data).ToString("X4");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public sealed class Bank7 : BulkStorage, IBoxDetailNameRead
|
|||
public override GameVersion Version { get => GameVersion.USUM; set { } }
|
||||
public override PersonalTable7 Personal => PersonalTable.USUM;
|
||||
public override ReadOnlySpan<ushort> HeldItems => Legal.HeldItems_SM;
|
||||
protected override PK7 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt67(data);
|
||||
protected override Bank7 CloneInternal() => new(Data.ToArray(), PKMType, BoxStart, SlotsPerBox);
|
||||
public override string PlayTimeString => $"{Year:00}{Month:00}{Day:00}_{Hours:00}ː{Minutes:00}";
|
||||
protected internal override string ShortSummary => PlayTimeString;
|
||||
|
|
|
|||
|
|
@ -28,11 +28,8 @@ protected BulkStorage(Memory<byte> data, Type t, int start, int slotsPerBox = 30
|
|||
public sealed override Type PKMType => blank.GetType();
|
||||
public sealed override PKM BlankPKM => blank.Clone();
|
||||
|
||||
protected override PKM GetPKM(byte[] data) => EntityFormat.GetFromBytes(data, prefer: Context) ?? blank;
|
||||
protected override byte[] DecryptPKM(byte[] data) => GetPKM(data).Data.ToArray();
|
||||
|
||||
protected override int SIZE_STORED => blank.SIZE_STORED;
|
||||
protected override int SIZE_PARTY => blank.SIZE_PARTY;
|
||||
public override int SIZE_STORED => blank.SIZE_STORED;
|
||||
public override int SIZE_PARTY => blank.SIZE_PARTY;
|
||||
public sealed override int MaxEV => blank.MaxEV;
|
||||
public sealed override byte Generation => blank.Format;
|
||||
public sealed override EntityContext Context => blank.Context;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
|
||||
{
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_4RSTORED;
|
||||
protected override int SIZE_PARTY => PokeCrypto.SIZE_4RSTORED;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_4RSTORED;
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_4RSTORED;
|
||||
public int MaxToyID => (int) ((SaveRevision == 0) ? RanchToyType.Poke_Ball : RanchToyType.Water);
|
||||
public int SaveRevision => Version == GameVersion.DP ? 0 : 1;
|
||||
public string SaveRevisionString => Version == GameVersion.DP ? "-DP" : "-Pt";
|
||||
|
|
@ -39,7 +39,9 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision
|
|||
protected internal override string ShortSummary => $"{OT} {PlayTimeString}";
|
||||
public override string Extension => ".bin";
|
||||
|
||||
protected override RK4 GetPKM(byte[] data) => new(data);
|
||||
protected override RK4 GetPKM(Memory<byte> data) => new(data);
|
||||
protected override void DecryptPKM(Span<byte> data) => PokeCrypto.Decrypt45(data[..PokeCrypto.SIZE_4STORED]);
|
||||
|
||||
public override StorageSlotSource GetBoxSlotFlags(int index) => index >= SlotCount ? StorageSlotSource.Locked : StorageSlotSource.None;
|
||||
protected override bool IsSlotSwapProtected(int box, int slot) => IsBoxSlotOverwriteProtected(box, slot);
|
||||
public override bool IsPKMPresent(ReadOnlySpan<byte> data) => EntityDetection.IsPresentSAV4Ranch(data);
|
||||
|
|
@ -172,17 +174,6 @@ private int GetOccupiedSlotCount()
|
|||
return 0;
|
||||
}
|
||||
|
||||
protected override byte[] DecryptPKM(byte[] data)
|
||||
{
|
||||
var pokeData = PokeCrypto.DecryptArray45(data.AsSpan(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, string htName = "", ushort htTID = 0, ushort htSID = 0, RanchOwnershipType type = RanchOwnershipType.Hayley)
|
||||
{
|
||||
RK4 rk = (RK4)this.GetCompatiblePKM(pk);
|
||||
|
|
@ -191,10 +182,10 @@ public void WriteBoxSlotInternal(PKM pk, Span<byte> data, string htName = "", us
|
|||
rk.HandlingTrainerSID = htSID;
|
||||
rk.HandlingTrainerName = htName;
|
||||
|
||||
WriteBoxSlot(rk, data);
|
||||
WriteSlotBox(rk, data);
|
||||
}
|
||||
|
||||
public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
||||
protected override void WriteSlotBox(PKM pk, Span<byte> data)
|
||||
{
|
||||
if (pk is not RK4 rk4)
|
||||
{
|
||||
|
|
@ -206,7 +197,7 @@ public override void WriteBoxSlot(PKM pk, Span<byte> data)
|
|||
if (!isBlank && rk4.OwnershipType == RanchOwnershipType.None)
|
||||
rk4.OwnershipType = RanchOwnershipType.Hayley; // Pokémon without an Ownership type get erased when the save is loaded. Hayley is considered 'default'.
|
||||
|
||||
base.WriteBoxSlot(rk4, data);
|
||||
base.WriteSlotBox(rk4, data);
|
||||
}
|
||||
|
||||
private void UpdateMetadata(int pkEnd)
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public void SetTeam(IReadOnlyList<PK3> team, int teamIndex)
|
|||
for (int p = 0; p < 6; p++)
|
||||
{
|
||||
int offset = ofs + (PokeCrypto.SIZE_3PARTY * p);
|
||||
team[p].EncryptedPartyData.CopyTo(Data[offset..]);
|
||||
team[p].WriteEncryptedDataParty(Data[offset..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public void SetTeam(IReadOnlyList<PK6> team, int t)
|
|||
{
|
||||
int offset = start + (PokeCrypto.SIZE_6PARTY * ((t * 6) + p));
|
||||
offset += 8 * (((t * 6) + p) / 6); // 8 bytes padding between teams
|
||||
team[p].EncryptedPartyData.CopyTo(Data[offset..]);
|
||||
team[p].WriteEncryptedDataParty(Data[offset..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public void SetTeam(IReadOnlyList<PK7> team, int teamIndex)
|
|||
for (int p = 0; p < 6; p++)
|
||||
{
|
||||
int offset = ofs + (PokeCrypto.SIZE_6PARTY * p);
|
||||
team[p].EncryptedPartyData.CopyTo(Data[offset..]);
|
||||
team[p].WriteEncryptedDataParty(Data[offset..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,13 +148,19 @@ public void ResetPresetIndexes()
|
|||
// 0x4B8-53F Party Member 6
|
||||
private static int GetPartyOffset(int index) => 0x1FC + (PokeSize * index);
|
||||
private Span<byte> GetPartySpan(int index) => Data.Slice(GetPartyOffset(index), PokeSize);
|
||||
public BK4 GetPartySlotAtIndex(int index) => BK4.ReadUnshuffle(GetPartySpan(index));
|
||||
public BK4 GetPartySlotAtIndex(int index)
|
||||
{
|
||||
var data = GetPartySpan(index).ToArray();
|
||||
PokeCrypto.Decrypt4BE(data);
|
||||
return new BK4(data);
|
||||
}
|
||||
|
||||
public void SetPartySlotAtIndex(PKM pk, int index)
|
||||
{
|
||||
while (index > 0 && !GetPartySlotPresent(index - 1))
|
||||
index--;
|
||||
|
||||
pk.EncryptedBoxData.CopyTo(GetPartySpan(index));
|
||||
pk.WriteEncryptedDataParty(GetPartySpan(index));
|
||||
SetPartySlotPresent(index, pk.Species != 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public static void SetQRData(PK7 pk7, Span<byte> span, int box = 0, int slot = 0
|
|||
WriteInt32LittleEndian(span[0x0C..], slot);
|
||||
WriteInt32LittleEndian(span[0x10..], num_copies); // No need to check max num_copies, payload parser handles it on-console.
|
||||
|
||||
pk7.EncryptedPartyData.CopyTo(span[0x30..]); // Copy in Pokémon data
|
||||
pk7.WriteEncryptedDataParty(span[0x30..]); // Copy in Pokémon data
|
||||
SetDexData(span[0x140..], pk7.Species, pk7.Form, pk7.IsShiny, pk7.Gender);
|
||||
|
||||
var chk = Checksums.CRC16Invert(span[..0x1A0]);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public static int GetFusedSlotOffset(int slot)
|
|||
public Memory<byte> this[int i] => Raw.Slice(GetFusedSlotOffset(i), SizeStored);
|
||||
private Span<byte> GetSlotSpan(int index) => Data.Slice(GetFusedSlotOffset(index), SizeStored);
|
||||
private PK8 GetStoredSlot(int index) => (PK8)SAV.GetStoredSlot(GetSlotSpan(index));
|
||||
private void SetStoredSlot(PK8 pk, int index) => pk.EncryptedBoxData.CopyTo(GetSlotSpan(index));
|
||||
private void SetStoredSlot(PK8 pk, int index) => pk.WriteEncryptedDataStored(GetSlotSpan(index));
|
||||
|
||||
public PK8 Kyurem { get => GetStoredSlot(0); set => SetStoredSlot(value, 0); }
|
||||
public PK8 NecrozmaSolgaleo { get => GetStoredSlot(1); set => SetStoredSlot(value, 1); }
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ namespace PKHeX.Core;
|
|||
|
||||
public sealed class Box9(SaveFile sav, SCBlock block) : SaveBlock<SaveFile>(sav, block.Raw)
|
||||
{
|
||||
private const int afterBox = BoxLayout9.BoxCount * 30 * PokeCrypto.SIZE_9PARTY;
|
||||
public Memory<byte> RideLegend => Raw.Slice(afterBox, PokeCrypto.SIZE_9PARTY);
|
||||
private const int afterBox = BoxLayout9.BoxCount * 30 * PokeCrypto.SIZE_8PARTY;
|
||||
public Memory<byte> RideLegend => Raw.Slice(afterBox, PokeCrypto.SIZE_8PARTY);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ public bool IsEnabled
|
|||
[Category(General), Description("Encrypted Entity data.")]
|
||||
public PK9 Entity
|
||||
{
|
||||
get => new(Data.Slice(0x09, PokeCrypto.SIZE_9PARTY).ToArray());
|
||||
set => value.EncryptedPartyData.CopyTo(Data.Slice(0x9, PokeCrypto.SIZE_9PARTY));
|
||||
get => new(Data.Slice(0x09, PokeCrypto.SIZE_8PARTY).ToArray());
|
||||
set => value.WriteEncryptedDataParty(Data.Slice(0x9, PokeCrypto.SIZE_8PARTY));
|
||||
}
|
||||
|
||||
// 7 bytes of padding, for alignment of next field.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ public sealed class Party9(SAV9SV sav, SCBlock block) : SaveBlock<SAV9SV>(sav, b
|
|||
{
|
||||
public int PartyCount
|
||||
{
|
||||
get => Data[6 * PokeCrypto.SIZE_9PARTY];
|
||||
set => Data[6 * PokeCrypto.SIZE_9PARTY] = (byte)value;
|
||||
get => Data[6 * PokeCrypto.SIZE_8PARTY];
|
||||
set => Data[6 * PokeCrypto.SIZE_8PARTY] = (byte)value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ namespace PKHeX.Core;
|
|||
public sealed class Party9a(SAV9ZA sav, SCBlock block) : SaveBlock<SAV9ZA>(sav, block.Raw)
|
||||
{
|
||||
private const int MaxCount = 6;
|
||||
public const int SlotSize = PokeCrypto.SIZE_9PARTY + 0x40 + 0x48;
|
||||
public const int SlotSize = PokeCrypto.SIZE_8PARTY + 0x40 + 0x48;
|
||||
|
||||
public Memory<byte> GetSlot(int slot) => block.Raw.Slice(SlotSize * slot, SlotSize);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ public PK8 GetSlot(int slot)
|
|||
public void SetSlot(int slot, PK8 pk)
|
||||
{
|
||||
var ofs = GetSlotOffset(slot);
|
||||
var data1 = pk.EncryptedPartyData;
|
||||
var dest = Data[ofs..];
|
||||
pk.WriteEncryptedDataStored(dest);
|
||||
// Wipe Party Stats
|
||||
Array.Clear(data1, LEN_STORED, LEN_PARTYSTAT);
|
||||
data1.CopyTo(Data[ofs..]);
|
||||
dest.Slice(LEN_STORED, LEN_PARTYSTAT).Clear();
|
||||
}
|
||||
|
||||
public PK8[] GetTeam()
|
||||
|
|
|
|||
|
|
@ -77,10 +77,10 @@ public PK9 GetSlot(int slot)
|
|||
public void SetSlot(int slot, PK9 pk)
|
||||
{
|
||||
var ofs = GetSlotOffset(slot);
|
||||
var data = pk.EncryptedPartyData;
|
||||
var dest = Data[ofs..];
|
||||
pk.WriteEncryptedDataStored(dest);
|
||||
// Wipe Party Stats
|
||||
Array.Clear(data, LEN_STORED, LEN_PARTYSTAT);
|
||||
data.CopyTo(Data[ofs..]);
|
||||
dest.Slice(LEN_STORED, LEN_PARTYSTAT).Clear();
|
||||
}
|
||||
|
||||
public PK9[] GetTeam()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public int DumpBoxes(string path, bool boxFolders = false)
|
|||
if (!sav.HasBox)
|
||||
return -1;
|
||||
|
||||
Span<byte> data = stackalloc byte[sav.SIZE_PARTY];
|
||||
var boxData = sav.BoxData;
|
||||
int boxSlotCount = sav.BoxSlotCount;
|
||||
var ctr = 0;
|
||||
|
|
@ -49,7 +50,9 @@ public int DumpBoxes(string path, bool boxFolders = false)
|
|||
if (File.Exists(fn))
|
||||
continue;
|
||||
|
||||
File.WriteAllBytes(fn, pk.DecryptedPartyData);
|
||||
pk.ForcePartyData();
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
File.WriteAllBytes(fn, data);
|
||||
ctr++;
|
||||
}
|
||||
return ctr;
|
||||
|
|
@ -66,6 +69,7 @@ public int DumpBox(string path, int currentBox)
|
|||
if (!sav.HasBox)
|
||||
return -1;
|
||||
|
||||
Span<byte> data = stackalloc byte[sav.SIZE_PARTY];
|
||||
var boxData = sav.BoxData;
|
||||
int boxSlotCount = sav.BoxSlotCount;
|
||||
var ctr = 0;
|
||||
|
|
@ -80,7 +84,9 @@ public int DumpBox(string path, int currentBox)
|
|||
if (File.Exists(fileName))
|
||||
continue;
|
||||
|
||||
File.WriteAllBytes(fileName, pk.DecryptedPartyData);
|
||||
pk.ForcePartyData();
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
File.WriteAllBytes(fileName, data);
|
||||
ctr++;
|
||||
}
|
||||
return ctr;
|
||||
|
|
|
|||
|
|
@ -205,7 +205,12 @@ private string CreateDragDropPKM(PictureBox pb, bool encrypt, out bool external)
|
|||
string newfile = FileUtil.GetPKMTempFileName(pk, encrypt);
|
||||
try
|
||||
{
|
||||
var data = encrypt ? pk.EncryptedPartyData : pk.DecryptedPartyData;
|
||||
pk.ForcePartyData();
|
||||
Span<byte> data = stackalloc byte[pk.SIZE_PARTY];
|
||||
if (!encrypt)
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
else
|
||||
pk.WriteEncryptedDataParty(data);
|
||||
external = TryMakeDragDropPKM(pb, data, newfile);
|
||||
}
|
||||
// Tons of things can happen with drag & drop; don't try to handle things, just indicate failure.
|
||||
|
|
|
|||
|
|
@ -1232,7 +1232,11 @@ private async void Dragout_MouseDown(object sender, MouseEventArgs e)
|
|||
var pk = PreparePKM();
|
||||
var preModify = pk.Clone();
|
||||
var encrypt = ModifierKeys == Keys.Control;
|
||||
var data = encrypt ? pk.EncryptedPartyData : pk.DecryptedPartyData;
|
||||
var data = new byte[pk.SIZE_PARTY];
|
||||
if (!encrypt)
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
else
|
||||
pk.WriteEncryptedDataParty(data);
|
||||
|
||||
// Create Temp File to Drag
|
||||
var newfile = FileUtil.GetPKMTempFileName(pk, encrypt);
|
||||
|
|
|
|||
|
|
@ -283,6 +283,11 @@ private void TryProcess(string source, string destDir, IReadOnlyList<StringInstr
|
|||
}
|
||||
|
||||
if (editor.Process(pk, pkFilters, instructions))
|
||||
File.WriteAllBytes(Path.Combine(destDir, Path.GetFileName(source)), pk.DecryptedPartyData);
|
||||
{
|
||||
Span<byte> result = stackalloc byte[pk.SIZE_PARTY];
|
||||
pk.ForcePartyData();
|
||||
pk.WriteDecryptedDataParty(result);
|
||||
File.WriteAllBytes(Path.Combine(destDir, Path.GetFileName(source)), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ private void ClickDelete(object sender, EventArgs e)
|
|||
{
|
||||
// Data from Box: Delete from save file
|
||||
var exist = b.Read(SAV);
|
||||
if (!exist.DecryptedBoxData.SequenceEqual(pk.DecryptedBoxData)) // data modified already?
|
||||
if (!exist.EqualsStored(pk)) // data modified already?
|
||||
{
|
||||
WinFormsUtil.Error(MsgDBDeleteFailModified, MsgDBDeleteFailWarning);
|
||||
return;
|
||||
|
|
@ -263,7 +263,9 @@ private void ClickSet(object sender, EventArgs e)
|
|||
return;
|
||||
}
|
||||
|
||||
File.WriteAllBytes(path, pk.DecryptedBoxData);
|
||||
Span<byte> data = stackalloc byte[pk.SIZE_STORED];
|
||||
pk.WriteDecryptedDataStored(data);
|
||||
File.WriteAllBytes(path, data);
|
||||
|
||||
var info = new SlotInfoFileSingle(path);
|
||||
var entry = new SlotCache(info, pk);
|
||||
|
|
@ -446,8 +448,14 @@ private void Menu_Export_Click(object sender, EventArgs e)
|
|||
string path = fbd.SelectedPath;
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
Span<byte> data = stackalloc byte[SAV.SIZE_PARTY];
|
||||
foreach (var pk in Results.Select(z => z.Entity))
|
||||
File.WriteAllBytes(Path.Combine(path, PathUtil.CleanFileName(pk.FileName)), pk.DecryptedPartyData);
|
||||
{
|
||||
var fileName = Path.Combine(path, PathUtil.CleanFileName(pk.FileName));
|
||||
pk.ForcePartyData();
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
File.WriteAllBytes(fileName, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void Menu_Import_Click(object sender, EventArgs e)
|
||||
|
|
|
|||
|
|
@ -349,9 +349,9 @@ private void SaveBattleAgency()
|
|||
WriteUInt16LittleEndian(SAV.Data[0x6C3EE..], (ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C3EE) | InvitedValue : 0));
|
||||
WriteUInt16LittleEndian(SAV.Data[0x6C526..], (ushort)(CHK_TrainerInvited.Checked ? GetSavData16(0x6C526) | InvitedValue : 0));
|
||||
}
|
||||
SAV.SetData(p[0].EncryptedBoxData, 0x6C200);
|
||||
SAV.SetData(p[1].EncryptedPartyData, 0x6C2E8);
|
||||
SAV.SetData(p[2].EncryptedPartyData, 0x6C420);
|
||||
p[0].WriteEncryptedDataStored(SAV.Data[0x6C200..]); // BattleFesSave
|
||||
p[1].WriteEncryptedDataParty(SAV.Data[0x6C2E8..]);
|
||||
p[2].WriteEncryptedDataParty(SAV.Data[0x6C420..]);
|
||||
|
||||
var gradeDefeated = ((((int)NUD_Defeated.Value & 0xF) << 12) | (((int)NUD_Grade.Value & 0x3F) << 6) | (SAV.Data[0x6C55C] & 0x3F));
|
||||
WriteUInt16LittleEndian(SAV.Data[0x6C558..], (ushort)NUD_DefeatMon.Value);
|
||||
|
|
|
|||
|
|
@ -366,7 +366,11 @@ private static void SavePKM(PKM pk, string path, ReadOnlySpan<char> pkx)
|
|||
{
|
||||
SaveBackup(path);
|
||||
var ext = Path.GetExtension(path);
|
||||
var data = ext == $".{pkx}" ? pk.DecryptedPartyData : pk.EncryptedPartyData;
|
||||
Span<byte> data = stackalloc byte[pk.SIZE_PARTY];
|
||||
if (ext == $".{pkx}")
|
||||
pk.WriteDecryptedDataParty(data);
|
||||
else
|
||||
pk.WriteEncryptedDataParty(data);
|
||||
File.WriteAllBytes(path, data);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ private static IEnumerable<string> GetHomeEncrypted()
|
|||
public static void CheckCrypto1()
|
||||
{
|
||||
var paths = GetHomeEncrypted();
|
||||
|
||||
const int maxSize = HomeCrypto.SIZE_STORED;
|
||||
var totalSize = PKH.GetPaddedSize(maxSize, out _);
|
||||
Span<byte> write = stackalloc byte[totalSize];
|
||||
foreach (var f in paths)
|
||||
{
|
||||
var data = File.ReadAllBytes(f);
|
||||
|
|
@ -46,8 +50,8 @@ public static void CheckCrypto1()
|
|||
|
||||
ph1.Clone().Should().NotBeNull();
|
||||
|
||||
var write = ph1.Rebuild();
|
||||
write.Length.Should().Be(decrypted.Length);
|
||||
var writeLength = ph1.Rebuild(write);
|
||||
writeLength.Should().Be(decrypted.Length);
|
||||
for (int i = 0; i < decrypted.Length; i++)
|
||||
write[i].Should().Be(decrypted[i], $"Offset {i:X2}");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user