diff options
-rw-r--r-- | docs/release-notes.md | 4 | ||||
-rw-r--r-- | src/SMAPI.Mods.SaveBackup/ModEntry.cs | 50 |
2 files changed, 39 insertions, 15 deletions
diff --git a/docs/release-notes.md b/docs/release-notes.md index dada7726..b49307c6 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -17,6 +17,10 @@ * Added `performance` command to track mod performance metrics. This is an advanced experimental feature. (Thanks to Drachenkätzchen!) * Added `test_input` command to view button codes in the console. +* For the Save Backup mod: + * Fixed extra files under `Saves` (e.g. manual backups) not being ignored. + * Fixed Android issue where game files were backed up. + * For modders: * Asset propagation for player sprites now affects other players' sprites, and updates recolor maps (e.g. sleeves). * Reworked the order that asset editors/loaders are called between multiple mods to support some framework mods like Content Patcher and Json Assets. Note that the order is undefined and should not be depended on. diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 3b47759b..b1d368a1 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -68,22 +68,21 @@ namespace StardewModdingAPI.Mods.SaveBackup if (targetFile.Exists || fallbackDir.Exists) return; - // back up saves - this.Monitor.Log($"Backing up saves to {targetFile.FullName}...", LogLevel.Trace); - if (!this.TryCompress(Constants.SavesPath, targetFile, out Exception compressError)) + // copy saves to fallback directory (ignore non-save files/folders) + this.Monitor.Log($"Backing up saves to {fallbackDir.FullName}...", LogLevel.Trace); + DirectoryInfo savesDir = new DirectoryInfo(Constants.SavesPath); + this.RecursiveCopy(savesDir, fallbackDir, copyRoot: false, entry => this.MatchSaveFolders(savesDir, entry)); + + // compress backup if possible + this.Monitor.Log("Compressing backup if possible...", LogLevel.Trace); + if (!this.TryCompress(fallbackDir.FullName, targetFile, out Exception compressError)) { - // log error (expected on Android due to missing compression DLLs) - if (Constants.TargetPlatform == GamePlatform.Android) - this.Monitor.VerboseLog($"Compression isn't supported on Android:\n{compressError}"); - else - { - this.Monitor.Log("Couldn't zip the save backup, creating uncompressed backup instead.", LogLevel.Debug); - this.Monitor.Log(compressError.ToString(), LogLevel.Trace); - } - - // fallback to uncompressed - this.RecursiveCopy(new DirectoryInfo(Constants.SavesPath), fallbackDir, copyRoot: false); + if (Constants.TargetPlatform != GamePlatform.Android) // expected to fail on Android + this.Monitor.Log($"Couldn't compress backup, leaving it uncompressed.\n{compressError}", LogLevel.Trace); } + else + fallbackDir.Delete(recursive: true); + this.Monitor.Log("Backup done!", LogLevel.Trace); } catch (Exception ex) @@ -198,12 +197,16 @@ namespace StardewModdingAPI.Mods.SaveBackup /// <param name="source">The file or folder to copy.</param> /// <param name="targetFolder">The folder to copy into.</param> /// <param name="copyRoot">Whether to copy the root folder itself, or <c>false</c> to only copy its contents.</param> + /// <param name="filter">A filter which matches the files or directories to copy, or <c>null</c> to copy everything.</param> /// <remarks>Derived from the SMAPI installer code.</remarks> - private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, bool copyRoot = true) + private void RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, bool copyRoot = true, Func<FileSystemInfo, bool> filter = null) { if (!targetFolder.Exists) targetFolder.Create(); + if (filter?.Invoke(source) == false) + return; + switch (source) { case FileInfo sourceFile: @@ -220,5 +223,22 @@ namespace StardewModdingAPI.Mods.SaveBackup throw new NotSupportedException($"Unknown filesystem info type '{source.GetType().FullName}'."); } } + + /// <summary>A copy filter which matches save folders.</summary> + /// <param name="savesFolder">The folder containing save folders.</param> + /// <param name="entry">The current entry to check under <paramref name="savesFolder"/>.</param> + private bool MatchSaveFolders(DirectoryInfo savesFolder, FileSystemInfo entry) + { + // only need to filter top-level entries + string parentPath = (entry as FileInfo)?.DirectoryName ?? (entry as DirectoryInfo)?.Parent?.FullName; + if (parentPath != savesFolder.FullName) + return true; + + + // match folders with Name_ID format + return + entry is DirectoryInfo + && ulong.TryParse(entry.Name.Split('_').Last(), out _); + } } } |