Implement better backup mechanism.

This commit is contained in:
Eusth 2017-02-22 00:34:07 +01:00
parent 1f298a4857
commit 06c85a93a5
6 changed files with 167 additions and 75 deletions

View file

@ -63,6 +63,7 @@
<ItemGroup>
<Compile Include="PatchContext.cs" />
<Compile Include="Patcher\BackupManager.cs" />
<Compile Include="Patcher\BackupUnit.cs" />
<Compile Include="Patcher\Patcher.cs" />
<Compile Include="Patcher\Virtualizer.cs" />
<Compile Include="Program.cs" />

View file

@ -30,6 +30,7 @@ namespace IPA
public string IPARoot { get; private set; }
public string ShortcutPath { get; private set; }
public string IPA { get; private set; }
public string BackupPath { get; private set; }
private PatchContext() { }
@ -39,7 +40,7 @@ namespace IPA
context.Args = args;
context.Executable = args[0];
context.ProjectRoot = Path.GetDirectoryName(context.Executable);
context.ProjectRoot = new FileInfo(context.Executable).Directory.FullName;
context.IPARoot = Path.Combine(context.ProjectRoot, "IPA");
context.IPA = Assembly.GetExecutingAssembly().Location ?? Path.Combine(context.ProjectRoot, "IPA.exe");
context.LauncherPathSrc = Path.Combine(context.IPARoot, "Launcher.exe");
@ -50,10 +51,12 @@ namespace IPA
context.ManagedPath = Path.Combine(context.DataPathDst, "Managed");
context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngine.dll");
context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-Csharp.dll");
context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName);
string shortcutName = string.Format("{0} (Patch & Launch)", context.ProjectName);
context.ShortcutPath = Path.Combine(context.ProjectRoot, shortcutName) + ".lnk";
Directory.CreateDirectory(context.BackupPath);
return context;
}
}

View file

