Terraria ModLoader  0.11.7.8
A mod to make and play Terraria mods
BackupIO.cs
Go to the documentation of this file.
1 using Ionic.Zip;
2 using Ionic.Zlib;
3 using System;
4 using System.IO;
5 using System.Linq;
6 using System.Text;
7 using Terraria.Social;
8 using Terraria.Utilities;
9 
10 namespace Terraria.ModLoader
11 {
12  internal static class BackupIO
13  {
14  // at the end of WorldGen.generateWorld:
15  // BackupIO.archiveLock = true;
16  // in WorldGen.do_worldGenCallBack after WorldFile.saveWorld:
17  // BackupIO.archiveLock = false;
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));
23 
28  private static void RunArchiving(Action<ZipFile, bool, string> saveAction, bool isCloudSave, string dir, string name, string path) {
29  try {
30  Directory.CreateDirectory(dir);
31  DeleteOldArchives(dir, isCloudSave, name);
32 
33  using (var zip = new ZipFile(Path.Combine(dir, TodaysBackup(name, isCloudSave)), Encoding.UTF8)) {
34  // use zip64 extensions if necessary for huge files
35  zip.UseZip64WhenSaving = Zip64Option.AsNecessary;
36  zip.ZipErrorAction = ZipErrorAction.Throw;
37  saveAction(zip, isCloudSave, path);
38  zip.Save();
39  }
40  }
41  catch (Exception e) {
42  Logging.tML.Error("A problem occurred when trying to create a backup file.", e);
43  }
44  }
45 
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";
55 
56  if (!isCloud && (File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) {
57  zip.AddFiles(Directory.GetFiles(path), false, Path.GetFileNameWithoutExtension(path));
58  }
59  else {
60  if (isCloud) zip.AddEntry(Path.GetFileName(path), FileUtilities.ReadAllBytes(path, true));
61  else zip.AddFile(path, "");
62  }
63  }
64 
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)) {
75  DeleteArchive(path);
76  }
77 
78  var archives = new DirectoryInfo(dir).GetFiles($"*{GetArchiveName(name, isCloudSave)}*.zip", SearchOption.TopDirectoryOnly)
79  .OrderBy(f => GetTime(f.Name))
80  .ToArray();
81 
82  FileInfo previous = null;
83 
84  foreach (var archived in archives) {
85  if (previous == null) {
86  previous = archived;
87  continue;
88  }
89 
90  var time = GetTime(archived.Name);
91  var freshness =
92  IsArchiveOlder(time, TimeSpan.FromDays(30))
93  ? 30 : IsArchiveOlder(time, TimeSpan.FromDays(7))
94  ? 7 : 1;
95 
96  if ((time - GetTime(previous.Name)).Days < freshness) {
97  DeleteArchive(previous.FullName);
98  }
99  previous = archived;
100  }
101  }
102 
103  private static void DeleteArchive(string path) {
104  try {
105  File.Delete(path);
106  }
107  catch (Exception e) {
108  Logging.tML.Error("Problem deleting old archive file", e);
109  }
110  }
111 
115  public static class World
116  {
117  public static readonly string WorldDir = Path.Combine(Main.SavePath, "Worlds");
118  public static readonly string WorldBackupDir = Path.Combine(WorldDir, "Backups");
119 
120  internal static void ArchiveWorld(string path, bool isCloudSave)
121  => RunArchiving(WriteArchive, isCloudSave, WorldBackupDir, Path.GetFileNameWithoutExtension(path), path);
122 
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);
127  }
128  }
129 
133  public static class Player
134  {
135  public static readonly string PlayerDir = Path.Combine(Main.SavePath, "Players");
136  public static readonly string PlayerBackupDir = Path.Combine(PlayerDir, "Backups");
137 
138  public static void ArchivePlayer(string path, bool isCloudSave)
139  => RunArchiving(WriteArchive, isCloudSave, PlayerBackupDir, Path.GetFileNameWithoutExtension(path), path);
140 
144  private static void WriteArchive(ZipFile zip, bool isCloudSave, string path) {
145  // Write .plr and .tplr files
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);
149 
150  // Write other files, such as tmap files to the zip
151  if (isCloudSave) WriteCloudFiles(zip, path);
152  else WriteLocalFiles(zip, path);
153  }
154 
158  private static void WriteCloudFiles(ZipFile zip, string path) {
159  // Path is still equal to local path
160  var name = Path.GetFileNameWithoutExtension(path);
161  path = Path.ChangeExtension(path, "");
162  path = path.Substring(0, path.Length - 1);
163  // Write map files from plr dir in cloud
164  var cloudFiles = SocialAPI.Cloud.GetFiles().Where(p => p.StartsWith(path, StringComparison.CurrentCultureIgnoreCase)
165  && (p.EndsWith(".map", StringComparison.CurrentCultureIgnoreCase) || p.EndsWith(".tmap", StringComparison.CurrentCultureIgnoreCase)));
166 
167  foreach (string cloudPath in cloudFiles)
168  zip.AddEntry($"{name}/{Path.GetFileName(cloudPath)}", FileUtilities.ReadAllBytes(cloudPath, true));
169  }
170 
174  private static void WriteLocalFiles(ZipFile zip, string path) {
175  // Write map files from plr dir
176  var plrDir = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
177  if (Directory.Exists(plrDir)) zip.AddZipEntry(plrDir);
178  }
179  }
180  }
181 }
static void WriteArchive(ZipFile zip, bool isCloudSave, string path)
Definition: BackupIO.cs:123
Responsible for archiving world backups
Definition: BackupIO.cs:115
Responsible for archiving player backups
Definition: BackupIO.cs:133
static void WriteCloudFiles(ZipFile zip, string path)
Write cloud files, which will get the relevant part of the path and write map & tmap files ...
Definition: BackupIO.cs:158
static void WriteArchive(ZipFile zip, bool isCloudSave, string path)
Write the archive. Writes the .plr and .tplr files, then writes the player directory ...
Definition: BackupIO.cs:144
static void WriteLocalFiles(ZipFile zip, string path)
Write local files, which simply writes the entire player dir
Definition: BackupIO.cs:174