Implement better backup mechanism.
This commit is contained in:
parent
1f298a4857
commit
06c85a93a5
6 changed files with 167 additions and 75 deletions
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
private static string GetBackupName(string file)
|
||||
public static bool HasBackup(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;
|
||||
return FindLatestBackup(context) != null;
|
||||
}
|
||||
|
||||
public static string FindLatestBackup(string file)
|
||||
public static bool Restore(PatchContext context)
|
||||
{
|
||||
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
128
IPA/Patcher/BackupUnit.cs
Normal 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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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,15 +132,6 @@ 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))
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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")]
|
||||
|
|
Loading…
Reference in a new issue