diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2019-11-24 13:49:30 -0500 |
commit | a3f21685049cabf2d824c8060dc0b1de47e9449e (patch) | |
tree | ad9add30e9da2a50e0ea0245f1546b7378f0d282 /src/SMAPI.Mods.SaveBackup/ModEntry.cs | |
parent | 6521df7b131924835eb797251c1e956fae0d6e13 (diff) | |
parent | 277bf082675b98b95bf6184fe3c7a45b969c7ac2 (diff) | |
download | SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.gz SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.bz2 SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.zip |
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Mods.SaveBackup/ModEntry.cs')
-rw-r--r-- | src/SMAPI.Mods.SaveBackup/ModEntry.cs | 142 |
1 files changed, 94 insertions, 48 deletions
diff --git a/src/SMAPI.Mods.SaveBackup/ModEntry.cs b/src/SMAPI.Mods.SaveBackup/ModEntry.cs index 30dbfbe6..3b47759b 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) { @@ -66,48 +68,23 @@ 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. - this.Monitor.Log($"Adding {targetFile.Name}...", LogLevel.Trace); - switch (Constants.TargetPlatform) + // back up saves + this.Monitor.Log($"Backing up saves to {targetFile.FullName}...", LogLevel.Trace); + if (!this.TryCompress(Constants.SavesPath, targetFile, out Exception compressError)) { - 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); } catch (Exception ex) { @@ -124,20 +101,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); } } } @@ -148,6 +128,72 @@ namespace StardewModdingAPI.Mods.SaveBackup } } + /// <summary>Create a zip using the best available method.</summary> + /// <param name="sourcePath">The file or directory path to zip.</param> + /// <param name="destination">The destination file to create.</param> + /// <param name="error">The error which occurred trying to compress, if applicable. This is <see cref="NotSupportedException"/> if compression isn't supported on this platform.</param> + /// <returns>Returns whether compression succeeded.</returns> + 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; + } + } + + /// <summary>Create a zip using the .NET compression library.</summary> + /// <param name="sourcePath">The file or directory path to zip.</param> + /// <param name="destination">The destination file to create.</param> + /// <exception cref="NotSupportedException">The compression libraries aren't available on this system.</exception> + 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 }); + } + + /// <summary>Create a zip using a process command on MacOS.</summary> + /// <param name="sourcePath">The file or directory path to zip.</param> + /// <param name="destination">The destination file to create.</param> + 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(); + } + /// <summary>Recursively copy a directory or file.</summary> /// <param name="source">The file or folder to copy.</param> /// <param name="targetFolder">The folder to copy into.</param> |