Optimize modlists before installation. Also fixed an async bug in VFS.

This commit is contained in:
Timothy Baldridge 2019-11-17 22:21:24 -07:00
parent 50bbd0eb1f
commit 13de3913da
7 changed files with 103 additions and 4 deletions

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Windows.Navigation;
using Wabbajack.Common;
using Wabbajack.Lib.Downloaders;
using Wabbajack.VirtualFileSystem;
@ -160,6 +161,9 @@ namespace Wabbajack.Lib
File.Move(from, to);
else
File.Copy(from, to);
// If we don't do this, the file will use the last-modified date of the file when it was compressed
// into an archive, which isn't really what we want in the case of files installed archives
File.SetLastWriteTime(to, DateTime.Now);
}
vFiles.GroupBy(f => f.FromFile)
@ -285,5 +289,40 @@ namespace Wabbajack.Lib
File.WriteAllText(cache, e.FileHash());
return HashArchive(e);
}
/// <summary>
/// The user may already have some files in the OutputFolder. If so we can go through these and
/// figure out which need to be updated, deleted, or left alone
/// </summary>
public void OptimizeModlist()
{
Utils.Log("Optimizing Modlist directives");
var indexed = ModList.Directives.ToDictionary(d => d.To);
indexed.Values.PMap(Queue, d =>
{
// Bit backwards, but we want to return null for
// all files we *want* installed. We return the files
// to remove from the install list.
var path = Path.Combine(OutputFolder, d.To);
if (!File.Exists(path)) return null;
var fi = new FileInfo(path);
if (fi.Length != d.Size) return null;
return path.FileHash() == d.Hash ? d : null;
}).Where(d => d != null)
.Do(d => indexed.Remove(d.To));
Utils.Log($"Optimized {ModList.Directives.Count} directives to {indexed.Count} required");
var requiredArchives = indexed.Values.OfType<FromArchive>()
.GroupBy(d => d.ArchiveHashPath[0])
.Select(d => d.Key)
.ToHashSet();
ModList.Archives = ModList.Archives.Where(a => requiredArchives.Contains(a.Hash)).ToList();
ModList.Directives = indexed.Values.ToList();
}
}
}

View File

