Browse Source

Implement better backup mechanism.

tags/3,1b
Eusth 7 years ago
parent
commit
06c85a93a5
6 changed files with 164 additions and 72 deletions
  1. +1
    -0
      IPA/IPA.csproj
  2. +5
    -2
      IPA/PatchContext.cs
  3. +12
    -46
      IPA/Patcher/BackupManager.cs
  4. +128
    -0
      IPA/Patcher/BackupUnit.cs
  5. +16
    -22
      IPA/Program.cs
  6. +2
    -2
      IPA/Properties/AssemblyInfo.cs

+ 1
- 0
IPA/IPA.csproj 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" />


+ 5
- 2
IPA/PatchContext.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;
}
}


+ 12
- 46
IPA/Patcher/BackupManager.cs 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));
}
private static string GetBackupName(string file)
{
string backup = file + ".Original";

if (File.Exists(backup))
{
int i = 1;
string backupBase = backup;
while (File.Exists(backup))
{
backup = backupBase + i++;
}
}
return backup;
return new DirectoryInfo(context.BackupPath)
.GetDirectories()
.OrderByDescending(p => p.Name)
.Select(p => BackupUnit.FromDirectory(p, context))
.FirstOrDefault();
}

public static string FindLatestBackup(string file)
public static bool HasBackup(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;
return FindLatestBackup(context) != null;
}
public static bool Restore(string file)
public static bool Restore(PatchContext context)
{
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
- 0
IPA/Patcher/BackupUnit.cs 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!");
}
}
}

}
}

+ 16
- 22
IPA/Program.cs 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);
}
}



+ 2
- 2
IPA/Properties/AssemblyInfo.cs 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")]

Loading…
Cancel
Save