From 6b347b83a7c588638224a02840b1d90b4557ea2a Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 9 Feb 2019 15:16:14 -0500 Subject: fix Save Backup not pruning old backups if they're uncompressed --- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 30dbfbe6..33adf76d 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -124,20 +124,23 @@ namespace StardewModdingAPI.Mods.SaveBackup try { var oldBackups = backupFolder - .GetFiles() + .GetFileSystemInfos() .OrderByDescending(p => p.CreationTimeUtc) .Skip(backupsToKeep); - foreach (FileInfo file in oldBackups) + foreach (FileSystemInfo entry in oldBackups) { try { - this.Monitor.Log($"Deleting {file.Name}...", LogLevel.Trace); - file.Delete(); + this.Monitor.Log($"Deleting {entry.Name}...", LogLevel.Trace); + if (entry is DirectoryInfo folder) + folder.Delete(recursive: true); + else + entry.Delete(); } catch (Exception ex) { - this.Monitor.Log($"Error deleting old save backup '{file.Name}': {ex}", LogLevel.Error); + this.Monitor.Log($"Error deleting old save backup '{entry.Name}': {ex}", LogLevel.Error); } } } -- cgit From 307055b028e0d6643983189e96a5ebcbe89d32ba Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 4 Apr 2019 18:43:56 -0400 Subject: bump version to 3.0 --- src/SMAPI.Mods.SaveBackup/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index e147bd39..eddd3443 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "2.11.3", + "Version": "3.0.0", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "2.11.3" + "MinimumApiVersion": "3.0.0" } -- cgit From abffdc2dab2a1c03904427b251acac9d800e0912 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 24 Apr 2019 23:45:34 -0400 Subject: simplify project names --- .../SMAPI.Mods.SaveBackup.csproj | 35 ++++++++++++++++++++++ .../StardewModdingAPI.Mods.SaveBackup.csproj | 35 ---------------------- 2 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj delete mode 100644 src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj new file mode 100644 index 00000000..9375cb64 --- /dev/null +++ b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj @@ -0,0 +1,35 @@ + + + + SMAPI.Mods.SaveBackup + SaveBackup + net45 + false + latest + $(SolutionDir)\..\bin\$(Configuration)\Mods\SaveBackup + false + x86 + + + + + False + + + + + + Properties\GlobalAssemblyInfo.cs + + + + + + PreserveNewest + + + + + + + diff --git a/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj b/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj deleted file mode 100644 index 460f3c93..00000000 --- a/src/SMAPI.Mods.SaveBackup/StardewModdingAPI.Mods.SaveBackup.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - StardewModdingAPI.Mods.SaveBackup - SaveBackup - net45 - false - latest - $(SolutionDir)\..\bin\$(Configuration)\Mods\SaveBackup - false - x86 - - - - - False - - - - - - Properties\GlobalAssemblyInfo.cs - - - - - - PreserveNewest - - - - - - - -- cgit From 904c39eb7286df6ad3098dfdcfbf09cb282141e4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Tue, 18 Jun 2019 23:59:30 -0400 Subject: move assembly references out of common.targets --- src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj index 9375cb64..d2e41e77 100644 --- a/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj +++ b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj @@ -17,6 +17,13 @@ + + + $(GamePath)\$(GameExecutableName).exe + False + + + Properties\GlobalAssemblyInfo.cs -- cgit From 2b4bc2c282e17ba431f9a6ec1892b87a37eb4bb7 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 29 Jul 2019 13:15:27 -0400 Subject: back up saves in a background thread --- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 33adf76d..845df453 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -4,6 +4,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using StardewValley; namespace StardewModdingAPI.Mods.SaveBackup @@ -40,9 +41,10 @@ namespace StardewModdingAPI.Mods.SaveBackup DirectoryInfo backupFolder = new DirectoryInfo(this.BackupFolder); backupFolder.Create(); - // back up saves - this.CreateBackup(backupFolder); - this.PruneBackups(backupFolder, this.BackupsToKeep); + // back up & prune saves + Task + .Run(() => this.CreateBackup(backupFolder)) + .ContinueWith(backupTask => this.PruneBackups(backupFolder, this.BackupsToKeep)); } catch (Exception ex) { @@ -68,7 +70,7 @@ namespace StardewModdingAPI.Mods.SaveBackup // create zip // due to limitations with the bundled Mono on Mac, we can't reference System.IO.Compression. - this.Monitor.Log($"Adding {targetFile.Name}...", LogLevel.Trace); + this.Monitor.Log($"Backing up saves to {targetFile.FullName}...", LogLevel.Trace); switch (Constants.TargetPlatform) { case GamePlatform.Linux: @@ -108,6 +110,7 @@ namespace StardewModdingAPI.Mods.SaveBackup } break; } + this.Monitor.Log("Backup done!", LogLevel.Trace); } catch (Exception ex) { -- cgit From 25e4aa14d865cdf9f3616cfb0fd981aaaf0e3535 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 9 Aug 2019 17:47:53 -0400 Subject: remove legacy AssemblyInfo and GlobalAssemblyInfo files, use consistent assembly names --- src/SMAPI.Mods.SaveBackup/Properties/AssemblyInfo.cs | 4 ---- src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj | 9 +-------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 src/SMAPI.Mods.SaveBackup/Properties/AssemblyInfo.cs (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/Properties/AssemblyInfo.cs b/src/SMAPI.Mods.SaveBackup/Properties/AssemblyInfo.cs deleted file mode 100644 index fc6b26fa..00000000 --- a/src/SMAPI.Mods.SaveBackup/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("StardewModdingAPI.Mods.SaveBackup")] -[assembly: AssemblyDescription("")] diff --git a/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj index d2e41e77..2d031408 100644 --- a/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj +++ b/src/SMAPI.Mods.SaveBackup/SMAPI.Mods.SaveBackup.csproj @@ -1,10 +1,9 @@  - SMAPI.Mods.SaveBackup SaveBackup + StardewModdingAPI.Mods.SaveBackup net45 - false latest $(SolutionDir)\..\bin\$(Configuration)\Mods\SaveBackup false @@ -24,12 +23,6 @@ - - - Properties\GlobalAssemblyInfo.cs - - - PreserveNewest -- cgit From 15cd316ce44fb1ca43b29f6087243cc3af2f78fb Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Wed, 2 Oct 2019 01:19:33 -0400 Subject: fix Save Backup mod on Android --- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 845df453..efc6f39f 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -73,6 +73,7 @@ namespace StardewModdingAPI.Mods.SaveBackup this.Monitor.Log($"Backing up saves to {targetFile.FullName}...", LogLevel.Trace); switch (Constants.TargetPlatform) { + case GamePlatform.Android: case GamePlatform.Linux: case GamePlatform.Windows: { -- cgit From b4a8c1c2ac9d1b3796a144d267cceadf5f7831ff Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 7 Oct 2019 15:05:01 -0400 Subject: rework Save Backup to support Android --- src/SMAPI.Mods.SaveBackup/ModEntry.cs | 119 ++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 40 deletions(-) (limited to 'src/SMAPI.Mods.SaveBackup') diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index efc6f39f..3b47759b 100644 --- a/src/SMAPI.Mods.SaveBackup/ModEntry.cs +++ b/src/SMAPI.Mods.SaveBackup/ModEntry.cs @@ -68,48 +68,21 @@ namespace StardewModdingAPI.Mods.SaveBackup if (targetFile.Exists || fallbackDir.Exists) return; - // create zip - // due to limitations with the bundled Mono on Mac, we can't reference System.IO.Compression. + // back up saves this.Monitor.Log($"Backing up saves to {targetFile.FullName}...", LogLevel.Trace); - switch (Constants.TargetPlatform) + if (!this.TryCompress(Constants.SavesPath, targetFile, out Exception compressError)) { - case GamePlatform.Android: - case GamePlatform.Linux: - case GamePlatform.Windows: - { - try - { - // create compressed backup - Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); - Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); - Type compressionLevelType = coreAssembly.GetType("System.IO.Compression.CompressionLevel") ?? throw new InvalidOperationException("Can't load CompressionLevel type."); - Type zipFileType = fsAssembly.GetType("System.IO.Compression.ZipFile") ?? throw new InvalidOperationException("Can't load ZipFile type."); - MethodInfo createMethod = zipFileType.GetMethod("CreateFromDirectory", new[] { typeof(string), typeof(string), compressionLevelType, typeof(bool) }) ?? throw new InvalidOperationException("Can't load ZipFile.CreateFromDirectory method."); - createMethod.Invoke(null, new object[] { Constants.SavesPath, targetFile.FullName, CompressionLevel.Fastest, false }); - } - catch (Exception ex) when (ex is TypeLoadException || ex.InnerException is TypeLoadException) - { - // create uncompressed backup if compression fails - this.Monitor.Log("Couldn't zip the save backup, creating uncompressed backup instead.", LogLevel.Debug); - this.Monitor.Log(ex.ToString(), LogLevel.Trace); - this.RecursiveCopy(new DirectoryInfo(Constants.SavesPath), fallbackDir, copyRoot: false); - } - } - break; - - case GamePlatform.Mac: - { - DirectoryInfo saveFolder = new DirectoryInfo(Constants.SavesPath); - ProcessStartInfo startInfo = new ProcessStartInfo - { - FileName = "zip", - Arguments = $"-rq \"{targetFile.FullName}\" \"{saveFolder.Name}\" -x \"*.DS_Store\" -x \"__MACOSX\"", - WorkingDirectory = $"{Constants.SavesPath}/../", - CreateNoWindow = true - }; - new Process { StartInfo = startInfo }.Start(); - } - break; + // 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); } this.Monitor.Log("Backup done!", LogLevel.Trace); } @@ -155,6 +128,72 @@ namespace StardewModdingAPI.Mods.SaveBackup } } + /// Create a zip using the best available method. + /// The file or directory path to zip. + /// The destination file to create. + /// The error which occurred trying to compress, if applicable. This is if compression isn't supported on this platform. + /// Returns whether compression succeeded. + private bool TryCompress(string sourcePath, FileInfo destination, out Exception error) + { + try + { + if (Constants.TargetPlatform == GamePlatform.Mac) + this.CompressUsingMacProcess(sourcePath, destination); // due to limitations with the bundled Mono on Mac, we can't reference System.IO.Compression + else + this.CompressUsingNetFramework(sourcePath, destination); + + error = null; + return true; + } + catch (Exception ex) + { + error = ex; + return false; + } + } + + /// Create a zip using the .NET compression library. + /// The file or directory path to zip. + /// The destination file to create. + /// The compression libraries aren't available on this system. + private void CompressUsingNetFramework(string sourcePath, FileInfo destination) + { + // get compress method + MethodInfo createFromDirectory; + try + { + // create compressed backup + Assembly coreAssembly = Assembly.Load("System.IO.Compression, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); + Assembly fsAssembly = Assembly.Load("System.IO.Compression.FileSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") ?? throw new InvalidOperationException("Can't load System.IO.Compression assembly."); + Type compressionLevelType = coreAssembly.GetType("System.IO.Compression.CompressionLevel") ?? throw new InvalidOperationException("Can't load CompressionLevel type."); + Type zipFileType = fsAssembly.GetType("System.IO.Compression.ZipFile") ?? throw new InvalidOperationException("Can't load ZipFile type."); + createFromDirectory = zipFileType.GetMethod("CreateFromDirectory", new[] { typeof(string), typeof(string), compressionLevelType, typeof(bool) }) ?? throw new InvalidOperationException("Can't load ZipFile.CreateFromDirectory method."); + } + catch (Exception ex) + { + throw new NotSupportedException("Couldn't load the .NET compression libraries on this system.", ex); + } + + // compress file + createFromDirectory.Invoke(null, new object[] { sourcePath, destination.FullName, CompressionLevel.Fastest, false }); + } + + /// Create a zip using a process command on MacOS. + /// The file or directory path to zip. + /// The destination file to create. + private void CompressUsingMacProcess(string sourcePath, FileInfo destination) + { + DirectoryInfo saveFolder = new DirectoryInfo(sourcePath); + ProcessStartInfo startInfo = new ProcessStartInfo + { + FileName = "zip", + Arguments = $"-rq \"{destination.FullName}\" \"{saveFolder.Name}\" -x \"*.DS_Store\" -x \"__MACOSX\"", + WorkingDirectory = $"{saveFolder.FullName}/../", + CreateNoWindow = true + }; + new Process { StartInfo = startInfo }.Start(); + } + /// Recursively copy a directory or file. /// The file or folder to copy. /// The folder to copy into. -- cgit