diff --git a/Wabbajack.Lib/AInstaller.cs b/Wabbajack.Lib/AInstaller.cs
index c2833ea1..6c8d7512 100644
--- a/Wabbajack.Lib/AInstaller.cs
+++ b/Wabbajack.Lib/AInstaller.cs
@@ -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);
}
+
+ ///
+ /// 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
+ ///
+ 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()
+ .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();
+
+ }
}
}
diff --git a/Wabbajack.Lib/MO2Compiler.cs b/Wabbajack.Lib/MO2Compiler.cs
index 9aa041d1..b13e67da 100644
--- a/Wabbajack.Lib/MO2Compiler.cs
+++ b/Wabbajack.Lib/MO2Compiler.cs
@@ -305,7 +305,7 @@ namespace Wabbajack.Lib
private void BuildArchivePatches(string archive_sha, IEnumerable group,
Dictionary 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());
diff --git a/Wabbajack.Lib/MO2Installer.cs b/Wabbajack.Lib/MO2Installer.cs
index 0620cfb4..3658ba1a 100644
--- a/Wabbajack.Lib/MO2Installer.cs
+++ b/Wabbajack.Lib/MO2Installer.cs
@@ -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
diff --git a/Wabbajack.Test/ACompilerTest.cs b/Wabbajack.Test/ACompilerTest.cs
index 4197ad33..fd86133e 100644
--- a/Wabbajack.Test/ACompilerTest.cs
+++ b/Wabbajack.Test/ACompilerTest.cs
@@ -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();
diff --git a/Wabbajack.Test/SanityTests.cs b/Wabbajack.Test/SanityTests.cs
index fc49e42a..22f2485a 100644
--- a/Wabbajack.Test/SanityTests.cs
+++ b/Wabbajack.Test/SanityTests.cs
@@ -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
+ {
+ { "/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()
diff --git a/Wabbajack.Test/TestUtils.cs b/Wabbajack.Test/TestUtils.cs
index abb10136..f39e95d3 100644
--- a/Wabbajack.Test/TestUtils.cs
+++ b/Wabbajack.Test/TestUtils.cs
@@ -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))
diff --git a/Wabbajack.VirtualFileSystem/Context.cs b/Wabbajack.VirtualFileSystem/Context.cs
index 0eba5c69..2137ce7d 100644
--- a/Wabbajack.VirtualFileSystem/Context.cs
+++ b/Wabbajack.VirtualFileSystem/Context.cs
@@ -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> StageWith(IEnumerable files)
+ public DisposableList StageWith(IEnumerable files)
{
return new DisposableList(Stage(files), files);
}