mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-26 22:10:21 +00:00
Minimize BAK file allocations (#3426)
Stop allocating an entire shadow copy of the save file whenever we create a new savefile object from file. Prior commits added the clear SaveFileMetadata class to cleanly track the file path. Backups are copied from the original path.
This commit is contained in:
parent
04856122b7
commit
bc2549b24e
11 changed files with 60 additions and 69 deletions
|
@ -148,6 +148,19 @@ namespace PKHeX.Core
|
|||
public sealed override int MaxBallID => Legal.MaxBallID_3;
|
||||
public sealed override int MaxGameID => Legal.MaxGameID_3;
|
||||
|
||||
/// <summary>
|
||||
/// Force loads a new <see cref="SAV3"/> object to the requested <see cref="version"/>.
|
||||
/// </summary>
|
||||
/// <param name="version"> Version to retrieve for</param>
|
||||
/// <returns>New <see cref="SaveFile"/> object.</returns>
|
||||
public SAV3 ForceLoad(GameVersion version) => version switch
|
||||
{
|
||||
GameVersion.R or GameVersion.S or GameVersion.RS => new SAV3RS(Data),
|
||||
GameVersion.E => new SAV3E(Data),
|
||||
GameVersion.FR or GameVersion.LG or GameVersion.FRLG => new SAV3FRLG(Data),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(version)),
|
||||
};
|
||||
|
||||
public sealed override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
|
||||
public sealed override int BoxCount => 14;
|
||||
|
@ -182,7 +195,7 @@ namespace PKHeX.Core
|
|||
WriteUInt16LittleEndian(sector[0xFF6..], chk);
|
||||
}
|
||||
|
||||
if (State.BAK.Length < SaveUtil.SIZE_G3RAW) // don't update HoF for half-sizes
|
||||
if (Data.Length < SaveUtil.SIZE_G3RAW) // don't update HoF for half-sizes
|
||||
return;
|
||||
|
||||
// Hall of Fame Checksums
|
||||
|
@ -208,7 +221,7 @@ namespace PKHeX.Core
|
|||
return false;
|
||||
}
|
||||
|
||||
if (State.BAK.Length < SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes
|
||||
if (Data.Length < SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes
|
||||
return true;
|
||||
|
||||
if (!IsSectorValidExtra(0x1C000))
|
||||
|
@ -246,7 +259,7 @@ namespace PKHeX.Core
|
|||
list.Add($"Sector {i} @ {i*SIZE_SECTOR:X5} invalid.");
|
||||
}
|
||||
|
||||
if (State.BAK.Length > SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes
|
||||
if (Data.Length > SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes
|
||||
{
|
||||
if (!IsSectorValidExtra(0x1C000))
|
||||
list.Add("HoF first sector invalid.");
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace PKHeX.Core
|
|||
public override string Extension => this.GCExtension();
|
||||
public override PersonalTable Personal => PersonalTable.RS;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_COLO;
|
||||
public SAV3GCMemoryCard? MemoryCard { get; }
|
||||
public SAV3GCMemoryCard? MemoryCard { get; init; }
|
||||
|
||||
// 3 Save files are stored
|
||||
// 0x0000-0x6000 contains memory card data
|
||||
|
@ -33,17 +33,19 @@ namespace PKHeX.Core
|
|||
private readonly StrategyMemo StrategyMemo;
|
||||
public const int MaxShadowID = 0x80; // 128
|
||||
private int Memo;
|
||||
public SAV3Colosseum(byte[] data, SAV3GCMemoryCard memCard) : this(data, memCard.Data) => MemoryCard = memCard;
|
||||
public SAV3Colosseum(byte[] data) : this(data, (byte[])data.Clone()) { }
|
||||
|
||||
private readonly byte[] BAK;
|
||||
|
||||
public SAV3Colosseum() : base(SaveUtil.SIZE_G3COLO)
|
||||
{
|
||||
BAK = Array.Empty<byte>();
|
||||
StrategyMemo = Initialize();
|
||||
ClearBoxes();
|
||||
}
|
||||
|
||||
private SAV3Colosseum(byte[] data, byte[] bak) : base(data, bak)
|
||||
public SAV3Colosseum(byte[] data) : base(data)
|
||||
{
|
||||
BAK = data;
|
||||
InitializeData();
|
||||
StrategyMemo = Initialize();
|
||||
}
|
||||
|
@ -119,7 +121,7 @@ namespace PKHeX.Core
|
|||
byte[] newSAV = EncryptColosseum(slot, digest);
|
||||
|
||||
// Put save slot back in original save data
|
||||
byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData() : (byte[]) State.BAK.Clone();
|
||||
byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData() : (byte[])BAK.Clone();
|
||||
Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length);
|
||||
return newFile;
|
||||
}
|
||||
|
@ -128,8 +130,7 @@ namespace PKHeX.Core
|
|||
protected override SaveFile CloneInternal()
|
||||
{
|
||||
var data = GetInnerData();
|
||||
var sav = MemoryCard is not null ? new SAV3Colosseum(data, MemoryCard) : new SAV3Colosseum(data);
|
||||
return sav;
|
||||
return new SAV3Colosseum(data) { MemoryCard = MemoryCard };
|
||||
}
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3CSTORED;
|
||||
|
|
|
@ -14,11 +14,10 @@ namespace PKHeX.Core
|
|||
public override string Extension => this.GCExtension();
|
||||
public override PersonalTable Personal => PersonalTable.RS;
|
||||
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_RS;
|
||||
public SAV3GCMemoryCard? MemoryCard { get; }
|
||||
public SAV3GCMemoryCard? MemoryCard { get; init; }
|
||||
public readonly bool Japanese = false; // todo?
|
||||
|
||||
public SAV3RSBox(byte[] data, SAV3GCMemoryCard memCard) : this(data, memCard.Data) => MemoryCard = memCard;
|
||||
public SAV3RSBox(byte[] data) : this(data, (byte[])data.Clone()) { }
|
||||
public SAV3RSBox(byte[] data, SAV3GCMemoryCard memCard) : this(data) => MemoryCard = memCard;
|
||||
|
||||
public SAV3RSBox() : base(SaveUtil.SIZE_G3BOX)
|
||||
{
|
||||
|
@ -27,7 +26,7 @@ namespace PKHeX.Core
|
|||
ClearBoxes();
|
||||
}
|
||||
|
||||
private SAV3RSBox(byte[] data, byte[] bak) : base(data, bak)
|
||||
public SAV3RSBox(byte[] data) : base(data)
|
||||
{
|
||||
Blocks = ReadBlocks(data);
|
||||
InitializeData();
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace PKHeX.Core
|
|||
{
|
||||
protected internal override string ShortSummary => $"{OT} ({Version}) #{SaveCount:0000}";
|
||||
public override string Extension => this.GCExtension();
|
||||
public SAV3GCMemoryCard? MemoryCard { get; }
|
||||
public SAV3GCMemoryCard? MemoryCard { get; init; }
|
||||
|
||||
private const int SLOT_SIZE = 0x28000;
|
||||
private const int SLOT_START = 0x6000;
|
||||
|
@ -27,11 +27,11 @@ namespace PKHeX.Core
|
|||
public int MaxShadowID => ShadowInfo.Count;
|
||||
private int OFS_PouchHeldItem, OFS_PouchKeyItem, OFS_PouchBalls, OFS_PouchTMHM, OFS_PouchBerry, OFS_PouchCologne, OFS_PouchDisc;
|
||||
private readonly int[] subOffsets = new int[16];
|
||||
public SAV3XD(byte[] data, SAV3GCMemoryCard memCard) : this(data, memCard.Data) => MemoryCard = memCard;
|
||||
public SAV3XD(byte[] data) : this(data, (byte[])data.Clone()) { }
|
||||
private readonly byte[] BAK;
|
||||
|
||||
public SAV3XD() : base(SaveUtil.SIZE_G3XD)
|
||||
{
|
||||
BAK = Array.Empty<byte>();
|
||||
// create fake objects
|
||||
StrategyMemo = new StrategyMemo();
|
||||
ShadowInfo = new ShadowInfoTableXD(false);
|
||||
|
@ -39,8 +39,9 @@ namespace PKHeX.Core
|
|||
ClearBoxes();
|
||||
}
|
||||
|
||||
private SAV3XD(byte[] data, byte[] bak) : base(data, bak)
|
||||
public SAV3XD(byte[] data) : base(data)
|
||||
{
|
||||
BAK = data;
|
||||
InitializeData(out StrategyMemo, out ShadowInfo);
|
||||
Initialize();
|
||||
}
|
||||
|
@ -141,7 +142,7 @@ namespace PKHeX.Core
|
|||
byte[] newSAV = GeniusCrypto.Encrypt(Data, 0x10, 0x27FD8, keys);
|
||||
|
||||
// Put save slot back in original save data
|
||||
byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData() : (byte[]) State.BAK.Clone();
|
||||
byte[] newFile = MemoryCard != null ? MemoryCard.ReadSaveGameData() : (byte[]) BAK.Clone();
|
||||
Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length);
|
||||
return newFile;
|
||||
}
|
||||
|
@ -150,8 +151,7 @@ namespace PKHeX.Core
|
|||
protected override SaveFile CloneInternal()
|
||||
{
|
||||
var data = GetInnerData();
|
||||
var sav = MemoryCard is not null ? new SAV3XD(data, MemoryCard) : new SAV3XD(data);
|
||||
return sav;
|
||||
return new SAV3XD(data) { MemoryCard = MemoryCard };
|
||||
}
|
||||
|
||||
protected override int SIZE_STORED => PokeCrypto.SIZE_3XSTORED;
|
||||
|
|
|
@ -19,9 +19,8 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray
|
|||
Initialize();
|
||||
}
|
||||
|
||||
private SAV8LA(byte[] data, IReadOnlyList<SCBlock> blocks) : base(data)
|
||||
private SAV8LA(IReadOnlyList<SCBlock> blocks) : base(Array.Empty<byte>())
|
||||
{
|
||||
Data = Array.Empty<byte>();
|
||||
AllBlocks = blocks;
|
||||
Blocks = new SaveBlockAccessor8LA(this);
|
||||
Initialize();
|
||||
|
@ -112,7 +111,7 @@ public sealed class SAV8LA : SaveFile, ISaveBlock8LA, ISCBlockArray
|
|||
var blockCopy = new SCBlock[AllBlocks.Count];
|
||||
for (int i = 0; i < AllBlocks.Count; i++)
|
||||
blockCopy[i] = AllBlocks[i].Clone();
|
||||
return new SAV8LA(State.BAK, blockCopy);
|
||||
return new SAV8LA(blockCopy);
|
||||
}
|
||||
|
||||
public override int MaxMoveID => Legal.MaxMoveID_8a;
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace PKHeX.Core
|
|||
Initialize();
|
||||
}
|
||||
|
||||
private SAV8SWSH(byte[] data, IReadOnlyList<SCBlock> blocks) : base(data)
|
||||
private SAV8SWSH(IReadOnlyList<SCBlock> blocks) : base(Array.Empty<byte>())
|
||||
{
|
||||
Data = Array.Empty<byte>();
|
||||
AllBlocks = blocks;
|
||||
|
@ -109,7 +109,7 @@ namespace PKHeX.Core
|
|||
var blockCopy = new SCBlock[AllBlocks.Count];
|
||||
for (int i = 0; i < AllBlocks.Count; i++)
|
||||
blockCopy[i] = AllBlocks[i].Clone();
|
||||
return new SAV8SWSH(State.BAK, blockCopy);
|
||||
return new SAV8SWSH(blockCopy);
|
||||
}
|
||||
|
||||
private int m_spec, m_item, m_move, m_abil;
|
||||
|
|
|
@ -16,21 +16,17 @@ namespace PKHeX.Core
|
|||
public SaveFileState State { get; }
|
||||
public SaveFileMetadata Metadata { get; private set; }
|
||||
|
||||
protected SaveFile(byte[] data, byte[] bak, bool exportable = true)
|
||||
protected SaveFile(byte[] data, bool exportable = true)
|
||||
{
|
||||
Data = data;
|
||||
State = new SaveFileState(bak, exportable);
|
||||
State = new SaveFileState(exportable);
|
||||
Metadata = new SaveFileMetadata(this);
|
||||
}
|
||||
|
||||
protected SaveFile(byte[] data, bool exportable = true) : this(data, (byte[])data.Clone(), exportable)
|
||||
{
|
||||
}
|
||||
|
||||
protected SaveFile(int size = 0)
|
||||
{
|
||||
Data = size == 0 ? Array.Empty<byte>() : new byte[size];
|
||||
State = new SaveFileState(Array.Empty<byte>(), false);
|
||||
State = new SaveFileState(false);
|
||||
Metadata = new SaveFileMetadata(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,8 @@
|
|||
/// </remarks>
|
||||
public readonly bool Exportable;
|
||||
|
||||
/// <summary>
|
||||
/// Original Binary Data the save file was loaded with.
|
||||
/// </summary>
|
||||
public readonly byte[] BAK;
|
||||
|
||||
public SaveFileState(byte[] bak, bool exportable = true)
|
||||
public SaveFileState(bool exportable = true)
|
||||
{
|
||||
BAK = bak;
|
||||
Exportable = exportable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -650,9 +650,9 @@ namespace PKHeX.Core
|
|||
switch (memCard.SelectedGameVersion)
|
||||
{
|
||||
// Side Games
|
||||
case COLO: sav = new SAV3Colosseum(data, memCard); break;
|
||||
case XD: sav = new SAV3XD(data, memCard); break;
|
||||
case RSBOX: sav = new SAV3RSBox(data, memCard); break;
|
||||
case COLO: sav = new SAV3Colosseum(data) { MemoryCard = memCard }; break;
|
||||
case XD: sav = new SAV3XD(data) { MemoryCard = memCard }; break;
|
||||
case RSBOX: sav = new SAV3RSBox(data, memCard) { MemoryCard = memCard }; break;
|
||||
|
||||
// No pattern matched
|
||||
default: return null;
|
||||
|
@ -821,19 +821,5 @@ namespace PKHeX.Core
|
|||
/// <param name="size">Size in bytes of the save data</param>
|
||||
/// <returns>A boolean indicating whether or not the save data size is valid.</returns>
|
||||
public static bool IsSizeValid(int size) => Sizes.Contains(size) || Handlers.Any(z => z.IsRecognized(size));
|
||||
|
||||
/// <summary>
|
||||
/// Force loads the provided <see cref="sav"/> to the requested <see cref="version"/>.
|
||||
/// </summary>
|
||||
/// <param name="sav">SaveFile data to force</param>
|
||||
/// <param name="version"> Version to retrieve for</param>
|
||||
/// <returns>New <see cref="SaveFile"/> object.</returns>
|
||||
public static SAV3 GetG3SaveOverride(SaveFile sav, GameVersion version) => version switch // Reset save file info
|
||||
{
|
||||
R or S or RS => new SAV3RS(sav.State.BAK),
|
||||
E => new SAV3E(sav.State.BAK),
|
||||
FR or LG or FRLG => new SAV3FRLG(sav.State.BAK),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(version)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -778,14 +778,14 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
public bool ExportBackup()
|
||||
{
|
||||
if (!SAV.State.Exportable)
|
||||
if (!SAV.State.Exportable || SAV.Metadata.FilePath is not { } file)
|
||||
return false;
|
||||
using var sfd = new SaveFileDialog {FileName = Util.CleanFileName(SAV.Metadata.BAKName)};
|
||||
if (sfd.ShowDialog() != DialogResult.OK)
|
||||
return false;
|
||||
|
||||
string path = sfd.FileName;
|
||||
File.WriteAllBytes(path, SAV.State.BAK);
|
||||
File.Copy(file, path);
|
||||
WinFormsUtil.Alert(MsgSaveBackup, path);
|
||||
|
||||
return true;
|
||||
|
@ -1110,7 +1110,8 @@ namespace PKHeX.WinForms.Controls
|
|||
{
|
||||
// Generational Interface
|
||||
ToggleSecrets(sav, HideSecretDetails);
|
||||
B_VerifyCHK.Visible = Menu_ExportBAK.Visible = SAV.State.Exportable;
|
||||
B_VerifyCHK.Visible = SAV.State.Exportable;
|
||||
Menu_ExportBAK.Visible = SAV.State.Exportable && SAV.Metadata.FilePath is not null;
|
||||
|
||||
if (sav is SAV4BR br)
|
||||
{
|
||||
|
|
|
@ -821,8 +821,11 @@ namespace PKHeX.WinForms
|
|||
// If backup folder exists, save a backup.
|
||||
string backupName = Path.Combine(BackupPath, Util.CleanFileName(sav.Metadata.BAKName));
|
||||
if (sav.State.Exportable && Directory.Exists(BackupPath) && !File.Exists(backupName))
|
||||
File.WriteAllBytes(backupName, sav.State.BAK);
|
||||
|
||||
{
|
||||
var src = sav.Metadata.FilePath;
|
||||
if (src is { } x && File.Exists(x))
|
||||
File.Copy(x, backupName);
|
||||
}
|
||||
if (!FileUtil.IsFileLocked(path))
|
||||
return true;
|
||||
|
||||
|
@ -846,18 +849,17 @@ namespace PKHeX.WinForms
|
|||
if (dialog.Result is GameVersion.Invalid)
|
||||
return false;
|
||||
|
||||
var s = SaveUtil.GetG3SaveOverride(sav, dialog.Result);
|
||||
var origin = s3.Metadata.FilePath;
|
||||
if (origin is not null)
|
||||
s.Metadata.SetExtraInfo(origin);
|
||||
|
||||
sav = s;
|
||||
if (sav is SAV3FRLG frlg)
|
||||
var s = s3.ForceLoad(dialog.Result);
|
||||
if (s is SAV3FRLG frlg)
|
||||
{
|
||||
bool result = frlg.ResetPersonal(dialog.Result);
|
||||
if (!result)
|
||||
return false;
|
||||
}
|
||||
var origin = sav.Metadata.FilePath;
|
||||
if (origin is not null)
|
||||
s.Metadata.SetExtraInfo(origin);
|
||||
sav = s;
|
||||
}
|
||||
else if (s3 is SAV3FRLG frlg) // IndeterminateSubVersion
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue