added patching support

This commit is contained in:
Timothy Baldridge 2019-07-21 21:36:25 -06:00
parent e4ca3cd01e
commit 1466be47c0
3 changed files with 75 additions and 4 deletions

View File

@ -170,6 +170,7 @@ namespace Wabbajack.Common
public dynamic IniData;
public string Name;
public string Meta;
public string AbsolutePath;
}
/// <summary>

View File

@ -120,5 +120,14 @@ namespace Wabbajack.Common
}
}
public static byte[] ReadAll(this Stream ins)
{
using (var ms = new MemoryStream())
{
ins.CopyTo(ms);
return ms.ToArray();
}
}
}
}

View File

@ -64,7 +64,7 @@ namespace Wabbajack
public Action<string, long, long> Progress_Function { get; }
public List<Directive> InstallDirectives { get; private set; }
public List<Archive> SelectedArchives { get; private set; }
public List<RawSourceFile> AllFiles { get; private set; }
public List<IndexedArchive> IndexedArchives;
@ -111,6 +111,7 @@ namespace Wabbajack
{
var info = metaname.FromJSON<IndexedArchive>();
info.Name = Path.GetFileName(file);
info.AbsolutePath = file;
var ini_name = file + ".meta";
if (ini_name.FileExists())
@ -163,13 +164,13 @@ namespace Wabbajack
.Where(p => p.FileExists())
.Select(p => new RawSourceFile() { Path = Path.Combine(Consts.GameFolderFilesDir, p.RelativeTo(GamePath)), AbsolutePath = p });
var all_files = mo2_files.Concat(game_files).ToList();
AllFiles = mo2_files.Concat(game_files).ToList();
Info("Found {0} files to build into mod list", all_files.Count);
Info("Found {0} files to build into mod list", AllFiles.Count);
var stack = MakeStack();
var results = all_files.AsParallel().Select(f => RunStack(stack, f)).ToList();
var results = AllFiles.AsParallel().Select(f => RunStack(stack, f)).ToList();
var nomatch = results.OfType<NoMatch>();
Info("No match for {0} files", nomatch.Count());
@ -179,11 +180,71 @@ namespace Wabbajack
InstallDirectives = results.Where(i => !(i is IgnoredDirectly)).ToList();
GatherArchives();
BuildPatches();
results.ToJSON("out.json");
}
/// <summary>
/// Fills in the Patch fields in files that require them
/// </summary>
private void BuildPatches()
{
var groups = InstallDirectives.OfType<PatchedFromArchive>()
.GroupBy(p => p.ArchiveHash)
.ToList();
Info("Patching building patches from {0} archives", groups.Count);
var absolute_paths = AllFiles.ToDictionary(e => e.Path, e => e.AbsolutePath);
Parallel.ForEach(groups, group => BuildArchivePatches(group.Key, group, absolute_paths));
if (InstallDirectives.OfType<PatchedFromArchive>().FirstOrDefault(f => f.Patch == null) != null)
{
Error("Missing patches after generation, this should not happen");
}
}
private void BuildArchivePatches(string archive_sha, IEnumerable<PatchedFromArchive> group, Dictionary<string, string> absolute_paths)
{
var archive = IndexedArchives.First(a => a.Hash == archive_sha);
var paths = group.Select(g => g.From).ToHashSet();
var streams = new Dictionary<string, MemoryStream>();
Info("Etracting Patch Files from {0}", archive.Name);
// First we fetch the source files from the input archive
using (var a = new ArchiveFile(archive.AbsolutePath))
{
a.Extract(entry =>
{
if (!paths.Contains(entry.FileName)) return null;
var result = new MemoryStream();
streams.Add(entry.FileName, result);
return result;
}, false);
}
var extracted = streams.ToDictionary(k => k.Key, v => v.Value.ToArray());
// Now Create the patches
Parallel.ForEach(group, entry =>
{
Info("Patching {0}", entry.To);
var ss = extracted[entry.From];
using (var origin = new MemoryStream(ss))
using (var dest = File.OpenRead(absolute_paths[entry.To]))
using (var output = new MemoryStream())
{
var a = origin.ReadAll();
var b = dest.ReadAll();
BSDiff.Create(a, b, output);
entry.Patch = output.ToArray().ToBase64();
}
});
}
private void GatherArchives()
{
var archives = IndexedArchives.GroupBy(a => a.Hash).ToDictionary(k => k.Key, k => k.First());