PKHeX/PKHeX.Core/Saves/SAV8.cs
Kurt 041074f26c Decrypt sav8 blocks in-place
Removes 1.5MB of allocation; we don't need to preserve the encrypted state.

Revise savefile clone operation to use its own constructor, as we need to call the base constructor first (backup file clone...) prior to decrypting.

Expose CryptStaticXorpadBytes as GetDecryptedRawData no longer exists.
2021-06-04 13:16:00 -07:00

164 lines
No EOL
6.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
/// <summary>
/// Generation 8 <see cref="SaveFile"/> object.
/// </summary>
public abstract class SAV8 : SaveFile, ISaveBlock8Main
{
// Save Data Attributes
protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}";
public override string Extension => string.Empty;
public override IReadOnlyList<string> PKMExtensions => PKM.Extensions.Where(f =>
{
int gen = f.Last() - 0x30;
return gen <= 8;
}).ToArray();
protected SAV8(byte[] data) : base(data) { }
protected SAV8() { }
// Configuration
protected override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
protected override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
public override int SIZE_BOXSLOT => PokeCrypto.SIZE_8PARTY;
public override PKM BlankPKM => new PK8();
public override Type PKMType => typeof(PK8);
public override int BoxCount => BoxLayout8.BoxCount;
public override int MaxEV => 252;
public override int Generation => 8;
// protected override int GiftCountMax => 48;
// protected override int GiftFlagMax => 0x100 * 8;
protected override int EventConstMax => 1000;
public override int OTLength => 12;
public override int NickLength => 12;
protected override PKM GetPKM(byte[] data) => new PK8(data);
protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray8(data);
#region Blocks
public abstract Box8 BoxInfo { get; }
public abstract Party8 PartyInfo { get; }
public abstract MyItem Items { get; }
public abstract PlayTime8 Played { get; }
public abstract MyStatus8 MyStatus { get; }
public abstract Misc8 Misc { get; }
public abstract Zukan8 Zukan { get; }
public abstract BoxLayout8 BoxLayout { get; }
public abstract Fused8 Fused { get; }
public abstract Daycare8 Daycare { get; }
public abstract Record8 Records { get; }
public abstract TrainerCard8 TrainerCard { get; }
public abstract RaidSpawnList8 Raid { get; }
public abstract RaidSpawnList8 RaidArmor { get; }
public abstract RaidSpawnList8 RaidCrown { get; }
public abstract TitleScreen8 TitleScreen { get; }
public abstract TeamIndexes8 TeamIndexes { get; }
#endregion
public override GameVersion Version => Game switch
{
(int)GameVersion.SW => GameVersion.SW,
(int)GameVersion.SH => GameVersion.SH,
_ => GameVersion.Invalid
};
public override string GetString(byte[] data, int offset, int length) => StringConverter.GetString7b(data, offset, length);
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0)
{
if (PadToSize == 0)
PadToSize = maxLength + 1;
return StringConverter.SetString7b(value, maxLength, PadToSize, PadWith);
}
// Player Information
public override int TID { get => MyStatus.TID; set => MyStatus.TID = value; }
public override int SID { get => MyStatus.SID; set => MyStatus.SID = value; }
public override int Game { get => MyStatus.Game; set => MyStatus.Game = value; }
public override int Gender { get => MyStatus.Gender; set => MyStatus.Gender = value; }
public override int Language { get => MyStatus.Language; set => MyStatus.Language = value; }
public override string OT { get => MyStatus.OT; set => MyStatus.OT = value; }
public override uint Money { get => Misc.Money; set => Misc.Money = value; }
public int Badges { get => Misc.Badges; set => Misc.Badges = value; }
public override int PlayedHours { get => Played.PlayedHours; set => Played.PlayedHours = value; }
public override int PlayedMinutes { get => Played.PlayedMinutes; set => Played.PlayedMinutes = value; }
public override int PlayedSeconds { get => Played.PlayedSeconds; set => Played.PlayedSeconds = value; }
// Inventory
public override IReadOnlyList<InventoryPouch> Inventory { get => Items.Inventory; set => Items.Inventory = value; }
// Storage
public override int GetPartyOffset(int slot) => Party + (SIZE_PARTY * slot);
public override int GetBoxOffset(int box) => Box + (SIZE_PARTY * box * 30);
public override string GetBoxName(int box) => BoxLayout[box];
public override void SetBoxName(int box, string value) => BoxLayout[box] = value;
public override byte[] GetDataForBox(PKM pkm) => pkm.EncryptedPartyData;
protected override void SetPKM(PKM pkm, bool isParty = false)
{
PK8 pk = (PK8)pkm;
// Apply to this Save File
DateTime Date = DateTime.Now;
pk.Trade(this, Date.Day, Date.Month, Date.Year);
if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk.Species, pk.Form))
{
pk.FormArgumentElapsed = pk.FormArgumentMaximum = 0;
pk.FormArgumentRemain = (byte)GetFormArgument(pkm);
}
pkm.RefreshChecksum();
AddCountAcquired(pkm);
}
private static uint GetFormArgument(PKM pkm)
{
if (pkm.Form == 0)
return 0;
return pkm.Species switch
{
(int)Species.Furfrou => 5u, // Furfrou
(int)Species.Hoopa => 3u, // Hoopa
_ => 0u
};
}
private void AddCountAcquired(PKM pkm)
{
if (pkm.WasEgg)
{
Records.AddRecord(00);
}
else // capture, assume wild
{
Records.AddRecord(01); // wild capture
Records.AddRecord(06); // total captured
Records.AddRecord(16); // wild encountered
Records.AddRecord(23); // total battled
}
if (pkm.CurrentHandler == 1)
Records.AddRecord(17, 2); // trade * 2 -- these games count 1 trade as 2 for some reason.
}
protected override void SetDex(PKM pkm) => Zukan.SetDex(pkm);
public override bool GetCaught(int species) => Zukan.GetCaught(species);
public override bool GetSeen(int species) => Zukan.GetSeen(species);
public override int PartyCount
{
get => PartyInfo.PartyCount;
protected set => PartyInfo.PartyCount = value;
}
protected override byte[] BoxBuffer => BoxInfo.Data;
protected override byte[] PartyBuffer => PartyInfo.Data;
public override PKM GetDecryptedPKM(byte[] data) => GetPKM(DecryptPKM(data));
public override PKM GetBoxSlot(int offset) => GetDecryptedPKM(GetData(BoxInfo.Data, offset, SIZE_PARTY)); // party format in boxes!
}
}