diff --git a/GeneralMods/AdvancedSaveBackup/SaveBackup.cs b/GeneralMods/AdvancedSaveBackup/SaveBackup.cs index bb24d44c..9b998b09 100644 --- a/GeneralMods/AdvancedSaveBackup/SaveBackup.cs +++ b/GeneralMods/AdvancedSaveBackup/SaveBackup.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.IO.Compression; using System.Linq; +using System.Reflection; using Omegasis.SaveBackup.Framework; using StardewModdingAPI; using StardewModdingAPI.Events; @@ -57,21 +58,108 @@ namespace Omegasis.SaveBackup this.BackupSaves(SaveBackup.NightlyBackupsPath); } + /// Recursively copy a directory or file. + /// The file or folder to copy. + /// The folder to copy into. + /// Whether to copy the root folder itself, or false to only copy its contents. + /// A filter which matches the files or directories to copy, or null to copy everything. + /// Derived from the SMAPI installer code. + /// Returns whether any files were copied. + private bool RecursiveCopy(FileSystemInfo source, DirectoryInfo targetFolder, bool copyRoot = true) + { + if (!source.Exists) + return false; + + bool anyCopied = false; + + switch (source) + { + case FileInfo sourceFile: + targetFolder.Create(); + sourceFile.CopyTo(Path.Combine(targetFolder.FullName, sourceFile.Name)); + anyCopied = true; + break; + + case DirectoryInfo sourceDir: + DirectoryInfo targetSubfolder = copyRoot ? new DirectoryInfo(Path.Combine(targetFolder.FullName, sourceDir.Name)) : targetFolder; + foreach (var entry in sourceDir.EnumerateFileSystemInfos()) + anyCopied = this.RecursiveCopy(entry, targetSubfolder) || anyCopied; + break; + + default: + throw new NotSupportedException($"Unknown filesystem info type '{source.GetType().FullName}'."); + } + + return anyCopied; + } + + /// 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 }); + } + + private void OriginCompressLogic(string folderPath) + { + ZipFile.CreateFromDirectory(SaveBackup.SavesPath, Path.Combine(folderPath, $"backup-{DateTime.Now:yyyyMMdd'-'HHmmss}.zip")); + } + /// Back up saves to the specified folder. /// The folder path in which to generate saves. private void BackupSaves(string folderPath) { // back up saves Directory.CreateDirectory(folderPath); - ZipFile.CreateFromDirectory(SaveBackup.SavesPath, Path.Combine(folderPath, $"backup-{DateTime.Now:yyyyMMdd'-'HHmmss}.zip")); + if(Constants.TargetPlatform == GamePlatform.Android) + { + FileInfo targetFile = new FileInfo(Path.Combine(folderPath, $"backup-{DateTime.Now:yyyyMMdd'-'HHmmss}.zip")); + try + { + this.CompressUsingNetFramework(folderPath, targetFile); + } + catch (NotSupportedException) + { + this.RecursiveCopy(new DirectoryInfo(SaveBackup.SavesPath), new DirectoryInfo(Path.Combine(folderPath, $"backup-{DateTime.Now:yyyyMMdd'-'HHmmss}")), false); + } + } + else + { + this.OriginCompressLogic(folderPath); + } // delete old backups new DirectoryInfo(folderPath) - .EnumerateFiles() + .GetFileSystemInfos() .OrderByDescending(f => f.CreationTime) .Skip(this.Config.SaveCount) .ToList() - .ForEach(file => file.Delete()); + .ForEach(file => + { + if (file is DirectoryInfo folder) + folder.Delete(recursive: true); + else + file.Delete(); + }); } } }