-
Notifications
You must be signed in to change notification settings - Fork 0
/
Program.cs
317 lines (256 loc) · 10.9 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Savescum
{
class Program
{
private const string ARGUMENT_SEPARATOR = "=";
private const string OPERATION_SAVE = "save" ;
private const string OPERATION_LOAD = "load";
private const string PREFIX_BACKUP = "SavescumBackup";
private const string PREFIX_PROTECT = "SavescumOverwriteProtection";
private const string FORMAT_SAVE = "{0}\\{1}{2:D3}";
private const string FORMAT_PROTECT = "{0}\\{1}{2:yyyy-MM-dd_hh-mm-ss}";
private const string ARGUMENT_OPERATION = "operation";
private const string ARGUMENT_PATH_GAME = "gamePath";
private const string ARGUMENT_PATH_BACKUP = "backupPath";
private const string ARGUMENT_PATH_PROTECT = "protectPath";
private const string ARGUMENT_PREFIX_BACKUP = "backupPrefix";
private const string ARGUMENT_PREFIX_PROTECT = "protectPrefix";
private static readonly int MAX_NAME_COUNT = 999;
private static ArgumentProperties s_argumentProperties;
// These are required so no default set
private static string s_pathGame;
private static string s_pathBackup;
private static string s_pathProtect;
// Set argument defaults
private static string s_prefixBackup = PREFIX_BACKUP;
private static string s_prefixProtect = PREFIX_PROTECT;
static void Main(string[] args)
{
PrintStartBanner();
if (args.Length == 0)
{
PrintErrorNoArguments();
PrintUsage();
Environment.Exit(1);
}
try
{
s_argumentProperties = new ArgumentProperties(args, ARGUMENT_SEPARATOR);
}
catch (ArgumentException e)
{
HandleArgumentException(e);
}
string operation = "";
try
{
// required arguments give no defaults
operation = s_argumentProperties.GetString(ARGUMENT_OPERATION, null);
s_pathGame = s_argumentProperties.GetString(ARGUMENT_PATH_GAME, null);
s_pathBackup = s_argumentProperties.GetString(ARGUMENT_PATH_BACKUP, null);
// optional arguments *should* never throw when default is supplied
s_prefixBackup = s_argumentProperties.GetString(ARGUMENT_PREFIX_BACKUP, PREFIX_BACKUP);
}
catch (ArgumentException e)
{
HandleArgumentException(e);
}
switch (operation)
{
case OPERATION_SAVE:
DoSave();
break;
case OPERATION_LOAD:
DoLoad();
break;
default:
Console.WriteLine("Unknown operation: " + args[0]);
PrintUsage();
Environment.Exit(1);
break;
}
Environment.Exit(0);
}
private static void DoSave()
{
Console.WriteLine("Savescum SAVING ...");
string savePath = GenerateSavePath(s_pathBackup, s_prefixBackup);
if (savePath.Length == 0)
{
Console.Error.WriteLine("Error: Couldn't find un-used save name. ");
Console.Error.WriteLine(" Exceeded maximum number of saves (" + MAX_NAME_COUNT + ").");
Console.Error.WriteLine(" Delete some from: " + s_pathBackup);
return;
}
PrintCopyInfo(s_pathGame, savePath);
DirectoryCopy(s_pathGame, savePath, true);
Console.WriteLine();
Console.WriteLine("SAVE FINISHED to " + savePath);
Console.WriteLine();
}
private static void DoLoad()
{
// read optional parameters used only by load operation
s_prefixProtect = s_argumentProperties.GetString(ARGUMENT_PREFIX_PROTECT, PREFIX_PROTECT);
s_pathProtect = s_argumentProperties.GetString(ARGUMENT_PATH_PROTECT, s_pathBackup);
Console.WriteLine("Savescum LOADING...");
// Find latest save - notify and bail out if it isn't found
string latestBackupPath = FindLatestBackupPath(s_pathBackup, s_prefixBackup);
if (latestBackupPath.Length == 0)
{
Console.Error.WriteLine();
Console.Error.WriteLine(" Error: No backup saves found at ");
Console.Error.WriteLine(" path: " + s_pathBackup);
Console.Error.WriteLine(" prefix: " + s_prefixBackup);
Console.Error.WriteLine();
Console.Error.WriteLine(" NO FILES CHANGED");
Console.Error.WriteLine();
return;
}
Console.WriteLine(" Found latest backup at");
Console.WriteLine(" path: " + latestBackupPath);
// Backup existing save directory before writing over it
string protectDirPath = GenerateProtectPath(s_pathProtect, s_prefixProtect);
if (protectDirPath.Length == 0)
{
PrintProtectError(s_pathProtect, s_prefixProtect);
return;
}
Console.WriteLine(" Backing up directory before writing over it");
PrintCopyInfo(s_pathGame, protectDirPath);
DirectoryCopy(s_pathGame, protectDirPath, true);
// delete and write over
Console.WriteLine(" Deleting directory: " + s_pathGame);
DirectoryInfo deleteDir = new DirectoryInfo(s_pathGame);
deleteDir.Delete(true);
Console.WriteLine(" Restoring deleted directory from backup save");
PrintCopyInfo(latestBackupPath, s_pathGame);
DirectoryCopy(latestBackupPath, s_pathGame, true);
Console.WriteLine();
Console.WriteLine("LOAD FINISHED from " + latestBackupPath);
Console.WriteLine();
}
private static string FindLatestBackupPath(string pathBackup, string prefixBackup)
{
DirectoryInfo backupDirectoryInfo = new DirectoryInfo(pathBackup);
List<DirectoryInfo> directoryInfos = new List<DirectoryInfo>(
backupDirectoryInfo.GetDirectories(
prefixBackup + "*.*"));
// nothing found
if (directoryInfos.Count == 0)
{
return "";
}
// sort results to find latest by creation time
IOrderedEnumerable<DirectoryInfo> orderedInfos = directoryInfos.OrderBy(
directoryInfo =>
directoryInfo.CreationTime);
// newest will be last in the list
return orderedInfos.Last().FullName;
}
private static string GenerateSavePath(string path, string prefix)
{
string savePath;
for (int nameCount = 0; nameCount < MAX_NAME_COUNT; nameCount++)
{
savePath = String.Format(FORMAT_SAVE, path, prefix, nameCount);
DirectoryInfo dir = new DirectoryInfo(savePath);
if (!dir.Exists)
{
return savePath;
}
}
// Couldn't find a name that isn't used
return "";
}
private static string GenerateProtectPath(string path, string prefix)
{
string protectDirPath = String.Format(FORMAT_PROTECT, path, prefix, DateTime.Now);
DirectoryInfo dir = new DirectoryInfo(protectDirPath);
if (!dir.Exists)
{
return protectDirPath;
}
return "";
}
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
private static void HandleArgumentException(ArgumentException e)
{
PrintArgumentException(e);
PrintUsage();
Environment.Exit(1);
}
private static void PrintStartBanner()
{
Console.WriteLine();
Console.WriteLine("Savescum backup utility");
Console.WriteLine();
}
private static void PrintProtectError(string path, string prefix)
{
Console.Error.WriteLine("Error: Couldn't backup directory before writing over it");
Console.Error.WriteLine(" Path = " + path);
Console.Error.WriteLine(" Prefix = " + prefix);
Console.Error.WriteLine();
}
private static void PrintCopyInfo(string pathSource, string pathDest)
{
Console.WriteLine(" Copying directory");
Console.WriteLine(String.Format(" [{0}] ->", pathSource));
Console.WriteLine(String.Format(" [{0}] ...", pathDest));
Console.WriteLine(" Copy finished");
}
static void PrintUsage()
{
Console.WriteLine("Usage: Savescum operation=[save|load|clean|clear] [argument=value ...]");
Console.WriteLine();
}
private static void PrintArgumentException(ArgumentException e)
{
Console.Error.WriteLine("Error: ");
Console.Error.WriteLine(" " + e.Message);
Console.Error.WriteLine();
}
private static void PrintErrorNoArguments()
{
Console.Error.WriteLine("Error: No command-line arguments given");
Console.Error.WriteLine();
}
}
}