using System; using System.IO; namespace PKHeX.Core; /// /// Tracks information about where the originated from, and provides logic for saving to a file. /// public sealed record SaveFileMetadata(SaveFile SAV) { public SaveFile SAV { private get; init; } = SAV; /// /// Full path where the originated from. /// public string? FilePath { get; private set; } /// /// File Name of the . /// /// This is not always the original file name. We try to strip out the Backup name markings to get the original filename. public string? FileName { get; private set; } /// /// Directory in which the was saved in. /// public string? FileFolder { get; private set; } private byte[] Footer = []; // .dsv private byte[] Header = []; // .gci private ISaveHandler? Handler; private string BAKSuffix => $" [{SAV.ShortSummary}].bak"; /// /// Simple summary of the save file, to help differentiate it from other save files with the same filename. /// public string BAKName => FileName + BAKSuffix; public bool HasHeader => Header.Length != 0; public bool HasFooter => Footer.Length != 0; /// /// File Dialog filter to help save the file. /// public string Filter => $"{SAV.GetType().Name}|{GetSuggestedExtension()}|All Files|*.*"; /// /// Writes the input and appends the and if requested. /// /// Finalized save file data (with fixed checksums) to be written to a file /// Toggle flags /// Final save file data. public byte[] Finalize(byte[] data, BinaryExportSetting setting) { if (Footer.Length > 0 && setting.HasFlag(BinaryExportSetting.IncludeFooter)) data = [..data, ..Footer]; if (Header.Length > 0 && setting.HasFlag(BinaryExportSetting.IncludeHeader)) data = [..Header, ..data]; if (setting != BinaryExportSetting.None) Handler?.Finalize(data); return data; } /// /// Sets the details of any trimmed header and footer arrays to a object. /// public void SetExtraInfo(byte[] header, byte[] footer, ISaveHandler handler) { Header = header; Footer = footer; Handler = handler; } /// /// 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 || string.IsNullOrWhiteSpace(path)) // Blank save file { sav.Metadata.SetAsBlank(); return; } SetAsLoadedFile(path); } private void SetAsLoadedFile(string path) { FilePath = path; FileFolder = Path.GetDirectoryName(path); FileName = GetFileName(path, BAKSuffix); } private static string GetFileName(string path, string bak) { var bakName = Util.CleanFileName(bak); var fn = Path.GetFileName(path); return fn.EndsWith(bakName, StringComparison.Ordinal) ? fn[..^bakName.Length] : fn; } private void SetAsBlank() { FileFolder = FilePath = string.Empty; FileName = "Blank Save File"; } /// /// Gets the suggested file extension when writing to a saved file. /// public string GetSuggestedExtension() { var sav = SAV; var fn = sav.Metadata.FileName; if (fn != null) return Path.GetExtension(fn); if ((sav.Generation is 4 or 5) && sav.Metadata.HasFooter) return ".dsv"; return sav.Extension; } /// /// Gets suggested export options for the save file. /// /// Selected export extension public BinaryExportSetting GetSuggestedFlags(string? ext = null) { var flags = BinaryExportSetting.None; if (ext == ".dsv") flags |= BinaryExportSetting.IncludeFooter; if (ext == ".gci" || SAV is IGCSaveFile {MemoryCard: null} || ext == ".sram") flags |= BinaryExportSetting.IncludeHeader; return flags; } }