diff --git a/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs b/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs index e629f2814..9481b86ba 100644 --- a/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs +++ b/PKHeX.Core/Editing/Saves/Editors/SaveDataEditor.cs @@ -50,9 +50,8 @@ public sealed class FakePKMEditor : IPKMView public sealed class FakeSaveFile : SaveFile { public static readonly FakeSaveFile Default = new FakeSaveFile(); - protected override string BAKText => "Fake Save File"; - public override SaveFile Clone() => this; - public override string Filter => string.Empty; + protected internal override string ShortSummary => "Fake Save File"; + protected override SaveFile CloneInternal() => this; public override string Extension => string.Empty; public override bool ChecksumsValid => true; public override string ChecksumInfo => string.Empty; diff --git a/PKHeX.Core/Editing/Saves/Management/INamedFolderPath.cs b/PKHeX.Core/Editing/Saves/Management/INamedFolderPath.cs new file mode 100644 index 000000000..3803c188a --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Management/INamedFolderPath.cs @@ -0,0 +1,9 @@ +namespace PKHeX.Core +{ + public interface INamedFolderPath + { + string Path { get; } + string DisplayText { get; } + bool Custom { get; } + } +} diff --git a/PKHeX.Core/Editing/Saves/Management/SavePreview.cs b/PKHeX.Core/Editing/Saves/Management/SavePreview.cs new file mode 100644 index 000000000..3b2838683 --- /dev/null +++ b/PKHeX.Core/Editing/Saves/Management/SavePreview.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.IO; + +namespace PKHeX.Core +{ + /// + /// Bindable Summary of a for use in a sortable table. + /// + public sealed class SavePreview + { + public readonly SaveFile Save; + public readonly string FilePath; + + public SavePreview(SaveFile sav, string parent, string path) + { + Save = sav; + Folder = parent; + FilePath = path; + } + + public SavePreview(SaveFile sav, List paths) + { + var meta = sav.Metadata; + var dir = meta.FileFolder; + const string notFound = "???"; + var parent = dir == null ? notFound : paths.Find(z => dir.StartsWith(z.Path))?.DisplayText ?? notFound; + + Save = sav; + Folder = parent; + FilePath = meta.FilePath ?? notFound; + } + + public string OT => Save.OT; + public int G => Save.Generation; + public GameVersion Game => Save.Version; + + public string Played => Save.PlayTimeString.PadLeft(9, '0'); + public string FileTime => File.GetLastWriteTimeUtc(FilePath).ToString("yyyy.MM.dd:hh:mm:ss"); + + public string TID => Save.Generation >= 7 ? Save.TrainerID7.ToString("000000") : Save.TID.ToString("00000"); + public string SID => Save.Generation >= 7 ? Save.TrainerSID7.ToString("0000") : Save.SID.ToString("00000"); + + // ReSharper disable once MemberCanBePrivate.Local + // ReSharper disable once UnusedAutoPropertyAccessor.Local + public string Folder { get; } + + public string Name => Path.GetFileName(FilePath); + } +} diff --git a/PKHeX.Core/Legality/Verifiers/ParseSettings.cs b/PKHeX.Core/Legality/Verifiers/ParseSettings.cs index d82bdb2e4..434edfd42 100644 --- a/PKHeX.Core/Legality/Verifiers/ParseSettings.cs +++ b/PKHeX.Core/Legality/Verifiers/ParseSettings.cs @@ -74,7 +74,7 @@ public static bool InitFromSaveFileData(SaveFile sav) ActiveTrainer = sav; if (sav.Generation >= 3) return AllowGBCartEra = false; - bool vc = !sav.Exportable || (sav.FileName?.EndsWith("dat") ?? false); // default to true for non-exportable + bool vc = !sav.State.Exportable || (sav.Metadata.FileName?.EndsWith("dat") ?? false); // default to true for non-exportable return AllowGBCartEra = !vc; // physical cart selected } } diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index c06e69dcd..baae3435c 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV1 : SaveFile, ILangDeviantSave { - protected override string BAKText => $"{OT} ({Version}) - {PlayTimeString}"; - public override string Filter => "SAV File|*.sav|All Files|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; public int SaveRevision => Japanese ? 0 : 1; @@ -184,7 +183,7 @@ private int GetBoxRawDataOffset(int i) } // Configuration - public override SaveFile Clone() => new SAV1(Write(), Version); + protected override SaveFile CloneInternal() => new SAV1(Write(), Version); protected override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST; protected override int SIZE_PARTY => Japanese ? PokeCrypto.SIZE_1JLIST : PokeCrypto.SIZE_1ULIST; diff --git a/PKHeX.Core/Saves/SAV1Stadium.cs b/PKHeX.Core/Saves/SAV1Stadium.cs index 97f98022a..1f9af6abe 100644 --- a/PKHeX.Core/Saves/SAV1Stadium.cs +++ b/PKHeX.Core/Saves/SAV1Stadium.cs @@ -16,7 +16,7 @@ public sealed class SAV1Stadium : SAV_STADIUM public override IReadOnlyList HeldItems => Array.Empty(); public override GameVersion Version { get; protected set; } = GameVersion.Stadium; - public override SaveFile Clone() => new SAV1Stadium((byte[])Data.Clone(), Japanese); + protected override SaveFile CloneInternal() => new SAV1Stadium((byte[])Data.Clone(), Japanese); public override int Generation => 1; private int StringLength => Japanese ? StringLengthJ : StringLengthU; diff --git a/PKHeX.Core/Saves/SAV1StadiumJ.cs b/PKHeX.Core/Saves/SAV1StadiumJ.cs index f6a3f250c..32a93a528 100644 --- a/PKHeX.Core/Saves/SAV1StadiumJ.cs +++ b/PKHeX.Core/Saves/SAV1StadiumJ.cs @@ -17,7 +17,7 @@ public sealed class SAV1StadiumJ : SAV_STADIUM public override IReadOnlyList HeldItems => Array.Empty(); public override GameVersion Version { get; protected set; } = GameVersion.StadiumJ; - public override SaveFile Clone() => new SAV1StadiumJ((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV1StadiumJ((byte[])Data.Clone()); public override int Generation => 1; private const int StringLength = 6; // Japanese Only diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index b30e9f7dd..a2a8e5ae5 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV2 : SaveFile, ILangDeviantSave { - protected override string BAKText => $"{OT} ({Version}) - {PlayTimeString}"; - public override string Filter => "SAV File|*.sav|All Files|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; public int SaveRevision => Japanese ? 0 : !Korean ? 1 : 2; @@ -247,7 +246,7 @@ protected override byte[] GetFinalData() } // Configuration - public override SaveFile Clone() => new SAV2(Write(), Version); + protected override SaveFile CloneInternal() => new SAV2(Write(), Version); protected override int SIZE_STORED => Japanese ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST; protected override int SIZE_PARTY => Japanese ? PokeCrypto.SIZE_2JLIST : PokeCrypto.SIZE_2ULIST; diff --git a/PKHeX.Core/Saves/SAV2Stadium.cs b/PKHeX.Core/Saves/SAV2Stadium.cs index 95ec8b045..87efac0bb 100644 --- a/PKHeX.Core/Saves/SAV2Stadium.cs +++ b/PKHeX.Core/Saves/SAV2Stadium.cs @@ -16,7 +16,7 @@ public sealed class SAV2Stadium : SAV_STADIUM public override IReadOnlyList HeldItems => Legal.HeldItems_GSC; public override GameVersion Version { get; protected set; } = GameVersion.Stadium2; - public override SaveFile Clone() => new SAV1Stadium((byte[])Data.Clone(), Japanese); + protected override SaveFile CloneInternal() => new SAV1Stadium((byte[])Data.Clone(), Japanese); public override int Generation => 2; private const int StringLength = 12; diff --git a/PKHeX.Core/Saves/SAV3.cs b/PKHeX.Core/Saves/SAV3.cs index dd22663ec..67d5424d8 100644 --- a/PKHeX.Core/Saves/SAV3.cs +++ b/PKHeX.Core/Saves/SAV3.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV3 : SaveFile, ILangDeviantSave { - protected override string BAKText => $"{OT} ({Version}) - {PlayTimeString}"; - public override string Filter => "SAV File|*.sav|All Files|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; public int SaveRevision => Japanese ? 0 : 1; @@ -285,7 +284,7 @@ protected override byte[] GetFinalData() public int GetBlockOffset(int block) => BlockOfs[block]; // Configuration - public override SaveFile Clone() => new SAV3(Write(), Version); + protected override SaveFile CloneInternal() => new SAV3(Write(), Version); protected override int SIZE_STORED => PokeCrypto.SIZE_3STORED; protected override int SIZE_PARTY => PokeCrypto.SIZE_3PARTY; @@ -331,7 +330,7 @@ protected override void SetChecksums() BitConverter.GetBytes(chk).CopyTo(Data, ofs + 0xFF6); } - if (BAK.Length < SaveUtil.SIZE_G3RAW) // don't update HoF for half-sizes + if (State.BAK.Length < SaveUtil.SIZE_G3RAW) // don't update HoF for half-sizes return; // Hall of Fame Checksums @@ -355,7 +354,7 @@ public override bool ChecksumsValid return false; } - if (BAK.Length < SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes + if (State.BAK.Length < SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes return true; if (!IsChunkValidHoF(0x1C000)) @@ -391,7 +390,7 @@ public override string ChecksumInfo list.Add($"Block {BlockOrder[i]:00} @ {i*SIZE_BLOCK:X5} invalid."); } - if (BAK.Length > SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes + if (State.BAK.Length > SaveUtil.SIZE_G3RAW) // don't check HoF for half-sizes { if (!IsChunkValidHoF(0x1C000)) list.Add("HoF Block 1 invalid."); diff --git a/PKHeX.Core/Saves/SAV3Colosseum.cs b/PKHeX.Core/Saves/SAV3Colosseum.cs index 80b68da28..9b5244be2 100644 --- a/PKHeX.Core/Saves/SAV3Colosseum.cs +++ b/PKHeX.Core/Saves/SAV3Colosseum.cs @@ -10,8 +10,7 @@ namespace PKHeX.Core /// public sealed class SAV3Colosseum : SaveFile, IDisposable, IGCSaveFile { - protected override string BAKText => $"{OT} ({Version}) - {PlayTimeString}"; - public override string Filter => this.GCFilter(); + protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => this.GCExtension(); public override PersonalTable Personal => PersonalTable.RS; public override IReadOnlyList HeldItems => Legal.HeldItems_COLO; @@ -120,17 +119,16 @@ private byte[] GetInnerData() byte[] newSAV = EncryptColosseum(Data, digest); // Put save slot back in original save data - byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[])BAK.Clone(); + byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[]) State.BAK.Clone(); Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length); return newFile; } // Configuration - public override SaveFile Clone() + protected override SaveFile CloneInternal() { var data = GetInnerData(); var sav = IsMemoryCardSave ? new SAV3Colosseum(data, MC!) : new SAV3Colosseum(data); - sav.Header = (byte[])Header.Clone(); return sav; } diff --git a/PKHeX.Core/Saves/SAV3RSBox.cs b/PKHeX.Core/Saves/SAV3RSBox.cs index 46c3a16b5..8a097d723 100644 --- a/PKHeX.Core/Saves/SAV3RSBox.cs +++ b/PKHeX.Core/Saves/SAV3RSBox.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV3RSBox : SaveFile, IGCSaveFile { - protected override string BAKText => $"{Version} #{SaveCount:0000}"; - public override string Filter => this.GCFilter(); + protected internal override string ShortSummary => $"{Version} #{SaveCount:0000}"; public override string Extension => this.GCExtension(); public override PersonalTable Personal => PersonalTable.RS; public override IReadOnlyList HeldItems => Legal.HeldItems_RS; @@ -95,11 +94,10 @@ private byte[] GetInnerData() } // Configuration - public override SaveFile Clone() + protected override SaveFile CloneInternal() { var data = GetInnerData(); var sav = IsMemoryCardSave ? new SAV3RSBox(data, MC!) : new SAV3RSBox(data); - sav.Header = (byte[])Header.Clone(); return sav; } diff --git a/PKHeX.Core/Saves/SAV3XD.cs b/PKHeX.Core/Saves/SAV3XD.cs index 1aa99771b..2600bef99 100644 --- a/PKHeX.Core/Saves/SAV3XD.cs +++ b/PKHeX.Core/Saves/SAV3XD.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV3XD : SaveFile, IGCSaveFile { - protected override string BAKText => $"{OT} ({Version}) #{SaveCount:0000}"; - public override string Filter => this.GCFilter(); + protected internal override string ShortSummary => $"{OT} ({Version}) #{SaveCount:0000}"; public override string Extension => this.GCExtension(); public bool IsMemoryCardSave => MC != null; private readonly SAV3GCMemoryCard? MC; @@ -144,17 +143,16 @@ private byte[] GetInnerData() byte[] newSAV = GCSaveUtil.Encrypt(Data, 0x10, 0x27FD8, keys); // Put save slot back in original save data - byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[])BAK.Clone(); + byte[] newFile = MC != null ? MC.SelectedSaveData : (byte[]) State.BAK.Clone(); Array.Copy(newSAV, 0, newFile, SLOT_START + (SaveIndex * SLOT_SIZE), newSAV.Length); return newFile; } // Configuration - public override SaveFile Clone() + protected override SaveFile CloneInternal() { var data = GetInnerData(); var sav = IsMemoryCardSave ? new SAV3XD(data, MC!) : new SAV3XD(data); - sav.Header = (byte[]) Header.Clone(); return sav; } diff --git a/PKHeX.Core/Saves/SAV4.cs b/PKHeX.Core/Saves/SAV4.cs index 46a99fbd6..a78408875 100644 --- a/PKHeX.Core/Saves/SAV4.cs +++ b/PKHeX.Core/Saves/SAV4.cs @@ -12,8 +12,7 @@ namespace PKHeX.Core /// public abstract class SAV4 : SaveFile { - protected override string BAKText => $"{OT} ({Version}) - {PlayTimeString}"; - public override string Filter => (Footer.Length > 0 ? "DeSmuME DSV|*.dsv|" : string.Empty) + "SAV File|*.sav|All Files|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {PlayTimeString}"; public override string Extension => ".sav"; // Blocks & Offsets @@ -59,16 +58,15 @@ protected SAV4(byte[] data) : base(data) } // Configuration - public override SaveFile Clone() + protected override SaveFile CloneInternal() { - var sav = CloneInternal(); + var sav = CloneInternal4(); SetData(sav.General, General, 0); SetData(sav.Storage, Storage, 0); - sav.Footer = (byte[])Footer.Clone(); return sav; } - protected abstract SAV4 CloneInternal(); + protected abstract SAV4 CloneInternal4(); public override void CopyChangesFrom(SaveFile sav) { diff --git a/PKHeX.Core/Saves/SAV4BR.cs b/PKHeX.Core/Saves/SAV4BR.cs index 9b9418ad3..781443e93 100644 --- a/PKHeX.Core/Saves/SAV4BR.cs +++ b/PKHeX.Core/Saves/SAV4BR.cs @@ -10,8 +10,7 @@ namespace PKHeX.Core /// public sealed class SAV4BR : SaveFile { - protected override string BAKText => $"{Version} #{SaveCount:0000}"; - public override string Filter => "PbrSaveData|*"; + protected internal override string ShortSummary => $"{Version} #{SaveCount:0000}"; public override string Extension => string.Empty; public override PersonalTable Personal => PersonalTable.DP; public override IReadOnlyList HeldItems => Legal.HeldItems_DP; @@ -67,7 +66,7 @@ protected override byte[] GetFinalData() } // Configuration - public override SaveFile Clone() => new SAV4BR(Write()); + protected override SaveFile CloneInternal() => new SAV4BR(Write()); public readonly List SaveSlots = new List(SAVE_COUNT); public readonly List SaveNames = new List(SAVE_COUNT); diff --git a/PKHeX.Core/Saves/SAV4DP.cs b/PKHeX.Core/Saves/SAV4DP.cs index ba824925d..5a00a0af8 100644 --- a/PKHeX.Core/Saves/SAV4DP.cs +++ b/PKHeX.Core/Saves/SAV4DP.cs @@ -10,7 +10,7 @@ public sealed class SAV4DP : SAV4Sinnoh { public SAV4DP() => Initialize(); public SAV4DP(byte[] data) : base(data) => Initialize(); - protected override SAV4 CloneInternal() => Exportable ? new SAV4DP(Data) : new SAV4DP(); + protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4DP(Data) : new SAV4DP(); public override PersonalTable Personal => PersonalTable.DP; public override IReadOnlyList HeldItems => Legal.HeldItems_DP; diff --git a/PKHeX.Core/Saves/SAV4HGSS.cs b/PKHeX.Core/Saves/SAV4HGSS.cs index 6330f1d70..eecd7c8e3 100644 --- a/PKHeX.Core/Saves/SAV4HGSS.cs +++ b/PKHeX.Core/Saves/SAV4HGSS.cs @@ -10,7 +10,7 @@ public sealed class SAV4HGSS : SAV4 { public SAV4HGSS() => Initialize(); public SAV4HGSS(byte[] data) : base(data) => Initialize(); - protected override SAV4 CloneInternal() => Exportable ? new SAV4HGSS(Data) : new SAV4HGSS(); + protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4HGSS(Data) : new SAV4HGSS(); public override PersonalTable Personal => PersonalTable.HGSS; public override IReadOnlyList HeldItems => Legal.HeldItems_HGSS; diff --git a/PKHeX.Core/Saves/SAV4Pt.cs b/PKHeX.Core/Saves/SAV4Pt.cs index 859284ba3..ab53d4d88 100644 --- a/PKHeX.Core/Saves/SAV4Pt.cs +++ b/PKHeX.Core/Saves/SAV4Pt.cs @@ -9,7 +9,7 @@ public sealed class SAV4Pt : SAV4Sinnoh { public SAV4Pt() => Initialize(); public SAV4Pt(byte[] data) : base(data) => Initialize(); - protected override SAV4 CloneInternal() => Exportable ? new SAV4Pt(Data) : new SAV4Pt(); + protected override SAV4 CloneInternal4() => State.Exportable ? new SAV4Pt(Data) : new SAV4Pt(); public override PersonalTable Personal => PersonalTable.Pt; public override IReadOnlyList HeldItems => Legal.HeldItems_Pt; diff --git a/PKHeX.Core/Saves/SAV5.cs b/PKHeX.Core/Saves/SAV5.cs index a39d03516..19fcfa891 100644 --- a/PKHeX.Core/Saves/SAV5.cs +++ b/PKHeX.Core/Saves/SAV5.cs @@ -11,8 +11,7 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW protected override PKM GetPKM(byte[] data) => new PK5(data); protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data); - protected override string BAKText => $"{OT} ({(GameVersion)Game}) - {PlayTimeString}"; - public override string Filter => (Footer.Length != 0 ? "DeSmuME DSV|*.dsv|" : string.Empty) + "SAV File|*.sav|All Files|*.*"; + protected internal override string ShortSummary => $"{OT} ({(GameVersion)Game}) - {PlayTimeString}"; public override string Extension => ".sav"; public override IReadOnlyList HeldItems => Legal.HeldItems_BW; @@ -185,7 +184,7 @@ public byte[] CGearSkinData chkbytes.CopyTo(Data, CGearSkinInfoOffset); CGearSkinPresent = true; - Edited = true; + State.Edited = true; } } diff --git a/PKHeX.Core/Saves/SAV5B2W2.cs b/PKHeX.Core/Saves/SAV5B2W2.cs index 0f6e1bd84..d974d0966 100644 --- a/PKHeX.Core/Saves/SAV5B2W2.cs +++ b/PKHeX.Core/Saves/SAV5B2W2.cs @@ -18,7 +18,7 @@ public SAV5B2W2(byte[] data) : base(data) public override PersonalTable Personal => PersonalTable.B2W2; public SaveBlockAccessor5B2W2 Blocks { get; } - public override SaveFile Clone() => new SAV5B2W2((byte[])Data.Clone()) { Footer = (byte[])Footer.Clone() }; + protected override SaveFile CloneInternal() => new SAV5B2W2((byte[]) Data.Clone()); protected override int EventConstMax => 0x1AF; // this doesn't seem right? protected override int EventFlagMax => 0xBF8; public override int MaxItemID => Legal.MaxItemID_5_B2W2; diff --git a/PKHeX.Core/Saves/SAV5BW.cs b/PKHeX.Core/Saves/SAV5BW.cs index 0714b8832..ceba12ea5 100644 --- a/PKHeX.Core/Saves/SAV5BW.cs +++ b/PKHeX.Core/Saves/SAV5BW.cs @@ -18,7 +18,7 @@ public SAV5BW(byte[] data) : base(data) public override PersonalTable Personal => PersonalTable.BW; public SaveBlockAccessor5BW Blocks { get; } - public override SaveFile Clone() => new SAV5BW((byte[])Data.Clone()) { Footer = (byte[])Footer.Clone() }; + protected override SaveFile CloneInternal() => new SAV5BW((byte[])Data.Clone()); protected override int EventConstMax => 0x13E; protected override int EventFlagMax => 0xB60; public override int MaxItemID => Legal.MaxItemID_5_BW; diff --git a/PKHeX.Core/Saves/SAV6.cs b/PKHeX.Core/Saves/SAV6.cs index c68dcb3a8..d0e46b3f6 100644 --- a/PKHeX.Core/Saves/SAV6.cs +++ b/PKHeX.Core/Saves/SAV6.cs @@ -8,8 +8,7 @@ namespace PKHeX.Core public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IRegionOrigin, IGameSync { // Save Data Attributes - protected override string BAKText => $"{OT} ({Version}) - {Played.LastSavedTime}"; - public override string Filter => "Main SAV|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; public override string Extension => string.Empty; protected SAV6(byte[] data, int biOffset) : base(data, biOffset) { } diff --git a/PKHeX.Core/Saves/SAV6AO.cs b/PKHeX.Core/Saves/SAV6AO.cs index 6db65058b..ca4585870 100644 --- a/PKHeX.Core/Saves/SAV6AO.cs +++ b/PKHeX.Core/Saves/SAV6AO.cs @@ -27,7 +27,7 @@ public SAV6AO() : base(SaveUtil.SIZE_G6ORAS, SaveBlockAccessor6AO.boAO) public override PersonalTable Personal => PersonalTable.AO; public override IReadOnlyList HeldItems => Legal.HeldItem_AO; public SaveBlockAccessor6AO Blocks { get; } - public override SaveFile Clone() => new SAV6AO((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV6AO((byte[])Data.Clone()); public override int MaxMoveID => Legal.MaxMoveID_6_AO; public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; diff --git a/PKHeX.Core/Saves/SAV6AODemo.cs b/PKHeX.Core/Saves/SAV6AODemo.cs index 7b8bc7847..d62a8bebd 100644 --- a/PKHeX.Core/Saves/SAV6AODemo.cs +++ b/PKHeX.Core/Saves/SAV6AODemo.cs @@ -22,7 +22,7 @@ public SAV6AODemo() : base(SaveUtil.SIZE_G6ORASDEMO, SaveBlockAccessor6AODemo.bo public override PersonalTable Personal => PersonalTable.AO; public override IReadOnlyList HeldItems => Legal.HeldItem_AO; - public override SaveFile Clone() => new SAV6AODemo((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV6AODemo((byte[])Data.Clone()); public override int MaxMoveID => Legal.MaxMoveID_6_AO; public override int MaxItemID => Legal.MaxItemID_6_AO; public override int MaxAbilityID => Legal.MaxAbilityID_6_AO; diff --git a/PKHeX.Core/Saves/SAV6XY.cs b/PKHeX.Core/Saves/SAV6XY.cs index bae0a0e70..681e9475d 100644 --- a/PKHeX.Core/Saves/SAV6XY.cs +++ b/PKHeX.Core/Saves/SAV6XY.cs @@ -27,7 +27,7 @@ public SAV6XY() : base(SaveUtil.SIZE_G6XY, SaveBlockAccessor6XY.boXY) public override PersonalTable Personal => PersonalTable.XY; public override IReadOnlyList HeldItems => Legal.HeldItem_XY; public SaveBlockAccessor6XY Blocks { get; } - public override SaveFile Clone() => new SAV6XY((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV6XY((byte[])Data.Clone()); public override int MaxMoveID => Legal.MaxMoveID_6_XY; public override int MaxItemID => Legal.MaxItemID_6_XY; public override int MaxAbilityID => Legal.MaxAbilityID_6_XY; @@ -133,7 +133,8 @@ public void UnlockAllFriendSafariSlots() if (Data[ofs] != 0) // no friend data == 0x00 Data[ofs] = 0x3D; } - Edited = true; + + State.Edited = true; } public override GameVersion Version diff --git a/PKHeX.Core/Saves/SAV7.cs b/PKHeX.Core/Saves/SAV7.cs index f77a7a748..df148d3f8 100644 --- a/PKHeX.Core/Saves/SAV7.cs +++ b/PKHeX.Core/Saves/SAV7.cs @@ -11,8 +11,7 @@ namespace PKHeX.Core public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IRegionOrigin { // Save Data Attributes - protected override string BAKText => $"{OT} ({Version}) - {Played.LastSavedTime}"; - public override string Filter => "Main SAV|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; public override string Extension => string.Empty; public override IReadOnlyList PKMExtensions => PKM.Extensions.Where(f => @@ -32,7 +31,7 @@ protected SAV7(int size, int biOffset) : base(size, biOffset) protected void ReloadBattleTeams() { var demo = this is SAV7SM && Data.IsRangeAll((byte)0, BoxLayout.Offset, 0x4C4); // up to Battle Box values - if (demo || !Exportable) + if (demo || !State.Exportable) { BoxLayout.ClearBattleTeams(); } diff --git a/PKHeX.Core/Saves/SAV7SM.cs b/PKHeX.Core/Saves/SAV7SM.cs index d4e2a40f5..adb6cf8eb 100644 --- a/PKHeX.Core/Saves/SAV7SM.cs +++ b/PKHeX.Core/Saves/SAV7SM.cs @@ -37,7 +37,7 @@ private void Initialize() public override PersonalTable Personal => PersonalTable.SM; public override IReadOnlyList HeldItems => Legal.HeldItems_SM; - public override SaveFile Clone() => new SAV7SM((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV7SM((byte[])Data.Clone()); #region Blocks public SaveBlockAccessor7SM Blocks { get; } diff --git a/PKHeX.Core/Saves/SAV7USUM.cs b/PKHeX.Core/Saves/SAV7USUM.cs index 3cc1446c8..84da88a37 100644 --- a/PKHeX.Core/Saves/SAV7USUM.cs +++ b/PKHeX.Core/Saves/SAV7USUM.cs @@ -35,7 +35,7 @@ private void Initialize() public override PersonalTable Personal => PersonalTable.USUM; public override IReadOnlyList HeldItems => Legal.HeldItems_USUM; - public override SaveFile Clone() => new SAV7USUM((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV7USUM((byte[])Data.Clone()); protected override int EventFlagMax => 4928; public override int MaxMoveID => Legal.MaxMoveID_7_USUM; public override int MaxSpeciesID => Legal.MaxSpeciesID_7_USUM; diff --git a/PKHeX.Core/Saves/SAV7b.cs b/PKHeX.Core/Saves/SAV7b.cs index 962289f6a..05769cc7c 100644 --- a/PKHeX.Core/Saves/SAV7b.cs +++ b/PKHeX.Core/Saves/SAV7b.cs @@ -9,8 +9,7 @@ namespace PKHeX.Core /// public sealed class SAV7b : SAV_BEEF, IGameSync { - protected override string BAKText => $"{OT} ({Version}) - {Blocks.Played.LastSavedTime}"; - public override string Filter => "savedata|*.bin"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {Blocks.Played.LastSavedTime}"; public override string Extension => ".bin"; public override IReadOnlyList PKMExtensions => PKM.Extensions.Where(f => f[1] == 'b' && f[f.Length - 1] == '7').ToArray(); @@ -24,7 +23,7 @@ public sealed class SAV7b : SAV_BEEF, IGameSync public override PersonalTable Personal => PersonalTable.GG; public override IReadOnlyList HeldItems => Legal.HeldItems_GG; - public override SaveFile Clone() => new SAV7b((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV7b((byte[])Data.Clone()); public SaveBlockAccessor7b Blocks { get; } public override IReadOnlyList AllBlocks => Blocks.BlockInfo; diff --git a/PKHeX.Core/Saves/SAV8.cs b/PKHeX.Core/Saves/SAV8.cs index 3daca725e..dc74d7c6a 100644 --- a/PKHeX.Core/Saves/SAV8.cs +++ b/PKHeX.Core/Saves/SAV8.cs @@ -10,8 +10,7 @@ namespace PKHeX.Core public abstract class SAV8 : SaveFile, ISaveBlock8Main { // Save Data Attributes - protected override string BAKText => $"{OT} ({Version}) - {Played.LastSavedTime}"; - public override string Filter => "Main SAV|*.*"; + protected internal override string ShortSummary => $"{OT} ({Version}) - {Played.LastSavedTime}"; public override string Extension => string.Empty; public override IReadOnlyList PKMExtensions => PKM.Extensions.Where(f => diff --git a/PKHeX.Core/Saves/SAV8SWSH.cs b/PKHeX.Core/Saves/SAV8SWSH.cs index c374116ac..640333a9a 100644 --- a/PKHeX.Core/Saves/SAV8SWSH.cs +++ b/PKHeX.Core/Saves/SAV8SWSH.cs @@ -39,7 +39,7 @@ public override void CopyChangesFrom(SaveFile sav) var newB = z.AllBlocks; for (int i = 0; i < mine.Count; i++) newB[i].Data.CopyTo(mine[i].Data, 0); - Edited = true; + State.Edited = true; } public int SaveRevision { get; } @@ -83,7 +83,7 @@ public override void CopyChangesFrom(SaveFile sav) public T GetValue(uint key) where T : struct { - if (!Exportable) + if (!State.Exportable) return default; var value = Blocks.GetBlockValue(key); if (value is T v) @@ -93,13 +93,13 @@ public override void CopyChangesFrom(SaveFile sav) public void SetValue(uint key, T value) where T : struct { - if (!Exportable) + if (!State.Exportable) return; Blocks.SetBlockValue(key, value); } #endregion - public override SaveFile Clone() => new SAV8SWSH(BAK, AllBlocks.Select(z => z.Clone()).ToArray()); + protected override SaveFile CloneInternal() => new SAV8SWSH(State.BAK, AllBlocks.Select(z => z.Clone()).ToArray()); private int m_spec, m_item, m_move, m_abil; public override int MaxMoveID => m_move; diff --git a/PKHeX.Core/Saves/SAV_STADIUM.cs b/PKHeX.Core/Saves/SAV_STADIUM.cs index 7b21f179f..0745d4831 100644 --- a/PKHeX.Core/Saves/SAV_STADIUM.cs +++ b/PKHeX.Core/Saves/SAV_STADIUM.cs @@ -5,8 +5,7 @@ /// public abstract class SAV_STADIUM : SaveFile, ILangDeviantSave { - protected sealed override string BAKText => $"{OT} ({Version})"; - public sealed override string Filter => "SAV File|*.sav|All Files|*.*"; + protected internal sealed override string ShortSummary => $"{OT} ({Version})"; public sealed override string Extension => ".sav"; public abstract int SaveRevision { get; } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 2de7c40a2..7edf99df7 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -5,6 +5,19 @@ namespace PKHeX.Core { + public class SaveFileState + { + public bool Edited { get; set; } + public readonly bool Exportable; + public readonly byte[] BAK; + + public SaveFileState(byte[] bak, bool exportable = true) + { + BAK = bak; + Exportable = exportable; + } + } + /// /// Base Class for Save Files /// @@ -12,42 +25,42 @@ public abstract class SaveFile : ITrainerInfo, IGameValueLimit, IBoxDetailWallpa { // General Object Properties public byte[] Data; - public bool Edited; - public readonly bool Exportable; - public readonly byte[] BAK; + + public SaveFileState State { get; } + public SaveFileMetadata Metadata { get; private set; } protected SaveFile(byte[] data, byte[] bak, bool exportable = true) { Data = data; - BAK = bak; - Exportable = exportable; + State = new SaveFileState(bak, exportable); + Metadata = new SaveFileMetadata(this); } - protected SaveFile(byte[] data, bool exportable = true) : this(data, (byte[])data.Clone(), exportable) { } - - protected SaveFile() + protected SaveFile(byte[] data, bool exportable = true) : this(data, (byte[])data.Clone(), exportable) { - Data = BAK = Array.Empty(); - Exportable = false; } - protected SaveFile(int size) + protected SaveFile(int size = 0) { - Data = new byte[size]; - BAK = Data; - Exportable = false; + Data = size == 0 ? Array.Empty() : new byte[size]; + State = new SaveFileState(Array.Empty(), false); + Metadata = new SaveFileMetadata(this); } - public string? FileName, FilePath, FileFolder; - public string BAKName => $"{FileName} [{BAKText}].bak"; - protected abstract string BAKText { get; } - public abstract SaveFile Clone(); - public abstract string Filter { get; } - public byte[] Footer { protected get; set; } = Array.Empty(); // .dsv - public byte[] Header { protected get; set; } = Array.Empty(); // .gci - public virtual string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : + protected internal abstract string ShortSummary { get; } public abstract string Extension { get; } + protected abstract SaveFile CloneInternal(); + + public SaveFile Clone() + { + var sav = CloneInternal(); + sav.Metadata = Metadata; + return sav; + } + + public virtual string PlayTimeString => $"{PlayedHours}ː{PlayedMinutes:00}ː{PlayedSeconds:00}"; // not : + public virtual IReadOnlyList PKMExtensions => PKM.Extensions.Where(f => { int gen = f.Last() - 0x30; @@ -58,11 +71,7 @@ protected SaveFile(int size) public byte[] Write(ExportFlags flags = ExportFlags.None) { byte[] data = GetFinalData(); - if (Footer.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeFooter)) - return data.Concat(Footer).ToArray(); - if (Header.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeHeader)) - return Header.Concat(data).ToArray(); - return data; + return Metadata.Finalize(data, flags); } protected virtual byte[] GetFinalData() @@ -87,7 +96,7 @@ protected virtual byte[] GetFinalData() public void SetData(byte[] dest, byte[] input, int offset) { input.CopyTo(dest, offset); - Edited = true; + State.Edited = true; } public abstract string GetString(byte[] data, int offset, int length); diff --git a/PKHeX.Core/Saves/SaveFileMetadata.cs b/PKHeX.Core/Saves/SaveFileMetadata.cs new file mode 100644 index 000000000..3e51d7fb9 --- /dev/null +++ b/PKHeX.Core/Saves/SaveFileMetadata.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using System.Linq; + +namespace PKHeX.Core +{ + public class SaveFileMetadata + { + private readonly SaveFile SAV; + + public string? FileName; + public string? FilePath; + public string? FileFolder; + private byte[] Footer = Array.Empty(); // .dsv + private byte[] Header = Array.Empty(); // .gci + + public string BAKName => $"{FileName} [{SAV.ShortSummary}].bak"; + + public SaveFileMetadata(SaveFile sav) => SAV = sav; + + public bool HasHeader => Header.Length != 0; + public bool HasFooter => Footer.Length != 0; + public string Filter => $"{SAV.GetType().Name}|{GetSuggestedExtension()}|All Files|*.*"; + + public byte[] Finalize(byte[] data, ExportFlags flags) + { + if (Footer.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeFooter)) + return data.Concat(Footer).ToArray(); + if (Header.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeHeader)) + return Header.Concat(data).ToArray(); + return data; + } + + /// + /// Sets the details of any trimmed header and footer arrays to a object. + /// + public void SetExtraInfo(byte[] header, byte[] footer) + { + Header = header; + Footer = footer; + } + + /// + /// Sets the details of a path to a object. + /// + /// Full Path of the file + public void SetExtraInfo(string path) + { + var sav = SAV; + if (!sav.State.Exportable) // Blank save file + { + sav.Metadata.FileFolder = sav.Metadata.FilePath = string.Empty; + sav.Metadata.FileName = "Blank Save File"; + return; + } + + sav.Metadata.FilePath = path; + sav.Metadata.FileFolder = Path.GetDirectoryName(path); + sav.Metadata.FileName = string.Empty; + var bakName = Util.CleanFileName(sav.Metadata.BAKName); + sav.Metadata.FileName = Path.GetFileName(path); + if (sav.Metadata.FileName?.EndsWith(bakName) == true) + sav.Metadata.FileName = sav.Metadata.FileName.Substring(0, sav.Metadata.FileName.Length - bakName.Length); + } + + public string GetSuggestedExtension() + { + var sav = SAV; + var fn = sav.Metadata.FileName; + if (fn != null) + return Path.GetExtension(fn); + + if ((sav.Generation == 4 || sav.Generation == 5) && sav.Metadata.HasFooter) + return ".dsv"; + return sav.Extension; + } + } +} \ No newline at end of file diff --git a/PKHeX.Core/Saves/Storage/Bank3.cs b/PKHeX.Core/Saves/Storage/Bank3.cs index 78c68d51e..35b4424f3 100644 --- a/PKHeX.Core/Saves/Storage/Bank3.cs +++ b/PKHeX.Core/Saves/Storage/Bank3.cs @@ -11,11 +11,10 @@ public sealed class Bank3 : BulkStorage public override PersonalTable Personal => PersonalTable.RS; public override IReadOnlyList HeldItems => Legal.HeldItems_RS; - public override SaveFile Clone() => new Bank3((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new Bank3((byte[])Data.Clone()); public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4"); - protected override string BAKText => PlayTimeString; + protected internal override string ShortSummary => PlayTimeString; public override string Extension => ".gst"; - public override string Filter { get; } = "PokeStock G3 Storage|*.gst*"; public override int BoxCount => 64; private const int BoxNameSize = 9; diff --git a/PKHeX.Core/Saves/Storage/Bank4.cs b/PKHeX.Core/Saves/Storage/Bank4.cs index c17f8fc81..4abd7bafd 100644 --- a/PKHeX.Core/Saves/Storage/Bank4.cs +++ b/PKHeX.Core/Saves/Storage/Bank4.cs @@ -11,11 +11,10 @@ public sealed class Bank4 : BulkStorage public override PersonalTable Personal => PersonalTable.HGSS; public override IReadOnlyList HeldItems => Legal.HeldItems_HGSS; - public override SaveFile Clone() => new Bank4((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new Bank4((byte[])Data.Clone()); public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4"); - protected override string BAKText => PlayTimeString; + protected internal override string ShortSummary => PlayTimeString; public override string Extension => ".stk"; - public override string Filter { get; } = "PokeStock G4 Storage|*.stk*"; public override int BoxCount => 64; private const int BoxNameSize = 0x18; diff --git a/PKHeX.Core/Saves/Storage/Bank7.cs b/PKHeX.Core/Saves/Storage/Bank7.cs index 623315003..5a73ec2db 100644 --- a/PKHeX.Core/Saves/Storage/Bank7.cs +++ b/PKHeX.Core/Saves/Storage/Bank7.cs @@ -12,9 +12,9 @@ public sealed class Bank7 : BulkStorage public override PersonalTable Personal => PersonalTable.USUM; public override IReadOnlyList HeldItems => Legal.HeldItems_SM; - public override SaveFile Clone() => new Bank7((byte[])Data.Clone(), PKMType, BoxStart, SlotsPerBox); + protected override SaveFile CloneInternal() => new Bank7((byte[])Data.Clone(), PKMType, BoxStart, SlotsPerBox); public override string PlayTimeString => $"{Year:00}{Month:00}{Day:00}_{Hours:00}ː{Minutes:00}"; - protected override string BAKText => PlayTimeString; + protected internal override string ShortSummary => PlayTimeString; private const int GroupNameSize = 0x20; private const int BankNameSize = 0x24; private const int GroupNameSpacing = GroupNameSize + 2; diff --git a/PKHeX.Core/Saves/Storage/BulkStorage.cs b/PKHeX.Core/Saves/Storage/BulkStorage.cs index f62997473..780fa64b3 100644 --- a/PKHeX.Core/Saves/Storage/BulkStorage.cs +++ b/PKHeX.Core/Saves/Storage/BulkStorage.cs @@ -21,11 +21,10 @@ protected BulkStorage(byte[] data, Type t, int start, int slotsPerBox = 30) : ba protected readonly int SlotsPerBox; - protected override string BAKText => $"{Checksums.CRC16(Data, Box, Data.Length - Box):X4}"; - public override string Filter { get; } = "All Files|*.*"; - public override string Extension { get; } = ".bin"; - public sealed override bool ChecksumsValid { get; } = true; - public sealed override string ChecksumInfo { get; } = "No Info."; + protected internal override string ShortSummary => $"{Checksums.CRC16(Data, Box, Data.Length - Box):X4}"; + public override string Extension => ".bin"; + public sealed override bool ChecksumsValid => true; + public sealed override string ChecksumInfo => "No Info."; private readonly PKM blank; public sealed override Type PKMType => blank.GetType(); diff --git a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs index 3be060565..5d91ec61c 100644 --- a/PKHeX.Core/Saves/Storage/SAV4Ranch.cs +++ b/PKHeX.Core/Saves/Storage/SAV4Ranch.cs @@ -21,11 +21,10 @@ public sealed class SAV4Ranch : BulkStorage, ISaveFileRevision public override PersonalTable Personal => PersonalTable.Pt; public override IReadOnlyList HeldItems => Legal.HeldItems_Pt; - public override SaveFile Clone() => new SAV4Ranch((byte[])Data.Clone()); + protected override SaveFile CloneInternal() => new SAV4Ranch((byte[])Data.Clone()); public override string PlayTimeString => Checksums.CRC16(Data, 0, Data.Length).ToString("X4"); - protected override string BAKText => $"{OT} {PlayTimeString}"; + protected internal override string ShortSummary => $"{OT} {PlayTimeString}"; public override string Extension => ".bin"; - public override string Filter { get; } = "Ranch G4 Storage|*.bin*"; protected override PKM GetPKM(byte[] data) => new PK4(data); protected override byte[] DecryptPKM(byte[] data) => PokeCrypto.DecryptArray45(data); diff --git a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs index 49b152e2a..29c8ba2e2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs +++ b/PKHeX.Core/Saves/Substructures/Gen6/MysteryBlock6.cs @@ -20,7 +20,7 @@ public void SetReceivedFlags(bool[] value) if (value.Length != MaxReceivedFlag) return; ArrayUtil.SetBitFlagArray(Data, Offset + FlagStart, value); - SAV.Edited = true; + SAV.State.Edited = true; } public DataMysteryGift[] GetGifts() diff --git a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs index 9dffcf0d6..addd06f02 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/MysteryBlock7.cs @@ -22,7 +22,7 @@ public bool[] MysteryGiftReceivedFlags if (value.Length != MaxReceivedFlag) return; ArrayUtil.SetBitFlagArray(Data, Offset + FlagStart, value); - SAV.Edited = true; + SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs b/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs index 8aa316564..7462208f4 100644 --- a/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs +++ b/PKHeX.Core/Saves/Substructures/Gen7/PokeListHeader.cs @@ -30,7 +30,7 @@ public PokeListHeader(SAV7b sav, int offset) : base(sav) { Offset = offset; PokeListInfo = LoadPointerData(); - if (!sav.Exportable) + if (!sav.State.Exportable) { for (int i = 0; i < COUNT; i++) PokeListInfo[i] = SLOT_EMPTY; diff --git a/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs b/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs index 021310808..103f65555 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/MyStatus8.cs @@ -16,7 +16,7 @@ public string Number { for (int i = 0; i < 3; i++) Data[0x01 + i] = (byte)(value.Length > i ? value[i] : '\0'); - SAV.Edited = true; + SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs b/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs index 9b3c5e5ba..9da5ea9e2 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/TeamIndexes8.cs @@ -12,7 +12,7 @@ public sealed class TeamIndexes8 : SaveBlock, ITeamIndexSet public void LoadBattleTeams() { - if (!SAV.Exportable) + if (!SAV.State.Exportable) { ClearBattleTeams(); return; diff --git a/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs b/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs index 74e6911e2..bb49a6ab5 100644 --- a/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs +++ b/PKHeX.Core/Saves/Substructures/Gen8/TrainerCard8.cs @@ -117,7 +117,7 @@ public string Number { for (int i = 0; i < 3; i++) Data[0x39 + i] = (byte) (value.Length > i ? value[i] : '\0'); - SAV.Edited = true; + SAV.State.Edited = true; } } diff --git a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs index 05beec857..7ec259c7f 100644 --- a/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs +++ b/PKHeX.Core/Saves/Substructures/PokeDex/Zukan7.cs @@ -32,7 +32,7 @@ private Zukan7(SaveFile sav, int dex, int langflag, Func for { DexFormIndexFetcher = form; FormBaseSpecies = GetFormIndexBaseSpeciesList(); - Debug.Assert(!SAV.Exportable || BitConverter.ToUInt32(SAV.Data, PokeDex) == MAGIC); + Debug.Assert(!SAV.State.Exportable || BitConverter.ToUInt32(SAV.Data, PokeDex) == MAGIC); } public Func DexFormIndexFetcher { get; } diff --git a/PKHeX.Core/Saves/Util/BoxUtil.cs b/PKHeX.Core/Saves/Util/BoxUtil.cs index 344a1b975..025ef6d77 100644 --- a/PKHeX.Core/Saves/Util/BoxUtil.cs +++ b/PKHeX.Core/Saves/Util/BoxUtil.cs @@ -212,7 +212,7 @@ public static string[] GetBoxNames(SaveFile sav) { int count = sav.BoxCount; var result = new string[count]; - if (!sav.Exportable) + if (!sav.State.Exportable) { for (int i = 0; i < count; i++) result[i] = $"Box {i + 1}"; diff --git a/PKHeX.Core/Saves/Util/SaveExtensions.cs b/PKHeX.Core/Saves/Util/SaveExtensions.cs index 627084656..2b53b6f0f 100644 --- a/PKHeX.Core/Saves/Util/SaveExtensions.cs +++ b/PKHeX.Core/Saves/Util/SaveExtensions.cs @@ -22,30 +22,7 @@ public static class SaveExtensions /// PKM input that is to be injected into the Save File. /// Indication whether or not the PKM is compatible. public static bool IsPKMCompatibleWithModifications(this SaveFile sav, PKM pk) => PKMConverter.IsPKMCompatibleWithModifications(pk, sav); - - /// - /// Sets the details of a path to a object. - /// - /// Save File to set path details to. - /// Full Path of the file - public static void SetFileInfo(this SaveFile sav, string path) - { - if (!sav.Exportable) // Blank save file - { - sav.FileFolder = sav.FilePath = string.Empty; - sav.FileName = "Blank Save File"; - return; - } - - sav.FilePath = path; - sav.FileFolder = Path.GetDirectoryName(path); - sav.FileName = string.Empty; - var bakName = Util.CleanFileName(sav.BAKName); - sav.FileName = Path.GetFileName(path); - if (sav.FileName?.EndsWith(bakName) == true) - sav.FileName = sav.FileName.Substring(0, sav.FileName.Length - bakName.Length); - } - + /// /// Gets suggested export options for the save file. /// diff --git a/PKHeX.Core/Saves/Util/SaveFinder.cs b/PKHeX.Core/Saves/Util/SaveFinder.cs index 1e72becf7..7c110873d 100644 --- a/PKHeX.Core/Saves/Util/SaveFinder.cs +++ b/PKHeX.Core/Saves/Util/SaveFinder.cs @@ -173,7 +173,7 @@ public static bool DetectSaveFile(out string path, out SaveFile? sav, IReadOnlyL return false; } - path = result.FilePath!; + path = result.Metadata.FilePath!; sav = result; return File.Exists(path); } diff --git a/PKHeX.Core/Saves/Util/SaveUtil.cs b/PKHeX.Core/Saves/Util/SaveUtil.cs index b2c47b7bd..1f02ef0fd 100644 --- a/PKHeX.Core/Saves/Util/SaveUtil.cs +++ b/PKHeX.Core/Saves/Util/SaveUtil.cs @@ -487,7 +487,7 @@ private static GameVersion GetIsG8SAV(byte[] data) var sav = GetVariantSAV(data); if (sav == null) return null; - sav.SetFileInfo(path); + sav.Metadata.SetExtraInfo(path); return sav; } @@ -501,8 +501,8 @@ private static GameVersion GetIsG8SAV(byte[] data) var sav = GetVariantSAVInternal(data); if (sav == null) return null; - sav.Header = header; - sav.Footer = footer; + + sav.Metadata.SetExtraInfo(header, footer); return sav; } @@ -569,8 +569,8 @@ private static GameVersion GetIsG8SAV(byte[] data) // No pattern matched default: return null; } - sav.Header = header; - sav.Footer = footer; + + sav.Metadata.SetExtraInfo(header, footer); return sav; } @@ -780,12 +780,9 @@ private static void CheckHeaderFooter(ref byte[] input, out byte[] header, out b input = input.SliceEnd(0xA4); return; } - int start = input.Length - FOOTER_DSV.Length; - for (int i = 0; i < FOOTER_DSV.Length; i++) - { - if (FOOTER_DSV[i] != input[start + i]) - return; - } + + if (!GetHasFooterDSV(input)) + return; footer = input.SliceEnd(SIZE_G4RAW); input = input.Slice(0, SIZE_G4RAW); @@ -814,6 +811,24 @@ private static void CheckHeaderFooter(ref byte[] input, out byte[] header, out b static bool IsGameMatchHeader(IEnumerable headers, byte[] data) => headers.Contains(Encoding.ASCII.GetString(data, 0, 4)); } + private static bool GetHasFooterDSV(byte[] input) + { + var signature = FOOTER_DSV; + if (!GetHasSignature(input, signature, input.Length - signature.Length)) + return false; + return true; + } + + private static bool GetHasSignature(byte[] input, byte[] signature, int start) + { + for (int i = 0; i < signature.Length; i++) + { + if (signature[i] != input[start + i]) + return false; + } + return true; + } + /// /// Force loads the provided to the requested . /// @@ -824,13 +839,13 @@ public static SAV3 GetG3SaveOverride(SaveFile sav, GameVersion ver) { return ver switch // Reset save file info { - R => new SAV3(sav.BAK, RS), - S => new SAV3(sav.BAK, RS), - RS => new SAV3(sav.BAK, RS), - E => new SAV3(sav.BAK, E), - FRLG => new SAV3(sav.BAK, FRLG), - FR => new SAV3(sav.BAK, FRLG), - LG => new SAV3(sav.BAK, FRLG), + R => new SAV3(sav.State.BAK, RS), + S => new SAV3(sav.State.BAK, RS), + RS => new SAV3(sav.State.BAK, RS), + E => new SAV3(sav.State.BAK, E), + FRLG => new SAV3(sav.State.BAK, FRLG), + FR => new SAV3(sav.State.BAK, FRLG), + LG => new SAV3(sav.State.BAK, FRLG), _ => throw new ArgumentException(nameof(ver)) }; } diff --git a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs index fcc0f16ab..92360a2c5 100644 --- a/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs +++ b/PKHeX.WinForms/Controls/SAV Editor/SAVEditor.cs @@ -456,13 +456,13 @@ private void UpdateStringSeed(object sender, EventArgs e) { var value = filterText.PadLeft(SAV.DaycareSeedSize, '0'); SAV.SetDaycareRNGSeed(SAV.DaycareIndex, value); - SAV.Edited = true; + SAV.State.Edited = true; } else if (tb == TB_GameSync && SAV is IGameSync sync) { var value = filterText.PadLeft(sync.GameSyncIDSize, '0'); sync.GameSyncID = value; - SAV.Edited = true; + SAV.State.Edited = true; } else if (SAV is ISecureValueStorage s) { @@ -471,7 +471,7 @@ private void UpdateStringSeed(object sender, EventArgs e) s.TimeStampCurrent = value; else if (tb == TB_Secure2) s.TimeStampPrevious = value; - SAV.Edited = true; + SAV.State.Edited = true; } } @@ -728,7 +728,7 @@ private void ClickVerifyCHK(object sender, EventArgs e) return; } - if (SAV.Edited) + if (SAV.State.Edited) { WinFormsUtil.Alert(MsgSaveChecksumFailEdited); return; @@ -775,14 +775,14 @@ public bool ExportSaveFile() public bool ExportBackup() { - if (!SAV.Exportable) + if (!SAV.State.Exportable) return false; - using var sfd = new SaveFileDialog {FileName = Util.CleanFileName(SAV.BAKName)}; + 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.BAK); + File.WriteAllBytes(path, SAV.State.BAK); WinFormsUtil.Alert(MsgSaveBackup, path); return true; @@ -1009,7 +1009,7 @@ private bool ToggleViewBox(SaveFile sav) private bool ToggleViewParty(SaveFile sav, int BoxTab) { - if (!sav.HasParty || !sav.Exportable) + if (!sav.HasParty || !sav.State.Exportable) { if (tabBoxMulti.TabPages.Contains(Tab_PartyBattle)) tabBoxMulti.TabPages.Remove(Tab_PartyBattle); @@ -1028,7 +1028,7 @@ private bool ToggleViewParty(SaveFile sav, int BoxTab) private bool ToggleViewDaycare(SaveFile sav, int BoxTab, int PartyTab) { - if ((!sav.HasDaycare && SL_Extra.SlotCount == 0) || !sav.Exportable) + if ((!sav.HasDaycare && SL_Extra.SlotCount == 0) || !sav.State.Exportable) { if (tabBoxMulti.TabPages.Contains(Tab_Other)) tabBoxMulti.TabPages.Remove(Tab_Other); @@ -1050,7 +1050,7 @@ private bool ToggleViewDaycare(SaveFile sav, int BoxTab, int PartyTab) private void ToggleViewSubEditors(SaveFile sav) { - if (!sav.Exportable || sav is BulkStorage) + if (!sav.State.Exportable || sav is BulkStorage) { GB_SAVtools.Visible = false; B_JPEG.Visible = false; @@ -1093,7 +1093,7 @@ private void ToggleViewSubEditors(SaveFile sav) SL_Extra.SAV = sav; SL_Extra.Initialize(sav.GetExtraSlots(HaX), InitializeDragDrop); } - GB_SAVtools.Visible = sav.Exportable && FLP_SAVtools.Controls.OfType().Any(c => c.Enabled); + GB_SAVtools.Visible = sav.State.Exportable && FLP_SAVtools.Controls.OfType().Any(c => c.Enabled); foreach (Control c in FLP_SAVtools.Controls.OfType()) c.Visible = c.Enabled; var list = FLP_SAVtools.Controls.OfType().ToArray(); @@ -1106,7 +1106,7 @@ private void ToggleViewMisc(SaveFile sav) { // Generational Interface ToggleSecrets(sav, HideSecretDetails); - B_VerifyCHK.Enabled = SAV.Exportable; + B_VerifyCHK.Enabled = SAV.State.Exportable; if (sav is SAV4BR br) { @@ -1138,7 +1138,7 @@ private void ToggleViewMisc(SaveFile sav) private void ToggleSecrets(SaveFile sav, bool hide) { - var shouldShow = sav.Exportable && !hide; + var shouldShow = sav.State.Exportable && !hide; TB_Secure1.Visible = TB_Secure2.Visible = L_Secure1.Visible = L_Secure2.Visible = shouldShow && sav is ISecureValueStorage; TB_GameSync.Visible = L_GameSync.Visible = shouldShow && sav is IGameSync; } diff --git a/PKHeX.WinForms/MainWindow/Main.cs b/PKHeX.WinForms/MainWindow/Main.cs index fdb2fe058..e0a808369 100644 --- a/PKHeX.WinForms/MainWindow/Main.cs +++ b/PKHeX.WinForms/MainWindow/Main.cs @@ -226,7 +226,7 @@ private void LoadBlankSaveFile(GameVersion ver) if (sav.Version == GameVersion.Invalid) // will fail to load sav = SaveUtil.GetBlankSAV((GameVersion)GameInfo.VersionDataSource.Max(z => z.Value), tr, lang); OpenSAV(sav, string.Empty); - C_SAV!.SAV.Edited = false; // Prevents form close warning from showing until changes are made + C_SAV!.SAV.State.Edited = false; // Prevents form close warning from showing until changes are made } private void FormLoadCheckForUpdates() @@ -486,7 +486,7 @@ private void MainMenuFolder(object sender, EventArgs e) { if (this.OpenWindowExists()) return; - var form = new SAV_FolderList(s => OpenSAV(SaveUtil.GetVariantSAV(s.FilePath!), s.FilePath!)); + var form = new SAV_FolderList(s => OpenSAV(SaveUtil.GetVariantSAV(s.Metadata.FilePath!), s.Metadata.FilePath!)); form.Show(); } @@ -729,11 +729,11 @@ private bool OpenSAV(SaveFile? sav, string path) return true; } - sav.SetFileInfo(path); + sav.Metadata.SetExtraInfo(path); if (!SanityCheckSAV(ref sav)) return true; - if (C_SAV.SAV.Edited && Settings.Default.ModifyUnset) + if (C_SAV.SAV.State.Edited && Settings.Default.ModifyUnset) { var prompt = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgProgramCloseUnsaved, MsgProgramSaveFileConfirm); if (prompt != DialogResult.Yes) @@ -747,7 +747,7 @@ private bool OpenSAV(SaveFile? sav, string path) dragout.Size = new Size(SpriteUtil.Spriter.Width, SpriteUtil.Spriter.Height); // clean fields - Menu_ExportSAV.Enabled = sav.Exportable; + Menu_ExportSAV.Enabled = sav.State.Exportable; // No changes made yet Menu_Undo.Enabled = false; @@ -807,7 +807,7 @@ private void ResetSAVPKMEditors(SaveFile sav) PKME_Tabs.FlickerInterface(); foreach (var p in Plugins) p.NotifySaveLoaded(); - sav.Edited = false; + sav.State.Edited = false; } private static string GetProgramTitle() @@ -830,9 +830,9 @@ private static string GetProgramTitle(SaveFile sav) var ver = GameInfo.GetVersionName(sav.Version); if (Settings.Default.HideSAVDetails) return title + $"[{ver}]"; - if (!sav.Exportable) // Blank save file - return title + $"{sav.FileName} [{sav.OT} ({ver})]"; - return title + Path.GetFileNameWithoutExtension(Util.CleanFileName(sav.BAKName)); // more descriptive + if (!sav.State.Exportable) // Blank save file + return title + $"{sav.Metadata.FileName} [{sav.OT} ({ver})]"; + return title + Path.GetFileNameWithoutExtension(Util.CleanFileName(sav.Metadata.BAKName)); // more descriptive } private static bool TryBackupExportCheck(SaveFile sav, string path) @@ -841,9 +841,9 @@ private static bool TryBackupExportCheck(SaveFile sav, string path) return false; // If backup folder exists, save a backup. - string backupName = Path.Combine(BackupPath, Util.CleanFileName(sav.BAKName)); - if (sav.Exportable && Directory.Exists(BackupPath) && !File.Exists(backupName)) - File.WriteAllBytes(backupName, sav.BAK); + 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); if (!FileUtil.IsFileLocked(path)) return true; @@ -856,7 +856,7 @@ private static bool SanityCheckSAV(ref SaveFile sav) { ParseSettings.InitFromSaveFileData(sav); // physical GB, no longer used in logic - if (sav.Exportable && sav is SAV3 s3) + if (sav.State.Exportable && sav is SAV3 s3) { if (s3.IndeterminateGame || ModifierKeys == Keys.Control) { @@ -1156,7 +1156,7 @@ private void DragoutDrop(object sender, DragEventArgs e) private void Main_FormClosing(object sender, FormClosingEventArgs e) { - if (C_SAV.SAV.Edited || PKME_Tabs.PKMIsUnsaved) + if (C_SAV.SAV.State.Edited || PKME_Tabs.PKMIsUnsaved) { var prompt = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, MsgProgramCloseUnsaved, MsgProgramCloseConfirm); if (prompt != DialogResult.Yes) diff --git a/PKHeX.WinForms/Subforms/SAV_FolderList.cs b/PKHeX.WinForms/Subforms/SAV_FolderList.cs index bac14a2ce..d0b6a4ed6 100644 --- a/PKHeX.WinForms/Subforms/SAV_FolderList.cs +++ b/PKHeX.WinForms/Subforms/SAV_FolderList.cs @@ -15,7 +15,7 @@ namespace PKHeX.WinForms public partial class SAV_FolderList : Form { private readonly Action OpenSaveFile; - private readonly List Paths; + private readonly List Paths; private readonly SortableBindingList Recent; private readonly SortableBindingList Backup; private readonly List