@ -305,7 +305,7 @@ namespace Wabbajack.Lib
private void BuildArchivePatches(string archive_sha, IEnumerable<PatchedFromArchive> group,
Dictionary<string, string> absolute_paths)
{
using (var files = VFS.StageWith(group.Select(g => VFS.Index.FileForArchiveHashPath(g.ArchiveHashPath))).Result)
using (var files = VFS.StageWith(group.Select(g => VFS.Index.FileForArchiveHashPath(g.ArchiveHashPath))))
{
var by_path = files.GroupBy(f => string.Join("|", f.FilesInFullPath.Skip(1).Select(i => i.Name)))
.ToDictionary(f => f.Key, f => f.First());

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Forms.VisualStyles;
using Wabbajack.Common;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi;
@ -17,6 +18,8 @@ namespace Wabbajack.Lib
{
public class MO2Installer : AInstaller
{
public bool WarnOnOverwrite { get; set; } = true;
public MO2Installer(string archive, ModList mod_list, string output_folder)
{
ModManager = ModManager.MO2;
@ -52,7 +55,7 @@ namespace Wabbajack.Lib
Directory.CreateDirectory(OutputFolder);
Directory.CreateDirectory(DownloadFolder);
if (Directory.Exists(Path.Combine(OutputFolder, "mods")))
if (Directory.Exists(Path.Combine(OutputFolder, "mods")) && WarnOnOverwrite)
{
if (MessageBox.Show(
"There already appears to be a Mod Organizer 2 install in this folder, are you sure you wish to continue" +
@ -66,6 +69,7 @@ namespace Wabbajack.Lib
}
}
OptimizeModlist();
HashArchives();
DownloadArchives();
@ -99,6 +103,7 @@ namespace Wabbajack.Lib
return true;
}
private void InstallIncludedDownloadMetas()
{
ModList.Directives

View File

@ -57,6 +57,7 @@ namespace Wabbajack.Test
{
var modlist = MO2Installer.LoadFromFile(compiler.ModListOutputFile);
var installer = new MO2Installer(compiler.ModListOutputFile, modlist, utils.InstallFolder);
installer.WarnOnOverwrite = false;
installer.DownloadFolder = utils.DownloadsFolder;
installer.GameFolder = utils.GameFolder;
installer.Begin().Wait();

View File

@ -55,6 +55,55 @@ namespace Wabbajack.Test
utils.VerifyInstalledFile(mod, @"Data\scripts\test.pex.copy");
}
[TestMethod]
public void TestUpdating()
{
var profile = utils.AddProfile();
var mod = utils.AddMod();
var unchanged = utils.AddModFile(mod, @"Data\scripts\unchanged.pex", 10);
var deleted = utils.AddModFile(mod, @"Data\scripts\deleted.pex", 10);
var modified = utils.AddModFile(mod, @"Data\scripts\modified.pex", 10);
utils.Configure();
utils.AddManualDownload(
new Dictionary<string, byte[]>
{
{ "/baz/unchanged.pex", File.ReadAllBytes(unchanged) },
{ "/baz/deleted.pex", File.ReadAllBytes(deleted) },
{ "/baz/modified.pex", File.ReadAllBytes(modified) },
});
CompileAndInstall(profile);
utils.VerifyInstalledFile(mod, @"Data\scripts\unchanged.pex");
utils.VerifyInstalledFile(mod, @"Data\scripts\deleted.pex");
utils.VerifyInstalledFile(mod, @"Data\scripts\modified.pex");
var unchanged_path = utils.PathOfInstalledFile(mod, @"Data\scripts\unchanged.pex");
var deleted_path = utils.PathOfInstalledFile(mod, @"Data\scripts\deleted.pex");
var modified_path = utils.PathOfInstalledFile(mod, @"Data\scripts\modified.pex");
var unchanged_modified = File.GetLastWriteTime(unchanged_path);
var modified_modified = File.GetLastWriteTime(modified_path);
File.WriteAllText(modified_path, "random data");
File.Delete(deleted_path);
CompileAndInstall(profile);
utils.VerifyInstalledFile(mod, @"Data\scripts\unchanged.pex");
utils.VerifyInstalledFile(mod, @"Data\scripts\deleted.pex");
utils.VerifyInstalledFile(mod, @"Data\scripts\modified.pex");
Assert.AreEqual(unchanged_modified, File.GetLastWriteTime(unchanged_path));
Assert.AreNotEqual(modified_modified, File.GetLastWriteTime(modified_path));
}
[TestMethod]
public void CleanedESMTest()

View File

@ -182,6 +182,11 @@ namespace Wabbajack.Test
}
}
public string PathOfInstalledFile(string mod, string file)
{
return Path.Combine(InstallFolder, "mods", mod, file);
}
public void VerifyAllFiles()
{
foreach (var dest_file in Directory.EnumerateFiles(InstallFolder, "*", DirectoryEnumerationOptions.Recursive))

View File

@ -139,7 +139,7 @@ namespace Wabbajack.VirtualFileSystem
{
var magic = Encoding.ASCII.GetString(br.ReadBytes(Encoding.ASCII.GetBytes(Magic).Length));
var fileVersion = br.ReadUInt64();
if (fileVersion != FileVersion || magic != magic)
if (fileVersion != FileVersion || magic != Magic)
throw new InvalidDataException("Bad Data Format");
var numFiles = br.ReadUInt64();
@ -223,7 +223,7 @@ namespace Wabbajack.VirtualFileSystem
}
}
public async Task<DisposableList<VirtualFile>> StageWith(IEnumerable<VirtualFile> files)
public DisposableList<VirtualFile> StageWith(IEnumerable<VirtualFile> files)
{
return new DisposableList<VirtualFile>(Stage(files), files);
}