From f67a235274b647849ff7275b4e3cec2510da59ee Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Tue, 3 Dec 2019 21:12:08 -0700 Subject: [PATCH] Logs and exceptions as classes --- Compression.BSA.Test/BSATests.cs | 2 +- Wabbajack.Common/FileExtractor.cs | 18 ++++---- Wabbajack.Common/StatusFeed/AErrorMessage.cs | 15 +++++++ Wabbajack.Common/StatusFeed/AStatusMessage.cs | 15 +++++++ .../StatusFeed/Errors/7zipReturnError.cs | 33 ++++++++++++++ .../StatusFeed/Errors/FileExtractionError.cs | 22 ++++++++++ .../StatusFeed/Errors/UnconvertedError.cs | 21 +++++++++ Wabbajack.Common/StatusFeed/GenericInfo.cs | 25 +++++++++++ Wabbajack.Common/StatusFeed/IError.cs | 12 ++++++ Wabbajack.Common/StatusFeed/IInfo.cs | 13 ++++++ Wabbajack.Common/StatusFeed/IStatusMessage.cs | 15 +++++++ .../StatusFeed/IUserIntervention.cs | 43 +++++++++++++++++++ Wabbajack.Common/StatusUpdate.cs | 1 + Wabbajack.Common/Utils.cs | 32 +++++++++++--- Wabbajack.Common/Wabbajack.Common.csproj | 10 +++++ Wabbajack.Common/WorkQueue.cs | 16 ++++++- .../CompilationErrors/InvalidGameESMError.cs | 39 +++++++++++++++++ .../CompilationSteps/DeconstructBSAs.cs | 5 ++- Wabbajack.Lib/Downloaders/NexusDownloader.cs | 5 ++- Wabbajack.Lib/MO2Installer.cs | 11 ++--- .../ConfirmUpdateOfExistingInstall.cs | 43 +++++++++++++++++++ Wabbajack.Lib/Validation/ValidateModlist.cs | 5 --- Wabbajack.Lib/Wabbajack.Lib.csproj | 3 ++ Wabbajack.Lib/zEditIntegration.cs | 2 +- .../ListValidation.cs | 4 +- Wabbajack.Test/ACompilerTest.cs | 2 +- Wabbajack.Test/AVortexCompilerTest.cs | 2 +- Wabbajack.Test/EndToEndTests.cs | 2 +- .../VirtualFileSystemTests.cs | 2 +- Wabbajack/View Models/Compilers/CompilerVM.cs | 3 +- .../View Models/Installers/InstallerVM.cs | 3 +- Wabbajack/View Models/MainWindowVM.cs | 18 +++++++- Wabbajack/Views/Common/LogCpuView.xaml | 2 +- 33 files changed, 400 insertions(+), 44 deletions(-) create mode 100644 Wabbajack.Common/StatusFeed/AErrorMessage.cs create mode 100644 Wabbajack.Common/StatusFeed/AStatusMessage.cs create mode 100644 Wabbajack.Common/StatusFeed/Errors/7zipReturnError.cs create mode 100644 Wabbajack.Common/StatusFeed/Errors/FileExtractionError.cs create mode 100644 Wabbajack.Common/StatusFeed/Errors/UnconvertedError.cs create mode 100644 Wabbajack.Common/StatusFeed/GenericInfo.cs create mode 100644 Wabbajack.Common/StatusFeed/IError.cs create mode 100644 Wabbajack.Common/StatusFeed/IInfo.cs create mode 100644 Wabbajack.Common/StatusFeed/IStatusMessage.cs create mode 100644 Wabbajack.Common/StatusFeed/IUserIntervention.cs create mode 100644 Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs create mode 100644 Wabbajack.Lib/StatusMessages/ConfirmUpdateOfExistingInstall.cs diff --git a/Compression.BSA.Test/BSATests.cs b/Compression.BSA.Test/BSATests.cs index ddb2d779e..8e2d29163 100644 --- a/Compression.BSA.Test/BSATests.cs +++ b/Compression.BSA.Test/BSATests.cs @@ -31,7 +31,7 @@ public class BSATests public static void Setup(TestContext testContext) { Queue = new WorkQueue(); - Utils.LogMessages.Subscribe(f => testContext.WriteLine(f)); + Utils.LogMessages.Subscribe(f => testContext.WriteLine(f.ShortDescription)); if (!Directory.Exists(_stagingFolder)) Directory.CreateDirectory(_stagingFolder); diff --git a/Wabbajack.Common/FileExtractor.cs b/Wabbajack.Common/FileExtractor.cs index 2ef4b2aa4..77e9bd875 100644 --- a/Wabbajack.Common/FileExtractor.cs +++ b/Wabbajack.Common/FileExtractor.cs @@ -10,6 +10,8 @@ using ICSharpCode.SharpZipLib.GZip; using Newtonsoft.Json; using OMODFramework; +using Wabbajack.Common.StatusFeed; +using Wabbajack.Common.StatusFeed.Errors; namespace Wabbajack.Common { @@ -46,11 +48,11 @@ public static void ExtractAll(WorkQueue queue, string source, string dest) else if (source.EndsWith(".exe")) ExtractAllWithInno(source, dest); else - ExtractAllWith7Zip(source, dest); + ExtractAllWith7Zip(queue, source, dest); } catch (Exception ex) { - Utils.Log($"Error while extracting {source}"); + queue.Log($"Error while extracting {source}"); throw ex; } } @@ -152,14 +154,14 @@ private static void ExtractAllWithBSA(WorkQueue queue, string source, string des } catch (Exception ex) { - Utils.Log($"While Extracting {source}"); + queue.Log($"While Extracting {source}"); throw ex; } } - private static void ExtractAllWith7Zip(string source, string dest) + private static void ExtractAllWith7Zip(WorkQueue queue, string source, string dest) { - Utils.Log($"Extracting {Path.GetFileName(source)}"); + queue.Log(new GenericInfo($"Extracting {Path.GetFileName(source)}", $"The contents of {source} are being extracted to {dest} using 7zip.exe")); var info = new ProcessStartInfo { @@ -204,11 +206,11 @@ private static void ExtractAllWith7Zip(string source, string dest) } p.WaitForExit(); - if (p.ExitCode != 0) + if (p.ExitCode == 0) { - Utils.Log(p.StandardOutput.ReadToEnd()); - Utils.Log($"Extraction error extracting {source}"); + return; } + queue.Log(new _7zipReturnError(p.ExitCode, source, dest, p.StandardOutput.ReadToEnd())); } /// diff --git a/Wabbajack.Common/StatusFeed/AErrorMessage.cs b/Wabbajack.Common/StatusFeed/AErrorMessage.cs new file mode 100644 index 000000000..ce1a13492 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/AErrorMessage.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public abstract class AErrorMessage : Exception, IError + { + public DateTime Timestamp { get; } = DateTime.Now; + public abstract string ShortDescription { get; } + public abstract string ExtendedDescription { get; } + } +} diff --git a/Wabbajack.Common/StatusFeed/AStatusMessage.cs b/Wabbajack.Common/StatusFeed/AStatusMessage.cs new file mode 100644 index 000000000..38c25d209 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/AStatusMessage.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public abstract class AStatusMessage : IStatusMessage + { + public DateTime Timestamp { get; } = DateTime.Now; + public abstract string ShortDescription { get; } + public abstract string ExtendedDescription { get; } + } +} diff --git a/Wabbajack.Common/StatusFeed/Errors/7zipReturnError.cs b/Wabbajack.Common/StatusFeed/Errors/7zipReturnError.cs new file mode 100644 index 000000000..cf35f15a1 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/Errors/7zipReturnError.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed.Errors +{ + public class _7zipReturnError : AStatusMessage, IError + { + private string _destination; + private string _filename; + private int _code; + private string _7zip_output; + public override string ShortDescription => $"7Zip returned an error while executing"; + + public override string ExtendedDescription => + $@"7Zip.exe should always return 0 when it finishes executing. While extracting {_filename} 7Zip encountered some error and +instead returned {_code} which indicates there was an error. The archive might be corrupt or in a format that 7Zip cannot handle. Please verify the file is valid and that you +haven't run out of disk space in the {_destination} folder. + +7Zip Output: +{_7zip_output}"; + + public _7zipReturnError(int code, string filename, string destination, string output) + { + _code = code; + _filename = filename; + _destination = destination; + _7zip_output = output; + } + } +} diff --git a/Wabbajack.Common/StatusFeed/Errors/FileExtractionError.cs b/Wabbajack.Common/StatusFeed/Errors/FileExtractionError.cs new file mode 100644 index 000000000..bb110f02e --- /dev/null +++ b/Wabbajack.Common/StatusFeed/Errors/FileExtractionError.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed.Errors +{ + class FileExtractionError : AStatusMessage, IError + { + private string _filename; + private string _destination; + public override string ShortDescription { get; } + public override string ExtendedDescription { get; } + + public FileExtractionError(string filename, string destination) + { + _filename = filename; + _destination = destination; + } + } +} diff --git a/Wabbajack.Common/StatusFeed/Errors/UnconvertedError.cs b/Wabbajack.Common/StatusFeed/Errors/UnconvertedError.cs new file mode 100644 index 000000000..0fa64eda7 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/Errors/UnconvertedError.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed.Errors +{ + public class UnconvertedError : AErrorMessage + { + private string _msg; + + public UnconvertedError(string msg) + { + _msg = msg; + } + + public override string ShortDescription { get => _msg; } + public override string ExtendedDescription { get; } = ""; + } +} diff --git a/Wabbajack.Common/StatusFeed/GenericInfo.cs b/Wabbajack.Common/StatusFeed/GenericInfo.cs new file mode 100644 index 000000000..e78b63229 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/GenericInfo.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public class GenericInfo : AStatusMessage, IInfo + { + public override string ShortDescription { get; } + public override string ExtendedDescription { get;} + + public GenericInfo(string short_description, string long_description = "") + { + ShortDescription = short_description; + ExtendedDescription = long_description; + } + + public override string ToString() + { + return ShortDescription; + } + } +} diff --git a/Wabbajack.Common/StatusFeed/IError.cs b/Wabbajack.Common/StatusFeed/IError.cs new file mode 100644 index 000000000..7bbb1f439 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/IError.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public interface IError : IStatusMessage + { + } +} diff --git a/Wabbajack.Common/StatusFeed/IInfo.cs b/Wabbajack.Common/StatusFeed/IInfo.cs new file mode 100644 index 000000000..d8b0adc83 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/IInfo.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public interface IInfo : IStatusMessage + { + + } +} diff --git a/Wabbajack.Common/StatusFeed/IStatusMessage.cs b/Wabbajack.Common/StatusFeed/IStatusMessage.cs new file mode 100644 index 000000000..62a8f60d7 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/IStatusMessage.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + public interface IStatusMessage + { + DateTime Timestamp { get; } + string ShortDescription { get; } + string ExtendedDescription { get; } + } +} diff --git a/Wabbajack.Common/StatusFeed/IUserIntervention.cs b/Wabbajack.Common/StatusFeed/IUserIntervention.cs new file mode 100644 index 000000000..d3c253f66 --- /dev/null +++ b/Wabbajack.Common/StatusFeed/IUserIntervention.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Wabbajack.Common.StatusFeed +{ + /// + /// Defines a message that requires user interaction. The user must perform some action + /// or make a choice. + /// + public interface IUserIntervention : IStatusMessage + { + /// + /// The user didn't make a choice, so this action should be aborted + /// + void Cancel(); + + /// + /// The user has provided the required information. + /// + /// + void Resume(T result); + } + + /// + /// Defines a message that requires user interaction. The user must perform some action + /// or make a choice. + /// + public interface IUserIntervention : IStatusMessage + { + /// + /// The user didn't make a choice, so this action should be aborted + /// + void Cancel(); + + /// + /// Resume without any further information + /// + void Resume(); + } +} diff --git a/Wabbajack.Common/StatusUpdate.cs b/Wabbajack.Common/StatusUpdate.cs index 809eba74c..0ccba2a32 100644 --- a/Wabbajack.Common/StatusUpdate.cs +++ b/Wabbajack.Common/StatusUpdate.cs @@ -1,5 +1,6 @@ using System; using System.Reactive.Subjects; +using Wabbajack.Common.StatusFeed; namespace Wabbajack.Common { diff --git a/Wabbajack.Common/Utils.cs b/Wabbajack.Common/Utils.cs index 6c0c9b58c..6df6748bf 100644 --- a/Wabbajack.Common/Utils.cs +++ b/Wabbajack.Common/Utils.cs @@ -16,6 +16,8 @@ using ICSharpCode.SharpZipLib.BZip2; using IniParser; using Newtonsoft.Json; +using ReactiveUI; +using Wabbajack.Common.StatusFeed; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using Directory = System.IO.Directory; @@ -44,8 +46,8 @@ static Utils() File.Delete(LogFile); } - private static readonly Subject LoggerSubj = new Subject(); - public static IObservable LogMessages => LoggerSubj; + private static readonly Subject LoggerSubj = new Subject(); + public static IObservable LogMessages => LoggerSubj; private static readonly string[] Suffix = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}; // Longs run out around EB @@ -53,15 +55,30 @@ static Utils() private static DateTime _startTime; + public static void Log(string msg) + { + Log(new GenericInfo(msg)); + } + + public static T Log(T msg) where T : IStatusMessage { lock (_lock) { - msg = $"{(DateTime.Now - _startTime).TotalSeconds:0.##} - {msg}"; - File.AppendAllText(LogFile, msg + "\r\n"); } LoggerSubj.OnNext(msg); + return msg; + } + + public static void Error(AErrorMessage err) + { + lock (_lock) + { + File.AppendAllText(LogFile, err.ShortDescription + "\r\n"); + } + LoggerSubj.OnNext(err); + throw err; } public static void LogToFile(string msg) @@ -668,10 +685,11 @@ public static bool TryGetPatch(string foundHash, string fileHash, out byte[] ePa return false; } + /* public static void Warning(string s) { Log($"WARNING: {s}"); - } + }*/ public static TV GetOrDefault(this Dictionary dict, TK key) { @@ -708,11 +726,12 @@ public static T ViaJSON(this T tv) return tv.ToJSON().FromJSONString(); } + /* public static void Error(string msg) { Log(msg); throw new Exception(msg); - } + }*/ public static Stream GetEmbeddedResourceStream(string name) { @@ -738,7 +757,6 @@ public static T FromYaml(this string s) .Build(); return d.Deserialize(new StringReader(s)); } - public static void LogStatus(string s) { Status(s); diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj index 89674ccf0..3631d2f08 100644 --- a/Wabbajack.Common/Wabbajack.Common.csproj +++ b/Wabbajack.Common/Wabbajack.Common.csproj @@ -108,6 +108,16 @@ + + + + + + + + + + diff --git a/Wabbajack.Common/WorkQueue.cs b/Wabbajack.Common/WorkQueue.cs index cab76814f..67a885b5d 100644 --- a/Wabbajack.Common/WorkQueue.cs +++ b/Wabbajack.Common/WorkQueue.cs @@ -5,6 +5,7 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; +using Wabbajack.Common.StatusFeed; namespace Wabbajack.Common { @@ -21,13 +22,26 @@ public class WorkQueue : IDisposable private readonly Subject _Status = new Subject(); public IObservable Status => _Status; - public List Threads { get; private set; } + private static readonly Subject _messages = new Subject(); + public IObservable Messages => _messages; + + public static List Threads { get; private set; } public WorkQueue(int threadCount = 0) { StartThreads(threadCount == 0 ? Environment.ProcessorCount : threadCount); } + public void Log(IStatusMessage msg) + { + _messages.OnNext(msg); + } + + public void Log(string msg) + { + _messages.OnNext(new GenericInfo(msg)); + } + private void StartThreads(int threadCount) { ThreadCount = threadCount; diff --git a/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs b/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs new file mode 100644 index 000000000..52ebcb3c0 --- /dev/null +++ b/Wabbajack.Lib/CompilationSteps/CompilationErrors/InvalidGameESMError.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Alphaleonis.Win32.Filesystem; +using Wabbajack.Common.StatusFeed; + +namespace Wabbajack.Lib.CompilationSteps.CompilationErrors +{ + public class InvalidGameESMError : AErrorMessage + { + private readonly string _hash; + private readonly string _path; + private readonly CleanedESM _esm; + private string _gameFileName => Path.GetFileName(_esm.To); + public override string ShortDescription + { + get => + $"Game file {_gameFileName} has a hash of {_hash} which does not match the expected value of {_esm.SourceESMHash}"; + } + + public override string ExtendedDescription + { + get => + $@"This modlist is setup to perform automatic cleaning of the stock game file {_gameFileName} in order to perform this cleaning Wabbajack must first verify that the +source file is in the correct state. It seems that the file in your game directory has a hash of {_hash} instead of the expect hash of {_esm.SourceESMHash}. This could be caused by +the modlist expecting a different of the game than you currently have installed, or perhaps you have already cleaned the file. You could attempt to fix this error by re-installing +the game, and then attempting to re-install this modlist. Also verify that the version of the game you have installed matches the version expected by this modlist."; + } + + public InvalidGameESMError(CleanedESM esm, string hash, string path) + { + _hash = hash; + _path = path; + _esm = esm; + } + } +} diff --git a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs index 892357b84..d1877a5f2 100644 --- a/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs +++ b/Wabbajack.Lib/CompilationSteps/DeconstructBSAs.cs @@ -5,6 +5,7 @@ using Compression.BSA; using Newtonsoft.Json; using Wabbajack.Common; +using Wabbajack.Common.StatusFeed.Errors; namespace Wabbajack.Lib.CompilationSteps { @@ -72,7 +73,7 @@ public override Directive Run(RawSourceFile source) foreach (var match in matches) { if (match is IgnoredDirectly) - Utils.Error($"File required for BSA {source.Path} creation doesn't exist: {match.To}"); + Utils.Error(new UnconvertedError($"File required for BSA {source.Path} creation doesn't exist: {match.To}")); _mo2Compiler.ExtraFiles.Add(match); } @@ -100,4 +101,4 @@ public ICompilationStep CreateStep(ACompiler compiler) } } } -} \ No newline at end of file +} diff --git a/Wabbajack.Lib/Downloaders/NexusDownloader.cs b/Wabbajack.Lib/Downloaders/NexusDownloader.cs index b925852e8..b17d1ee52 100644 --- a/Wabbajack.Lib/Downloaders/NexusDownloader.cs +++ b/Wabbajack.Lib/Downloaders/NexusDownloader.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Wabbajack.Common; +using Wabbajack.Common.StatusFeed.Errors; using Wabbajack.Lib.NexusApi; using Wabbajack.Lib.Validation; @@ -45,12 +46,12 @@ public void Prepare() var status = client.GetUserStatus(); if (!client.IsAuthenticated) { - Utils.Error($"Authenticating for the Nexus failed. A nexus account is required to automatically download mods."); + Utils.Error(new UnconvertedError($"Authenticating for the Nexus failed. A nexus account is required to automatically download mods.")); return; } if (status.is_premium) return; - Utils.Error($"Automated installs with Wabbajack requires a premium nexus account. {client.Username} is not a premium account."); + Utils.Error(new UnconvertedError($"Automated installs with Wabbajack requires a premium nexus account. {client.Username} is not a premium account.")); } public class State : AbstractDownloadState diff --git a/Wabbajack.Lib/MO2Installer.cs b/Wabbajack.Lib/MO2Installer.cs index ad4a71a11..5c6f5dc28 100644 --- a/Wabbajack.Lib/MO2Installer.cs +++ b/Wabbajack.Lib/MO2Installer.cs @@ -4,8 +4,10 @@ using System.Text; using System.Windows; using Wabbajack.Common; +using Wabbajack.Lib.CompilationSteps.CompilationErrors; using Wabbajack.Lib.Downloaders; using Wabbajack.Lib.NexusApi; +using Wabbajack.Lib.StatusMessages; using Wabbajack.Lib.Validation; using Directory = Alphaleonis.Win32.Filesystem.Directory; using File = Alphaleonis.Win32.Filesystem.File; @@ -59,12 +61,7 @@ protected override bool _Begin() 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" + - " with installation? If you do, you may render both your existing install and the new modlist inoperable.", - "Existing MO2 installation in install folder", - MessageBoxButton.YesNo, - MessageBoxImage.Exclamation) == MessageBoxResult.No) + if (Utils.Log(new ConfirmUpdateOfExistingInstall { ModListName = ModList.Name, OutputFolder = OutputFolder}).Task.Result == ConfirmUpdateOfExistingInstall.Choice.Abort) { Utils.Log("Existing installation at the request of the user, existing mods folder found."); return false; @@ -143,7 +140,7 @@ private void ValidateGameESMs() var hash = gameFile.FileHash(); if (hash != esm.SourceESMHash) { - Utils.Error("Game ESM hash doesn't match, is the ESM already cleaned? Please verify your local game files."); + Utils.Error(new InvalidGameESMError(esm, hash, gameFile)); } } } diff --git a/Wabbajack.Lib/StatusMessages/ConfirmUpdateOfExistingInstall.cs b/Wabbajack.Lib/StatusMessages/ConfirmUpdateOfExistingInstall.cs new file mode 100644 index 000000000..4cc00a828 --- /dev/null +++ b/Wabbajack.Lib/StatusMessages/ConfirmUpdateOfExistingInstall.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Wabbajack.Common.StatusFeed; + +namespace Wabbajack.Lib.StatusMessages +{ + public class ConfirmUpdateOfExistingInstall : AStatusMessage, IUserIntervention + { + public enum Choice + { + Continue, + Abort + } + + public string OutputFolder { get; set; } + public string ModListName { get; set; } + public override string ShortDescription { get; } = "Do you want to overwrite existing files?"; + + private TaskCompletionSource _source = new TaskCompletionSource(); + public Task Task => _source.Task; + + public override string ExtendedDescription + { + get => + $@"There appears to be a modlist already installed in the output folder. If you continue with the install, +Any files that exist in {OutputFolder} will be changed to match the files found in the {ModListName} modlist. This means that save games will be removed, custom settings +will be reverted. Are you sure you wish to continue?"; + } + + public void Cancel() + { + _source.SetResult(Choice.Abort); + } + + public void Resume() + { + _source.SetResult(Choice.Continue); + } + } +} diff --git a/Wabbajack.Lib/Validation/ValidateModlist.cs b/Wabbajack.Lib/Validation/ValidateModlist.cs index 72f6dc9fa..51ff03de6 100644 --- a/Wabbajack.Lib/Validation/ValidateModlist.cs +++ b/Wabbajack.Lib/Validation/ValidateModlist.cs @@ -77,11 +77,6 @@ public static void RunValidation(ModList modlist) /// public Permissions FilePermissions(NexusDownloader.State mod) { - if (mod.Author == null || mod.GameName == null || mod.ModID == null || mod.FileID == null) - { - Utils.Error($"Error: Null data for {mod.Author} {mod.GameName} {mod.ModID} {mod.FileID}"); - } - var author_permissions = AuthorPermissions.GetOrDefault(mod.Author)?.Permissions; var game_permissions = AuthorPermissions.GetOrDefault(mod.Author)?.Games.GetOrDefault(mod.GameName)?.Permissions; var mod_permissions = AuthorPermissions.GetOrDefault(mod.Author)?.Games.GetOrDefault(mod.GameName)?.Mods.GetOrDefault(mod.ModID) diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj index cb489f212..5dd11d0ab 100644 --- a/Wabbajack.Lib/Wabbajack.Lib.csproj +++ b/Wabbajack.Lib/Wabbajack.Lib.csproj @@ -86,6 +86,7 @@ + @@ -139,6 +140,7 @@ + @@ -222,5 +224,6 @@ Designer + \ No newline at end of file diff --git a/Wabbajack.Lib/zEditIntegration.cs b/Wabbajack.Lib/zEditIntegration.cs index e273e7160..c35ab2abf 100644 --- a/Wabbajack.Lib/zEditIntegration.cs +++ b/Wabbajack.Lib/zEditIntegration.cs @@ -60,7 +60,7 @@ public IncludeZEditPatches(ACompiler compiler) : base(compiler) merges.Where(m => m.Count() > 1) .Do(m => { - Utils.Warning( + Utils.Log( $"WARNING, you have two patches named {m.Key.name}\\{m.Key.filename} in your zEdit profiles. We'll pick one at random, this probably isn't what you want."); }); diff --git a/Wabbajack.Test.ListValidation/ListValidation.cs b/Wabbajack.Test.ListValidation/ListValidation.cs index cb25265b4..93b8f970b 100644 --- a/Wabbajack.Test.ListValidation/ListValidation.cs +++ b/Wabbajack.Test.ListValidation/ListValidation.cs @@ -17,7 +17,7 @@ public class ListValidation [ClassInitialize] public static void SetupNexus(TestContext context) { - Utils.LogMessages.Subscribe(context.WriteLine); + Utils.LogMessages.Subscribe(m => context.WriteLine(m.ToString())); var api = new NexusApiClient(); api.ClearUpdatedModsInCache(); } @@ -27,7 +27,7 @@ public static void SetupNexus(TestContext context) public void Setup() { Directory.CreateDirectory(Consts.ModListDownloadFolder); - Utils.LogMessages.Subscribe(s => TestContext.WriteLine(s)); + Utils.LogMessages.Subscribe(s => TestContext.WriteLine(s.ToString())); Queue = new WorkQueue(); } diff --git a/Wabbajack.Test/ACompilerTest.cs b/Wabbajack.Test/ACompilerTest.cs index a92035f55..af0b37e72 100644 --- a/Wabbajack.Test/ACompilerTest.cs +++ b/Wabbajack.Test/ACompilerTest.cs @@ -18,7 +18,7 @@ public void TestInitialize() utils = new TestUtils(); utils.Game = Game.SkyrimSpecialEdition; - Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f)); + Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f.ShortDescription)); } diff --git a/Wabbajack.Test/AVortexCompilerTest.cs b/Wabbajack.Test/AVortexCompilerTest.cs index af5779707..778cae5de 100644 --- a/Wabbajack.Test/AVortexCompilerTest.cs +++ b/Wabbajack.Test/AVortexCompilerTest.cs @@ -22,7 +22,7 @@ public void TestInitialize() Game = Game.DarkestDungeon }; - Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f)); + Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f.ShortDescription)); } [TestCleanup] diff --git a/Wabbajack.Test/EndToEndTests.cs b/Wabbajack.Test/EndToEndTests.cs index 16d7b0e91..7395403d0 100644 --- a/Wabbajack.Test/EndToEndTests.cs +++ b/Wabbajack.Test/EndToEndTests.cs @@ -30,7 +30,7 @@ public void TestInitialize() utils = new TestUtils(); utils.Game = Game.SkyrimSpecialEdition; - Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f)); + Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f.ToString())); if (!Directory.Exists(DOWNLOAD_FOLDER)) Directory.CreateDirectory(DOWNLOAD_FOLDER); diff --git a/Wabbajack.VirtualFileSystem.Test/VirtualFileSystemTests.cs b/Wabbajack.VirtualFileSystem.Test/VirtualFileSystemTests.cs index 6d3c73baa..6318b011f 100644 --- a/Wabbajack.VirtualFileSystem.Test/VirtualFileSystemTests.cs +++ b/Wabbajack.VirtualFileSystem.Test/VirtualFileSystemTests.cs @@ -22,7 +22,7 @@ public class VFSTests [TestInitialize] public void Setup() { - Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f)); + Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f.ShortDescription)); if (Directory.Exists(VFS_TEST_DIR)) Utils.DeleteDirectory(VFS_TEST_DIR); Directory.CreateDirectory(VFS_TEST_DIR); diff --git a/Wabbajack/View Models/Compilers/CompilerVM.cs b/Wabbajack/View Models/Compilers/CompilerVM.cs index af540398f..e25ddbbfb 100644 --- a/Wabbajack/View Models/Compilers/CompilerVM.cs +++ b/Wabbajack/View Models/Compilers/CompilerVM.cs @@ -8,6 +8,7 @@ using System.Reactive.Linq; using System.Windows.Media.Imaging; using Wabbajack.Common; +using Wabbajack.Common.StatusFeed; using Wabbajack.Lib; namespace Wabbajack @@ -36,7 +37,7 @@ public class CompilerVM : ViewModel public ObservableCollectionExtended StatusList { get; } = new ObservableCollectionExtended(); - public ObservableCollectionExtended Log => MWVM.Log; + public ObservableCollectionExtended Log => MWVM.Log; public IReactiveCommand BackCommand { get; } diff --git a/Wabbajack/View Models/Installers/InstallerVM.cs b/Wabbajack/View Models/Installers/InstallerVM.cs index 5cfaec6e6..1c35d60fb 100644 --- a/Wabbajack/View Models/Installers/InstallerVM.cs +++ b/Wabbajack/View Models/Installers/InstallerVM.cs @@ -18,6 +18,7 @@ using DynamicData; using DynamicData.Binding; using System.Reactive; +using Wabbajack.Common.StatusFeed; namespace Wabbajack { @@ -72,7 +73,7 @@ public class InstallerVM : ViewModel public float PercentCompleted => _percentCompleted.Value; public ObservableCollectionExtended StatusList { get; } = new ObservableCollectionExtended(); - public ObservableCollectionExtended Log => MWVM.Log; + public ObservableCollectionExtended Log => MWVM.Log; private readonly ObservableAsPropertyHelper _TargetManager; public ModManager? TargetManager => _TargetManager.Value; diff --git a/Wabbajack/View Models/MainWindowVM.cs b/Wabbajack/View Models/MainWindowVM.cs index 76faa530f..c1f1af143 100644 --- a/Wabbajack/View Models/MainWindowVM.cs +++ b/Wabbajack/View Models/MainWindowVM.cs @@ -6,8 +6,11 @@ using System.Linq; using System.Reactive.Disposables; using System.Reactive.Linq; +using System.Windows; using Wabbajack.Common; +using Wabbajack.Common.StatusFeed; using Wabbajack.Lib; +using Wabbajack.Lib.StatusMessages; namespace Wabbajack { @@ -24,7 +27,7 @@ public class MainWindowVM : ViewModel [Reactive] public ViewModel ActivePane { get; set; } - public ObservableCollectionExtended Log { get; } = new ObservableCollectionExtended(); + public ObservableCollectionExtended Log { get; } = new ObservableCollectionExtended(); public readonly Lazy Compiler; public readonly Lazy Installer; @@ -52,6 +55,10 @@ public MainWindowVM(MainWindow mainWindow, MainSettings settings) .Subscribe() .DisposeWith(CompositeDisposable); + Utils.LogMessages + .OfType() + .Subscribe(msg => ConfirmUpdate(msg)); + if (IsStartingFromModlist(out var path)) { Installer.Value.ModListLocation.TargetPath = path; @@ -64,6 +71,15 @@ public MainWindowVM(MainWindow mainWindow, MainSettings settings) } } + private void ConfirmUpdate(ConfirmUpdateOfExistingInstall msg) + { + var result = MessageBox.Show(msg.ExtendedDescription, msg.ShortDescription, MessageBoxButton.OKCancel); + if (result == MessageBoxResult.OK) + msg.Resume(); + else + msg.Cancel(); + } + private static bool IsStartingFromModlist(out string modlistPath) { string[] args = Environment.GetCommandLineArgs(); diff --git a/Wabbajack/Views/Common/LogCpuView.xaml b/Wabbajack/Views/Common/LogCpuView.xaml index 79d56c440..cb5b23b57 100644 --- a/Wabbajack/Views/Common/LogCpuView.xaml +++ b/Wabbajack/Views/Common/LogCpuView.xaml @@ -29,7 +29,7 @@ ScrollViewer.HorizontalScrollBarVisibility="Disabled"> - +