8using Terraria.Utilities;
 
   12    internal static class BackupIO
 
   18        public static bool archiveLock = 
false;
 
   19        private static bool IsArchiveOlder(DateTime time, TimeSpan thresholdAge) => (DateTime.Now - time) > thresholdAge;
 
   20        private static string GetArchiveName(
string name, 
bool isCloudSave) => name + (isCloudSave ? 
"-cloud" : 
"");
 
   21        private static string TodaysBackup(
string name, 
bool isCloudSave) => $
"{DateTime.Now:yyyy-MM-dd}-{GetArchiveName(name, isCloudSave)}.zip";
 
   22        private static DateTime GetTime(
string file) => Convert.ToDateTime(file.Substring(0, 10));
 
   28        private static void RunArchiving(Action<ZipFile, bool, string> saveAction, 
bool isCloudSave, 
string dir, 
string name, 
string path) {
 
   30                Directory.CreateDirectory(dir);
 
   31                DeleteOldArchives(dir, isCloudSave, name);
 
   33                using (var zip = 
new ZipFile(Path.Combine(dir, TodaysBackup(name, isCloudSave)), Encoding.UTF8)) {
 
   35                    zip.UseZip64WhenSaving = Zip64Option.AsNecessary;
 
   36                    zip.ZipErrorAction = ZipErrorAction.Throw;
 
   37                    saveAction(zip, isCloudSave, path);
 
   42                Logging.tML.Error(
"A problem occurred when trying to create a backup file.", e);
 
   51        private static void AddZipEntry(
this ZipFile zip, 
string path, 
bool isCloud = 
false) {
 
   52            zip.CompressionMethod = CompressionMethod.Deflate;
 
   53            zip.CompressionLevel = CompressionLevel.BestCompression;
 
   54            zip.Comment = $
"Archived on ${DateTime.Now} by tModLoader";
 
   56            if (!isCloud && (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) {
 
   57                zip.AddFiles(Directory.GetFiles(path), 
false, Path.GetFileNameWithoutExtension(path));
 
   60                if (isCloud) zip.AddEntry(Path.GetFileName(path), FileUtilities.ReadAllBytes(path, 
true));
 
   61                else zip.AddFile(path, 
"");
 
   72        private static void DeleteOldArchives(
string dir, 
bool isCloudSave, 
string name) {
 
   73            var path = Path.Combine(dir, TodaysBackup(name, isCloudSave));
 
   74            if (File.Exists(path)) {
 
   78            var archives = 
new DirectoryInfo(dir).GetFiles($
"*{GetArchiveName(name, isCloudSave)}*.zip", SearchOption.TopDirectoryOnly)
 
   79                .OrderBy(f => GetTime(f.Name))
 
   82            FileInfo previous = 
null;
 
   84            foreach (var archived 
in archives) {
 
   85                if (previous == 
null) {
 
   90                var time = GetTime(archived.Name);
 
   92                    IsArchiveOlder(time, TimeSpan.FromDays(30))
 
   93                    ? 30 : IsArchiveOlder(time, TimeSpan.FromDays(7))
 
   96                if ((time - GetTime(previous.Name)).Days < freshness) {
 
   97                    DeleteArchive(previous.FullName);
 
  103        private static void DeleteArchive(
string path) {
 
  108                Logging.tML.Error(
"Problem deleting old archive file", e);
 
  117            public static readonly 
string WorldDir = Path.Combine(Main.SavePath, 
"Worlds");
 
  120            internal static void ArchiveWorld(
string path, 
bool isCloudSave)
 
  123            private static void WriteArchive(ZipFile zip, 
bool isCloudSave, 
string path) {
 
  124                if (FileUtilities.Exists(path, isCloudSave)) zip.AddZipEntry(path, isCloudSave);
 
  125                path = Path.ChangeExtension(path, 
".twld");
 
  126                if (FileUtilities.Exists(path, isCloudSave)) zip.AddZipEntry(path, isCloudSave);
 
  135            public static readonly 
string PlayerDir = Path.Combine(Main.SavePath, 
"Players");
 
  144            private static void WriteArchive(ZipFile zip, 
bool isCloudSave, 
string path) {
 
  146                if (FileUtilities.Exists(path, isCloudSave)) zip.AddZipEntry(path, isCloudSave);
 
  147                path = Path.ChangeExtension(path, 
".tplr");
 
  148                if (FileUtilities.Exists(path, isCloudSave)) zip.AddZipEntry(path, isCloudSave);
 
  160                var name = Path.GetFileNameWithoutExtension(path);
 
  161                path = Path.ChangeExtension(path, 
"");
 
  162                path = path.Substring(0, path.Length - 1);
 
  164                var cloudFiles = SocialAPI.Cloud.GetFiles().Where(p => p.StartsWith(path, StringComparison.CurrentCultureIgnoreCase)
 
  165                                 && (p.EndsWith(
".map", StringComparison.CurrentCultureIgnoreCase) || p.EndsWith(
".tmap", StringComparison.CurrentCultureIgnoreCase)));
 
  167                foreach (
string cloudPath 
in cloudFiles)
 
  168                    zip.AddEntry($
"{name}/{Path.GetFileName(cloudPath)}", FileUtilities.ReadAllBytes(cloudPath, 
true));
 
  176                var plrDir = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
 
  177                if (Directory.Exists(plrDir)) zip.AddZipEntry(plrDir);
 
Responsible for archiving player backups
 
static void WriteLocalFiles(ZipFile zip, string path)
Write local files, which simply writes the entire player dir
 
static readonly string PlayerDir
 
static void WriteArchive(ZipFile zip, bool isCloudSave, string path)
Write the archive. Writes the .plr and .tplr files, then writes the player directory
 
static void WriteCloudFiles(ZipFile zip, string path)
Write cloud files, which will get the relevant part of the path and write map & tmap files
 
static void ArchivePlayer(string path, bool isCloudSave)
 
static readonly string PlayerBackupDir
 
Responsible for archiving world backups
 
static void WriteArchive(ZipFile zip, bool isCloudSave, string path)
 
static readonly string WorldBackupDir
 
static readonly string WorldDir