@ -9,61 +9,27 @@ namespace IPA.Patcher
{
public class BackupManager
{
public static void MakeBackup(string file)
public static BackupUnit FindLatestBackup(PatchContext context)
{
File.Copy(file, GetBackupName(file));
return new DirectoryInfo(context.BackupPath)
.GetDirectories()
.OrderByDescending(p => p.Name)
.Select(p => BackupUnit.FromDirectory(p, context))
.FirstOrDefault();
}
public static bool HasBackup(PatchContext context)
{
return FindLatestBackup(context) != null;
}
private static string GetBackupName(string file)
public static bool Restore(PatchContext context)
{
string backup = file + ".Original";
if (File.Exists(backup))
{
int i = 1;
string backupBase = backup;
while (File.Exists(backup))
{
backup = backupBase + i++;
}
}
return backup;
}
public static string FindLatestBackup(string file)
{
var directory = Path.GetDirectoryName(file);
var filename = Path.GetFileName(file);
var regex = new Regex(String.Format(@"^{0}\.Original\d*$", Regex.Escape(filename)));
var extractNumRegex = new Regex(@"\d+$");
string latestFile = null;
int latestNum = -1;
foreach(var f in Directory.GetFiles(directory))
{
if(regex.IsMatch(Path.GetFileName(f)))
{
var match = extractNumRegex.Match(f);
int number = match.Success ? int.Parse(match.Value) : 0;
if(number > latestNum)
{
latestNum = number;
latestFile = f;
}
}
}
return latestFile;
}
public static bool Restore(string file)
{
var backup = FindLatestBackup(file);
var backup = FindLatestBackup(context);
if(backup != null)
{
File.Delete(file);
File.Move(backup, file);
backup.Restore();
backup.Delete();
return true;
}
return false;

128
IPA/Patcher/BackupUnit.cs Normal file
View file

@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Patcher
{
/// <summary>
/// A unit for backup. WIP.
/// </summary>
public class BackupUnit
{
public string Name { get; private set; }
private DirectoryInfo _BackupPath;
private PatchContext _Context;
private List<string> _Files = new List<string>();
public BackupUnit(PatchContext context) : this(context, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss"))
{
}
private BackupUnit(PatchContext context, string name)
{
Name = name;
_Context = context;
_BackupPath = new DirectoryInfo(Path.Combine(_Context.BackupPath, Name));
}
public static BackupUnit FromDirectory(DirectoryInfo directory, PatchContext context)
{
var unit = new BackupUnit(context, directory.Name);
// Parse directory
foreach(var file in directory.GetFiles("*", SearchOption.AllDirectories)) {
var relativePath = file.FullName.Substring(directory.FullName.Length + 1);
unit._Files.Add(relativePath);
}
return unit;
}
public void Add(string file)
{
Add(new FileInfo(file));
}
internal void Delete()
{
_BackupPath.Delete(true);
}
/// <summary>
/// Adds a file to the list of changed files and backups it.
/// </summary>
/// <param name="path"></param>
public void Add(FileInfo file)
{
if(!file.FullName.StartsWith(_Context.ProjectRoot))
{
Console.Error.WriteLine("Invalid file path for backup! {0}", file);
return;
}
var relativePath = file.FullName.Substring(_Context.ProjectRoot.Length + 1);
var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
if(_Files.Contains(relativePath))
{
Console.WriteLine("Skipping backup of {0}", relativePath);
return;
}
// Copy over
backupPath.Directory.Create();
if (file.Exists)
{
file.CopyTo(backupPath.FullName);
} else
{
// Make empty file
backupPath.Create().Close();
}
// Add to list
_Files.Add(relativePath);
}
/// <summary>
/// Reverts the changes made in this unit.
/// </summary>
public void Restore()
{
foreach(var relativePath in _Files)
{
Console.WriteLine("Restoring {0}", relativePath);
// Original version
var backupFile = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
var target = new FileInfo(Path.Combine(_Context.ProjectRoot, relativePath));
if (backupFile.Exists)
{
if (backupFile.Length > 0)
{
Console.WriteLine(" {0} => {1}", backupFile.FullName, target.FullName);
target.Directory.Create();
backupFile.CopyTo(target.FullName, true);
} else
{
Console.WriteLine(" x {0}", target.FullName);
if(target.Exists)
{
target.Delete();
}
}
} else {
Console.Error.WriteLine("Backup not found!");
}
}
}
}
}

View file

@ -58,11 +58,14 @@ namespace IPA
{
try
{
var backup = new BackupUnit(context);
// Copying
Console.WriteLine("Updating files... ");
var nativePluginFolder = Path.Combine(context.DataPathDst, "Plugins");
bool isFlat = Directory.Exists(nativePluginFolder) && Directory.GetFiles(nativePluginFolder).Any(f => f.EndsWith(".dll"));
CopyAll(new DirectoryInfo(context.DataPathSrc), new DirectoryInfo(context.DataPathDst), (from, to) => NativePluginInterceptor(from, to, new DirectoryInfo(nativePluginFolder), isFlat) );
bool force = !BackupManager.HasBackup(context) || context.Args.Contains("-f") || context.Args.Contains("--force");
CopyAll(new DirectoryInfo(context.DataPathSrc), new DirectoryInfo(context.DataPathDst), force, backup, (from, to) => NativePluginInterceptor(from, to, new DirectoryInfo(nativePluginFolder), isFlat) );
Console.WriteLine("Successfully updated files!");
@ -77,7 +80,7 @@ namespace IPA
if (!patchedModule.IsPatched)
{
Console.Write("Patching UnityEngine.dll... ");
BackupManager.MakeBackup(context.EngineFile);
backup.Add(context.EngineFile);
patchedModule.Patch();
Console.WriteLine("Done!");
}
@ -87,7 +90,7 @@ namespace IPA
if (!virtualizedModule.IsVirtualized)
{
Console.Write("Virtualizing Assembly-Csharp.dll... ");
BackupManager.MakeBackup(context.AssemblyFile);
backup.Add(context.AssemblyFile);
virtualizedModule.Virtualize();
Console.WriteLine("Done!");
}
@ -120,8 +123,8 @@ namespace IPA
private static void Revert(PatchContext context)
{
Console.Write("Restoring game assembly... ");
if(BackupManager.Restore(context.AssemblyFile))
Console.Write("Restoring backup... ");
if(BackupManager.Restore(context))
{
Console.WriteLine("Done!");
} else
@ -129,16 +132,7 @@ namespace IPA
Console.WriteLine("Already vanilla!");
}
Console.Write("Restoring unity engine... ");
if(BackupManager.Restore(context.EngineFile))
{
Console.WriteLine("Done!");
}
else
{
Console.WriteLine("Already vanilla!");
}
if (File.Exists(context.ShortcutPath))
{
Console.WriteLine("Deleting shortcut...");
@ -209,22 +203,23 @@ namespace IPA
yield return to;
}
public static void CopyAll(DirectoryInfo source, DirectoryInfo target, Func<FileInfo, FileInfo, IEnumerable<FileInfo>> interceptor = null)
public static void CopyAll(DirectoryInfo source, DirectoryInfo target, bool aggressive, BackupUnit backup, Func<FileInfo, FileInfo, IEnumerable<FileInfo>> interceptor = null)
{
if(interceptor == null)
{
interceptor = PassThroughInterceptor;
}
Directory.CreateDirectory(target.FullName);
// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
{
foreach(var targetFile in interceptor(fi, new FileInfo(Path.Combine(target.FullName, fi.Name)))) {
if (!targetFile.Exists || targetFile.LastWriteTimeUtc < fi.LastWriteTimeUtc)
if (!targetFile.Exists || targetFile.LastWriteTimeUtc < fi.LastWriteTimeUtc || aggressive)
{
targetFile.Directory.Create();
Console.WriteLine(@"Copying {0}", targetFile.FullName);
backup.Add(targetFile);
fi.CopyTo(targetFile.FullName, true);
}
}
@ -233,9 +228,8 @@ namespace IPA
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir, interceptor);
DirectoryInfo nextTargetSubDir = new DirectoryInfo(Path.Combine(target.FullName, diSourceSubDir.Name));
CopyAll(diSourceSubDir, nextTargetSubDir, aggressive, backup, interceptor);
}
}

View file

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyVersion("3.1.1.0")]
[assembly: AssemblyFileVersion("3.1.1.0")]