diff --git a/Branding/PNGs/Wabba_Mouth.png b/Branding/PNGs/Wabba_Mouth.png
new file mode 100644
index 00000000..f2d2615b
Binary files /dev/null and b/Branding/PNGs/Wabba_Mouth.png differ
diff --git a/Branding/Project files/Wabba_Mouth.psd b/Branding/Project files/Wabba_Mouth.psd
new file mode 100644
index 00000000..c068e5b7
Binary files /dev/null and b/Branding/Project files/Wabba_Mouth.psd differ
diff --git a/Compression.BSA.Test/Compression.BSA.Test.csproj b/Compression.BSA.Test/Compression.BSA.Test.csproj
index 9dd108ef..83abb030 100644
--- a/Compression.BSA.Test/Compression.BSA.Test.csproj
+++ b/Compression.BSA.Test/Compression.BSA.Test.csproj
@@ -75,12 +75,6 @@
true
-
- ..\packages\SharpZipLib.1.2.0\lib\net45\ICSharpCode.SharpZipLib.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
@@ -96,7 +90,6 @@
-
@@ -104,5 +97,13 @@
Compression.BSA
+
+
+ 12.0.2
+
+
+ 1.2.0
+
+
\ No newline at end of file
diff --git a/Compression.BSA.Test/packages.config b/Compression.BSA.Test/packages.config
deleted file mode 100644
index 5db2ddea..00000000
--- a/Compression.BSA.Test/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/Compression.BSA/Compression.BSA.csproj b/Compression.BSA/Compression.BSA.csproj
index bf481c6a..a6b44cc6 100644
--- a/Compression.BSA/Compression.BSA.csproj
+++ b/Compression.BSA/Compression.BSA.csproj
@@ -76,37 +76,10 @@
MinimumRecommendedRules.ruleset
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\SharpZipLib.1.2.0\lib\net45\ICSharpCode.SharpZipLib.dll
-
-
- ..\packages\K4os.Compression.LZ4.1.1.11\lib\net46\K4os.Compression.LZ4.dll
-
-
- ..\packages\K4os.Compression.LZ4.Streams.1.1.11\lib\net46\K4os.Compression.LZ4.Streams.dll
-
-
- ..\packages\K4os.Hash.xxHash.1.0.6\lib\net46\K4os.Hash.xxHash.dll
-
-
- ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll
-
-
- ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll
-
-
- ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
@@ -127,7 +100,15 @@
-
+
+ 2.2.6
+
+
+ 1.1.11
+
+
+ 1.2.0
+
\ No newline at end of file
diff --git a/Compression.BSA/packages.config b/Compression.BSA/packages.config
deleted file mode 100644
index 4d29cdb4..00000000
--- a/Compression.BSA/packages.config
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/VirtualFileSystem.Test/App.config b/VirtualFileSystem.Test/App.config
index bd059d69..656cce25 100644
--- a/VirtualFileSystem.Test/App.config
+++ b/VirtualFileSystem.Test/App.config
@@ -8,7 +8,7 @@
-
+
diff --git a/VirtualFileSystem/VirtualFileSystem.csproj b/VirtualFileSystem/VirtualFileSystem.csproj
index 2e6b2c31..b365484b 100644
--- a/VirtualFileSystem/VirtualFileSystem.csproj
+++ b/VirtualFileSystem/VirtualFileSystem.csproj
@@ -70,30 +70,9 @@
MinimumRecommendedRules.ruleset
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\Ceras.4.1.7\lib\net47\Ceras.dll
-
-
- ..\packages\SharpZipLib.1.2.0\lib\net45\ICSharpCode.SharpZipLib.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
-
-
- ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
@@ -118,7 +97,23 @@
-
+
+
+
+ 2.2.6
+
+
+ 4.1.7
+
+
+ 12.0.2
+
+
+ 1.2.0
+
+
+ 1.5.0
+
\ No newline at end of file
diff --git a/VirtualFileSystem/app.config b/VirtualFileSystem/app.config
index f6e7dca8..64d479d3 100644
--- a/VirtualFileSystem/app.config
+++ b/VirtualFileSystem/app.config
@@ -4,7 +4,7 @@
-
+
diff --git a/VirtualFileSystem/packages.config b/VirtualFileSystem/packages.config
deleted file mode 100644
index 1191c694..00000000
--- a/VirtualFileSystem/packages.config
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack.Common/Wabbajack.Common.csproj b/Wabbajack.Common/Wabbajack.Common.csproj
index 081af695..7281a77e 100644
--- a/Wabbajack.Common/Wabbajack.Common.csproj
+++ b/Wabbajack.Common/Wabbajack.Common.csproj
@@ -70,56 +70,11 @@
MinimumRecommendedRules.ruleset
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\Ceras.4.1.7\lib\net47\Ceras.dll
-
-
- ..\packages\erri120.OMODFramework.1.0.0\lib\net472\erri120.OMODFramework.dll
-
-
- ..\packages\SharpZipLib.1.2.0\lib\net45\ICSharpCode.SharpZipLib.dll
-
-
- ..\packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll
-
-
- ..\packages\murmurhash.1.0.3\lib\net45\MurmurHash.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll
-
-
- ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll
-
-
- ..\packages\SevenZip.19.0.0\lib\net20\SevenZip.dll
-
-
- ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
-
-
- ..\packages\System.Data.HashFunction.Core.2.0.0\lib\net45\System.Data.HashFunction.Core.dll
-
-
- ..\packages\System.Data.HashFunction.Interfaces.2.0.0\lib\net45\System.Data.HashFunction.Interfaces.dll
-
-
- ..\packages\System.Data.HashFunction.xxHash.2.0.0\lib\net45\System.Data.HashFunction.xxHash.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
@@ -128,9 +83,6 @@
-
- ..\packages\YamlDotNet.8.0.0\lib\net45\YamlDotNet.dll
-
@@ -149,7 +101,6 @@
-
@@ -161,5 +112,37 @@
+
+
+ 2.2.6
+
+
+ 4.1.7
+
+
+ 1.0.0
+
+
+ 2.5.2
+
+
+ 1.0.3
+
+
+ 12.0.2
+
+
+ 1.0.2
+
+
+ 2.4.0
+
+
+ 2.0.0
+
+
+ 8.0.0
+
+
\ No newline at end of file
diff --git a/Wabbajack.Common/packages.config b/Wabbajack.Common/packages.config
deleted file mode 100644
index 8ab18089..00000000
--- a/Wabbajack.Common/packages.config
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack.Lib/Extensions/TaskExt.cs b/Wabbajack.Lib/Extensions/TaskExt.cs
new file mode 100644
index 00000000..a4053c23
--- /dev/null
+++ b/Wabbajack.Lib/Extensions/TaskExt.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Wabbajack
+{
+ public static class TaskExt
+ {
+ public static async void FireAndForget(this Task task, Action onException = null)
+ {
+ try
+ {
+ await task.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ when (onException != null)
+ {
+ onException(ex);
+ }
+ }
+ }
+}
diff --git a/Wabbajack.Lib/UI/UIUtils.cs b/Wabbajack.Lib/UI/UIUtils.cs
index ee7ad62e..13471acb 100644
--- a/Wabbajack.Lib/UI/UIUtils.cs
+++ b/Wabbajack.Lib/UI/UIUtils.cs
@@ -62,15 +62,36 @@ namespace Wabbajack.Lib
return null;
}
- public static BitmapImage BitmapImageFromResource(string name)
+ public static BitmapImage BitmapImageFromResource(string name) => BitmapImageFromStream(Utils.GetResourceStream(name));
+
+ public static BitmapImage BitmapImageFromStream(Stream stream)
{
var img = new BitmapImage();
img.BeginInit();
- img.StreamSource = Utils.GetResourceStream(name);
+ img.StreamSource = stream;
img.EndInit();
return img;
}
+ public static bool TryGetBitmapImageFromFile(string path, out BitmapImage bitmapImage)
+ {
+ try
+ {
+ if (!File.Exists(path))
+ {
+ bitmapImage = default;
+ return false;
+ }
+ bitmapImage = new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute));
+ return true;
+ }
+ catch (Exception)
+ {
+ bitmapImage = default;
+ return false;
+ }
+ }
+
public static string OpenFileDialog(string filter)
{
OpenFileDialog ofd = new OpenFileDialog();
diff --git a/Wabbajack.Lib/Wabbajack.Lib.csproj b/Wabbajack.Lib/Wabbajack.Lib.csproj
index 9c3461e2..6b0795d4 100644
--- a/Wabbajack.Lib/Wabbajack.Lib.csproj
+++ b/Wabbajack.Lib/Wabbajack.Lib.csproj
@@ -31,74 +31,15 @@
4
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\Ceras.4.1.7\lib\net47\Ceras.dll
-
-
- ..\packages\CommonMark.NET.0.15.1\lib\net45\CommonMark.dll
-
-
- ..\packages\DynamicData.6.13.18\lib\net461\DynamicData.dll
-
-
- ..\packages\MegaApiClient.1.7.1\lib\net46\MegaApiClient.dll
-
-
- ..\packages\Microsoft.Toolkit.Wpf.UI.Controls.WebView.5.1.1\lib\net462\Microsoft.Toolkit.Wpf.UI.Controls.WebView.dll
-
-
- ..\packages\Microsoft-WindowsAPICodePack-Core.1.1.3.3\lib\net452\Microsoft.WindowsAPICodePack.dll
-
-
- ..\packages\Microsoft-WindowsAPICodePack-Shell.1.1.3.3\lib\net452\Microsoft.WindowsAPICodePack.Shell.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\ReactiveUI.10.5.7\lib\net461\ReactiveUI.dll
-
-
- ..\packages\SharpCompress.0.23.0\lib\net45\SharpCompress.dll
-
-
- ..\packages\Splat.9.1.1\lib\net461\Splat.dll
-
-
- ..\packages\Splat.Drawing.9.1.1\lib\net461\Splat.Drawing.dll
-
-
- ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
-
-
- ..\packages\System.Drawing.Primitives.4.3.0\lib\net45\System.Drawing.Primitives.dll
- True
- True
-
-
- ..\packages\System.Reactive.4.2.0\lib\net46\System.Reactive.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
-
- ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
-
-
- ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
-
@@ -109,13 +50,7 @@
-
- ..\packages\WebSocketSharpFork.1.0.4.0\lib\net35\websocket-sharp.dll
-
-
- ..\packages\YamlDotNet.8.0.0\lib\net45\YamlDotNet.dll
-
@@ -160,6 +95,7 @@
+
@@ -182,7 +118,6 @@
-
@@ -204,7 +139,44 @@
-
+
+
+ 2.2.6
+
+
+ 4.1.7
+
+
+ 0.15.1
+
+
+ 1.7.1
+
+
+ 1.1.3.3
+
+
+ 5.1.1
+
+
+ 12.0.2
+
+
+ 10.5.7
+
+
+ 0.23.0
+
+
+ 4.2.0
+
+
+ 1.0.4
+
+
+ 8.0.0
+
+
MSBuild:Compile
diff --git a/Wabbajack.Lib/packages.config b/Wabbajack.Lib/packages.config
deleted file mode 100644
index 8b9d706b..00000000
--- a/Wabbajack.Lib/packages.config
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack.Test/Wabbajack.Test.csproj b/Wabbajack.Test/Wabbajack.Test.csproj
index 4a117a55..6079c766 100644
--- a/Wabbajack.Test/Wabbajack.Test.csproj
+++ b/Wabbajack.Test/Wabbajack.Test.csproj
@@ -1,6 +1,5 @@
-
Debug
@@ -78,55 +77,14 @@
MinimumRecommendedRules.ruleset
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\DynamicData.6.13.18\lib\net461\DynamicData.dll
-
-
- ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
-
-
- ..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\ReactiveUI.10.5.7\lib\net461\ReactiveUI.dll
-
-
- ..\packages\Splat.9.1.1\lib\net461\Splat.dll
-
-
- ..\packages\Splat.Drawing.9.1.1\lib\net461\Splat.Drawing.dll
-
-
- ..\packages\System.Drawing.Primitives.4.3.0\lib\net45\System.Drawing.Primitives.dll
- True
- True
-
-
- ..\packages\System.Reactive.4.2.0\lib\net46\System.Reactive.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
-
- ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
-
-
- ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
-
@@ -149,7 +107,6 @@
-
@@ -169,14 +126,26 @@
Wabbajack
+
+
+ 2.2.6
+
+
+ 1.3.2
+
+
+ 1.3.2
+
+
+ 12.0.2
+
+
+ 10.5.7
+
+
+ 4.2.0
+
+
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack.Test/app.config b/Wabbajack.Test/app.config
index 3e3e46ab..6af99795 100644
--- a/Wabbajack.Test/app.config
+++ b/Wabbajack.Test/app.config
@@ -12,7 +12,7 @@
-
+
diff --git a/Wabbajack.Test/packages.config b/Wabbajack.Test/packages.config
deleted file mode 100644
index ce27ac3f..00000000
--- a/Wabbajack.Test/packages.config
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack/App.xaml b/Wabbajack/App.xaml
index fa8f6a15..310e335e 100644
--- a/Wabbajack/App.xaml
+++ b/Wabbajack/App.xaml
@@ -6,6 +6,9 @@
+
+
+
diff --git a/Wabbajack/App.xaml.cs b/Wabbajack/App.xaml.cs
index 62d33cdc..7717a08e 100644
--- a/Wabbajack/App.xaml.cs
+++ b/Wabbajack/App.xaml.cs
@@ -13,22 +13,14 @@ namespace Wabbajack
{
public App()
{
- /*
- Utils.Log($"Wabbajack Build - {ThisAssembly.Git.Sha}");
- SetupHandlers();
-
- var args = Environment.GetCommandLineArgs();
- if (args.Length > 1)
+ // Wire any unhandled crashing exceptions to log before exiting
+ AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
{
- Utils.SetLoggerFn(f => { });
- WorkQueue.Init((a, b, c) => { }, (a, b) => { });
- var updater = new CheckForUpdates(args[1]);
- if (updater.FindOutdatedMods())
- {
- Environment.Exit(0);
- }
- Environment.Exit(1);
- }*/
+ // Don't do any special logging side effects
+ Utils.SetLoggerFn((s) => { });
+ Utils.Log("Uncaught error:");
+ Utils.Log(((Exception)e.ExceptionObject).ExceptionToString());
+ };
var appPath = Assembly.GetExecutingAssembly().Location;
if (!ExtensionManager.IsAssociated() || ExtensionManager.NeedsUpdating(appPath))
@@ -37,22 +29,11 @@ namespace Wabbajack
}
string[] args = Environment.GetCommandLineArgs();
- StartupUri = new Uri("UI/ModeSelectionWindow.xaml", UriKind.Relative);
+ StartupUri = new Uri("Views/ModeSelectionWindow.xaml", UriKind.Relative);
if (args.Length != 3) return;
if (!args[1].Contains("-i")) return;
// modlists gets loaded using a shell command
- StartupUri = new Uri("UI/MainWindow.xaml", UriKind.Relative);
- }
-
- private void SetupHandlers()
- {
- AppDomain.CurrentDomain.UnhandledException += AppHandler;
- }
-
- private void AppHandler(object sender, UnhandledExceptionEventArgs e)
- {
- Utils.Log("Uncaught error:");
- Utils.Log(((Exception)e.ExceptionObject).ExceptionToString());
+ StartupUri = new Uri("Views/MainWindow.xaml", UriKind.Relative);
}
}
}
\ No newline at end of file
diff --git a/Wabbajack/AppState.cs b/Wabbajack/AppState.cs
deleted file mode 100644
index ff590923..00000000
--- a/Wabbajack/AppState.cs
+++ /dev/null
@@ -1,427 +0,0 @@
-using Syroot.Windows.IO;
-using System;
-using ReactiveUI;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
-using System.IO.Compression;
-using System.Linq;
-using System.Net.Http;
-using System.Reactive.Subjects;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Reflection;
-using System.Threading;
-using System.Windows;
-using System.Windows.Input;
-using System.Windows.Media.Imaging;
-using System.Windows.Threading;
-using Wabbajack.Common;
-using Wabbajack.Lib.Downloaders;
-using Wabbajack.Lib.NexusApi;
-using Wabbajack.UI;
-using DynamicData;
-using DynamicData.Binding;
-using System.Reactive;
-using System.Text;
-using Wabbajack.Lib;
-
-namespace Wabbajack
-{
- public class AppState : ViewModel, IDataErrorInfo
- {
- public SlideShow Slideshow { get; }
-
- public readonly string WabbajackVersion = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).FileVersion;
-
- private string _mo2Folder;
-
- public readonly BitmapImage _noneImage = UIUtils.BitmapImageFromResource("Wabbajack.UI.none.jpg");
-
- private readonly Subject _statusSubject = new Subject();
- public ObservableCollectionExtended Status { get; } = new ObservableCollectionExtended();
-
- private ModList _ModList;
- public ModList ModList { get => _ModList; private set => this.RaiseAndSetIfChanged(ref _ModList, value); }
-
- private string _ModListPath;
- public string ModListPath { get => _ModListPath; private set => this.RaiseAndSetIfChanged(ref _ModListPath, value); }
-
- private RunMode _Mode;
- public RunMode Mode { get => _Mode; private set => this.RaiseAndSetIfChanged(ref _Mode, value); }
-
- private string _ModListName;
- public string ModListName { get => _ModListName; set => this.RaiseAndSetIfChanged(ref _ModListName, value); }
-
- private bool _UIReady;
- public bool UIReady { get => _UIReady; set => this.RaiseAndSetIfChanged(ref _UIReady, value); }
-
- private string _HTMLReport;
- public string HTMLReport { get => _HTMLReport; set => this.RaiseAndSetIfChanged(ref _HTMLReport, value); }
-
- private bool _Installing;
- public bool Installing { get => _Installing; set => this.RaiseAndSetIfChanged(ref _Installing, value); }
-
- // Command properties
- public IReactiveCommand ChangePathCommand { get; }
- public IReactiveCommand ChangeDownloadPathCommand { get; }
- public IReactiveCommand BeginCommand { get; }
- public IReactiveCommand ShowReportCommand { get; }
- public IReactiveCommand OpenReadmeCommand { get; }
- public IReactiveCommand OpenModListPropertiesCommand { get; }
-
- public AppState(RunMode mode)
- {
- if (Path.GetDirectoryName(Assembly.GetEntryAssembly().Location.ToLower()) == KnownFolders.Downloads.Path.ToLower())
- {
- MessageBox.Show(
- "Wabbajack is running inside your Downloads folder. This folder is often highly monitored by antivirus software and these can often " +
- "conflict with the operations Wabbajack needs to perform. Please move this executable outside of your Downloads folder and then restart the app.",
- "Cannot run inside Downloads",
- MessageBoxButton.OK,
- MessageBoxImage.Error);
- Environment.Exit(1);
- }
-
- Mode = mode;
-
- // Define commands
- this.ChangePathCommand = ReactiveCommand.Create(ExecuteChangePath);
- this.ChangeDownloadPathCommand = ReactiveCommand.Create(ExecuteChangeDownloadPath);
- this.ShowReportCommand = ReactiveCommand.Create(ShowReport);
- this.OpenModListPropertiesCommand = ReactiveCommand.Create(
- execute: OpenModListProperties,
- canExecute: this.WhenAny(x => x.UIReady)
- .ObserveOnGuiThread());
- this.OpenReadmeCommand = ReactiveCommand.Create(
- execute: this.OpenReadmeWindow,
- canExecute: this.WhenAny(x => x.ModList)
- .Select(modList => !string.IsNullOrEmpty(modList?.Readme))
- .ObserveOnGuiThread());
- this.BeginCommand = ReactiveCommand.Create(
- execute: this.ExecuteBegin,
- canExecute: this.WhenAny(x => x.UIReady)
- .ObserveOnGuiThread());
-
- this.Slideshow = new SlideShow(this);
-
- // Initialize work queue
- WorkQueue.Init(
- report_function: (id, msg, progress) => this._statusSubject.OnNext(new CPUStatus() { ID = id, Msg = msg, Progress = progress }),
- report_queue_size: (max, current) => this.SetQueueSize(max, current));
- // Compile progress updates and populate ObservableCollection
- this._statusSubject
- .ObserveOn(RxApp.TaskpoolScheduler)
- .ToObservableChangeSet(x => x.ID)
- .Batch(TimeSpan.FromMilliseconds(250))
- .EnsureUniqueChanges()
- .ObserveOn(RxApp.MainThreadScheduler)
- .Sort(SortExpressionComparer.Ascending(s => s.ID), SortOptimisations.ComparesImmutableValuesOnly)
- .Bind(this.Status)
- .Subscribe()
- .DisposeWith(this.CompositeDisposable);
- }
-
- public ObservableCollection Log { get; } = new ObservableCollection();
-
- private string _Location;
- public string Location { get => _Location; set => this.RaiseAndSetIfChanged(ref _Location, value); }
-
- private string _LocationLabel;
- public string LocationLabel { get => _LocationLabel; set => this.RaiseAndSetIfChanged(ref _LocationLabel, value); }
-
- private string _DownloadLocation;
- public string DownloadLocation { get => _DownloadLocation; set => this.RaiseAndSetIfChanged(ref _DownloadLocation, value); }
-
- private int _queueProgress;
- public int QueueProgress { get => _queueProgress; set => this.RaiseAndSetIfChanged(ref _queueProgress, value); }
-
- public string LogFile { get; }
-
- private void ExecuteChangePath()
- {
- switch (this.Mode)
- {
- case RunMode.Compile:
- Location = UIUtils.ShowFolderSelectionDialog("Select Your MO2 profile directory");
- break;
- case RunMode.Install:
- var folder = UIUtils.ShowFolderSelectionDialog("Select Installation directory");
- if (folder == null) return;
- Location = folder;
- if (DownloadLocation == null)
- {
- DownloadLocation = Path.Combine(Location, "downloads");
- }
- break;
- default:
- throw new NotImplementedException();
- }
- }
-
- private void ExecuteChangeDownloadPath()
- {
- var folder = UIUtils.ShowFolderSelectionDialog("Select a location for MO2 downloads");
- if (folder != null) DownloadLocation = folder;
- }
-
- private void ShowReport()
- {
- var file = Path.GetTempFileName() + ".html";
- File.WriteAllText(file, HTMLReport);
- Process.Start(file);
- }
-
- private ModlistPropertiesWindow modlistPropertiesWindow;
- public string newImagePath;
- public string readmePath;
- public bool ChangedProperties;
- private void OpenModListProperties()
- {
- if (UIReady)
- {
- if (modlistPropertiesWindow == null)
- {
- modlistPropertiesWindow = new ModlistPropertiesWindow(this);
- newImagePath = null;
- ChangedProperties = false;
-
- }
- if(!modlistPropertiesWindow.IsClosed)
- modlistPropertiesWindow.Show();
- else
- {
- modlistPropertiesWindow = null;
- OpenModListProperties();
- }
- }
- }
-
- private void OpenReadmeWindow()
- {
- if (string.IsNullOrEmpty(this.ModList.Readme)) return;
- using (var fs = new FileStream(this.ModListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
- using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
- using (var ms = new MemoryStream())
- {
- var entry = ar.GetEntry(this.ModList.Readme);
- using (var e = entry.Open())
- {
- e.CopyTo(ms);
- }
- ms.Seek(0, SeekOrigin.Begin);
- using (var reader = new StreamReader(ms))
- {
- var viewer = new TextViewer(reader.ReadToEnd(), this.ModListName);
- viewer.Show();
- }
- }
- }
-
- public string Error => "Error";
-
- public string this[string columnName] => Validate(columnName);
-
- private string Validate(string columnName)
- {
- string validationMessage = null;
- switch (columnName)
- {
- case "Location":
- if (Location == null)
- {
- validationMessage = null;
- }
- else switch (Mode)
- {
- case RunMode.Compile when Location != null && Directory.Exists(Location) && File.Exists(Path.Combine(Location, "modlist.txt")):
- Location = Path.Combine(Location, "modlist.txt");
- validationMessage = null;
- ConfigureForBuild();
- break;
- case RunMode.Install when Location != null && Directory.Exists(Location) && !Directory.EnumerateFileSystemEntries(Location).Any():
- validationMessage = null;
- break;
- case RunMode.Install when Location != null && Directory.Exists(Location) && Directory.EnumerateFileSystemEntries(Location).Any():
- validationMessage = "You have selected a non-empty directory. Installing the modlist here might result in a broken install!";
- break;
- default:
- validationMessage = "Invalid Mod Organizer profile directory";
- break;
- }
- break;
- }
- return validationMessage;
- }
-
- public void LogMsg(string msg)
- {
- Application.Current.Dispatcher.Invoke(() => Log.Add(msg));
- }
-
- public void SetQueueSize(int max, int current)
- {
- if (max == 0)
- max = 1;
- var total = current * 100 / max;
- QueueProgress = total;
- }
-
- private void ConfigureForBuild()
- {
- var profile_folder = Path.GetDirectoryName(Location);
- var mo2folder = Path.GetDirectoryName(Path.GetDirectoryName(profile_folder));
- if (!File.Exists(Path.Combine(mo2folder, "ModOrganizer.exe")))
- LogMsg($"Error! No ModOrganizer2.exe found in {mo2folder}");
-
- var profile_name = Path.GetFileName(profile_folder);
- this.ModListName = profile_name;
- this.Mode = RunMode.Compile;
-
- if (Utils.IsMO2Running(mo2folder))
- {
- MessageBox.Show("You need to close MO2 before running Wabbajack!",
- "Error", MessageBoxButton.OK);
- Environment.Exit(1);
- }
-
- var tmp_compiler = new Compiler(mo2folder);
- DownloadLocation = tmp_compiler.MO2DownloadsFolder;
-
- _mo2Folder = mo2folder;
- }
-
- internal void ConfigureForInstall(string source, ModList modlist)
- {
- this.ModList = modlist;
- this.ModListPath = source;
- this.Mode = RunMode.Install;
- ModListName = this.ModList.Name;
- HTMLReport = this.ModList.ReportHTML;
- Location = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
-
- var currentWJVersion = new Version(WabbajackVersion);
- var modlistWJVersion = new Version(modlist.WabbajackVersion);
-
- if (currentWJVersion > modlistWJVersion)
- {
- MessageBox.Show(
- "The selected Modlist was build with an earlier version of Wabbajack. " +
- $"Current Version: {WabbajackVersion}, " +
- $"Version used to build the Modlist: {modlist.WabbajackVersion}",
- "Information",
- MessageBoxButton.OK);
- }
- else if(currentWJVersion < modlistWJVersion)
- {
- MessageBox.Show(
- "The selected Modlist was build with a newer version of Wabbajack. " +
- $"Current Version: {WabbajackVersion}, " +
- $"Version used to build the Modlist: {modlist.WabbajackVersion}",
- "Information",
- MessageBoxButton.OK);
- }
-
- this.Slideshow.SlideShowElements = modlist.Archives
- .Select(m => m.State)
- .OfType()
- .Select(m =>
- new Slide(NexusApiUtils.FixupSummary(m.ModName),m.ModID,
- NexusApiUtils.FixupSummary(m.Summary), NexusApiUtils.FixupSummary(m.Author),
- m.Adult,m.NexusURL,m.SlideShowPic)).ToList();
-
-
- this.Slideshow.PreloadSlideShow();
- }
-
- private void ExecuteBegin()
- {
- UIReady = false;
- if (this.Mode == RunMode.Install)
- {
- this.Installing = true;
- var installer = new Installer(this.ModListPath, this.ModList, Location)
- {
- DownloadFolder = DownloadLocation
- };
- var th = new Thread(() =>
- {
- UIReady = false;
- try
- {
- installer.Install();
- }
- catch (Exception ex)
- {
- while (ex.InnerException != null) ex = ex.InnerException;
- LogMsg(ex.StackTrace);
- LogMsg(ex.ToString());
- LogMsg($"{ex.Message} - Can't continue");
- }
- finally
- {
- UIReady = true;
- this.Installing = false;
- }
- })
- {
- Priority = ThreadPriority.BelowNormal
- };
- th.Start();
- }
- else if (_mo2Folder != null)
- {
- var compiler = new Compiler(_mo2Folder)
- {
- MO2Profile = ModListName,
- ModListName = ChangedProperties ? this.Slideshow.ModName : null,
- ModListAuthor = ChangedProperties ? this.Slideshow.AuthorName : null,
- ModListDescription = ChangedProperties ? this.Slideshow.Summary : null,
- ModListImage = ChangedProperties ? newImagePath : null,
- ModListWebsite = ChangedProperties ? this.Slideshow.NexusSiteURL : null,
- ModListReadme = ChangedProperties ? readmePath : null,
- WabbajackVersion = WabbajackVersion
- };
- var th = new Thread(() =>
- {
- UIReady = false;
- try
- {
- compiler.Compile();
- if (compiler.ModList != null && compiler.ModList.ReportHTML != null)
- HTMLReport = compiler.ModList.ReportHTML;
- }
- catch (Exception ex)
- {
- while (ex.InnerException != null) ex = ex.InnerException;
- LogMsg(ex.StackTrace);
- LogMsg(ex.ToString());
- LogMsg($"{ex.Message} - Can't continue");
- }
- finally
- {
- UIReady = true;
- }
- })
- {
- Priority = ThreadPriority.BelowNormal
- };
- th.Start();
- }
- else
- {
- Utils.Log("Cannot compile modlist: no valid Mod Organizer profile directory selected.");
- UIReady = true;
- }
- }
- }
-
- public class CPUStatus
- {
- public int Progress { get; internal set; }
- public string Msg { get; internal set; }
- public int ID { get; internal set; }
- }
-}
\ No newline at end of file
diff --git a/Wabbajack/Converters/BoolToVisibilityConverter.cs b/Wabbajack/Converters/BoolToVisibilityConverter.cs
new file mode 100644
index 00000000..ad00a890
--- /dev/null
+++ b/Wabbajack/Converters/BoolToVisibilityConverter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+
+namespace Wabbajack
+{
+ [ValueConversion(typeof(Visibility), typeof(bool))]
+ public class BoolToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (targetType != typeof(Visibility))
+ throw new InvalidOperationException($"The target must be of type {nameof(Visibility)}");
+ bool compareTo = true;
+ if (parameter is bool p)
+ {
+ compareTo = p;
+ }
+ else if (parameter is string str && str.ToUpper().Equals("FALSE"))
+ {
+ compareTo = false;
+ }
+ return ((bool)value) == compareTo ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Wabbajack/Themes/LeftMarginMultiplierConverter.cs b/Wabbajack/Converters/LeftMarginMultiplierConverter.cs
similarity index 100%
rename from Wabbajack/Themes/LeftMarginMultiplierConverter.cs
rename to Wabbajack/Converters/LeftMarginMultiplierConverter.cs
diff --git a/Wabbajack/Extensions/ReactiveUIExt.cs b/Wabbajack/Extensions/ReactiveUIExt.cs
index 9c0fe1df..372794a8 100644
--- a/Wabbajack/Extensions/ReactiveUIExt.cs
+++ b/Wabbajack/Extensions/ReactiveUIExt.cs
@@ -5,6 +5,7 @@ using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
+using System.Threading.Tasks;
using DynamicData;
using DynamicData.Kernel;
using ReactiveUI;
@@ -151,6 +152,57 @@ namespace Wabbajack
});
}
+ public static IObservable SelectTask(this IObservable source, Func task)
+ {
+ return source
+ .SelectMany(async i =>
+ {
+ await task(i).ConfigureAwait(false);
+ return System.Reactive.Unit.Default;
+ });
+ }
+
+ public static IObservable SelectTask(this IObservable source, Func task)
+ {
+ return source
+ .SelectMany(async _ =>
+ {
+ await task().ConfigureAwait(false);
+ return System.Reactive.Unit.Default;
+ });
+ }
+
+ public static IObservable SelectTask(this IObservable source, Func> task)
+ {
+ return source
+ .SelectMany(_ => task());
+ }
+
+ public static IObservable SelectTask(this IObservable source, Func> task)
+ {
+ return source
+ .SelectMany(x => task(x));
+ }
+
+ public static IObservable DoTask(this IObservable source, Func task)
+ {
+ return source
+ .SelectMany(async (x) =>
+ {
+ await task(x).ConfigureAwait(false);
+ return x;
+ });
+ }
+
+ public static IObservable WhereCastable(this IObservable source)
+ where R : class
+ where T : class
+ {
+ return source
+ .Select(x => x as R)
+ .NotNull();
+ }
+
/// These snippets were provided by RolandPheasant (author of DynamicData)
/// They'll be going into the official library at some point, but are here for now.
#region Dynamic Data EnsureUniqueChanges
diff --git a/Wabbajack/FodyWeavers.xml b/Wabbajack/FodyWeavers.xml
index 36bc693f..26ec15b8 100644
--- a/Wabbajack/FodyWeavers.xml
+++ b/Wabbajack/FodyWeavers.xml
@@ -1,4 +1,5 @@
+
7z
diff --git a/Wabbajack/FodyWeavers.xsd b/Wabbajack/FodyWeavers.xsd
index 44a53744..b2d3e296 100644
--- a/Wabbajack/FodyWeavers.xsd
+++ b/Wabbajack/FodyWeavers.xsd
@@ -4,6 +4,7 @@
+
diff --git a/Wabbajack/UI/Icons/discord.png b/Wabbajack/Resources/Icons/discord.png
similarity index 100%
rename from Wabbajack/UI/Icons/discord.png
rename to Wabbajack/Resources/Icons/discord.png
diff --git a/Wabbajack/UI/Icons/github.png b/Wabbajack/Resources/Icons/github.png
similarity index 100%
rename from Wabbajack/UI/Icons/github.png
rename to Wabbajack/Resources/Icons/github.png
diff --git a/Wabbajack/UI/Icons/github_light.png b/Wabbajack/Resources/Icons/github_light.png
similarity index 100%
rename from Wabbajack/UI/Icons/github_light.png
rename to Wabbajack/Resources/Icons/github_light.png
diff --git a/Wabbajack/UI/Icons/next.png b/Wabbajack/Resources/Icons/next.png
similarity index 100%
rename from Wabbajack/UI/Icons/next.png
rename to Wabbajack/Resources/Icons/next.png
diff --git a/Wabbajack/UI/Icons/patreon.png b/Wabbajack/Resources/Icons/patreon.png
similarity index 100%
rename from Wabbajack/UI/Icons/patreon.png
rename to Wabbajack/Resources/Icons/patreon.png
diff --git a/Wabbajack/UI/Icons/patreon_light.png b/Wabbajack/Resources/Icons/patreon_light.png
similarity index 100%
rename from Wabbajack/UI/Icons/patreon_light.png
rename to Wabbajack/Resources/Icons/patreon_light.png
diff --git a/Wabbajack/UI/Icons/wabbajack.ico b/Wabbajack/Resources/Icons/wabbajack.ico
similarity index 100%
rename from Wabbajack/UI/Icons/wabbajack.ico
rename to Wabbajack/Resources/Icons/wabbajack.ico
diff --git a/Wabbajack/Resources/Wabba_Mouth.png b/Wabbajack/Resources/Wabba_Mouth.png
new file mode 100644
index 00000000..f2d2615b
Binary files /dev/null and b/Wabbajack/Resources/Wabba_Mouth.png differ
diff --git a/Wabbajack/UI/banner.png b/Wabbajack/Resources/banner.png
similarity index 100%
rename from Wabbajack/UI/banner.png
rename to Wabbajack/Resources/banner.png
diff --git a/Wabbajack/UI/banner_dark.png b/Wabbajack/Resources/banner_dark.png
similarity index 100%
rename from Wabbajack/UI/banner_dark.png
rename to Wabbajack/Resources/banner_dark.png
diff --git a/Wabbajack/UI/banner_small.png b/Wabbajack/Resources/banner_small.png
similarity index 100%
rename from Wabbajack/UI/banner_small.png
rename to Wabbajack/Resources/banner_small.png
diff --git a/Wabbajack/UI/banner_small_dark.png b/Wabbajack/Resources/banner_small_dark.png
similarity index 100%
rename from Wabbajack/UI/banner_small_dark.png
rename to Wabbajack/Resources/banner_small_dark.png
diff --git a/Wabbajack/UI/none.jpg b/Wabbajack/Resources/none.jpg
similarity index 100%
rename from Wabbajack/UI/none.jpg
rename to Wabbajack/Resources/none.jpg
diff --git a/Wabbajack/Themes/Styles.xaml b/Wabbajack/Themes/Styles.xaml
index 317947ac..14e242b2 100644
--- a/Wabbajack/Themes/Styles.xaml
+++ b/Wabbajack/Themes/Styles.xaml
@@ -7,7 +7,7 @@
mc:Ignorable="d">
-
+
@@ -22,12 +22,43 @@
#BDBDBD
#525252
-
-
+
+ #ffc400
+ #e83a40
+ #52b545
+
+ #BB86FC
+ #3700B3
+ #03DAC6
+ #C7FC86
+ #868CFC
+ #F686FC
+ #FC86C7
+ #FCBB86
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -807,7 +838,7 @@
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Enable the Slideshow
-
-
- Show NSFW Mods in the Slideshow
-
-
-
-
-
-
-
diff --git a/Wabbajack/UI/TextViewer.xaml b/Wabbajack/UI/TextViewer.xaml
deleted file mode 100644
index 2c56bb7c..00000000
--- a/Wabbajack/UI/TextViewer.xaml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
diff --git a/Wabbajack/AutoScrollBehavior.cs b/Wabbajack/Util/AutoScrollBehavior.cs
similarity index 100%
rename from Wabbajack/AutoScrollBehavior.cs
rename to Wabbajack/Util/AutoScrollBehavior.cs
diff --git a/Wabbajack/Util/CPUStatus.cs b/Wabbajack/Util/CPUStatus.cs
new file mode 100644
index 00000000..0a218d7a
--- /dev/null
+++ b/Wabbajack/Util/CPUStatus.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Wabbajack
+{
+ public class CPUStatus
+ {
+ public int Progress { get; internal set; }
+ public string Msg { get; internal set; }
+ public int ID { get; internal set; }
+ }
+}
diff --git a/Wabbajack/Themes/TreeViewItemExtensions.cs b/Wabbajack/Util/TreeViewItemExtensions.cs
similarity index 100%
rename from Wabbajack/Themes/TreeViewItemExtensions.cs
rename to Wabbajack/Util/TreeViewItemExtensions.cs
diff --git a/Wabbajack/View Models/CompilerVM.cs b/Wabbajack/View Models/CompilerVM.cs
new file mode 100644
index 00000000..73e79878
--- /dev/null
+++ b/Wabbajack/View Models/CompilerVM.cs
@@ -0,0 +1,149 @@
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using Splat;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+using Wabbajack.Common;
+using Wabbajack.Lib;
+
+namespace Wabbajack
+{
+ public class CompilerVM : ViewModel
+ {
+ public MainWindowVM MWVM { get; }
+
+ [Reactive]
+ public string Mo2Folder { get; set; }
+
+ [Reactive]
+ public string MOProfile { get; set; }
+
+ [Reactive]
+ public string ModListName { get; set; }
+
+ [Reactive]
+ public string Location { get; set; }
+
+ [Reactive]
+ public bool UIReady { get; set; } = true;
+
+ [Reactive]
+ public string AuthorName { get; set; }
+
+ [Reactive]
+ public string Summary { get; set; } = "Description (700 characters max)";
+
+ [Reactive]
+ public string ImagePath { get; set; }
+
+ private readonly ObservableAsPropertyHelper _Image;
+ public BitmapImage Image => _Image.Value;
+
+ [Reactive]
+ public string NexusSiteURL { get; set; }
+
+ [Reactive]
+ public string ReadMeText { get; set; }
+
+ [Reactive]
+ public string HTMLReport { get; set; }
+
+ [Reactive]
+ public string DownloadLocation { get; set; }
+
+ public IReactiveCommand BeginCommand { get; }
+
+ public CompilerVM(MainWindowVM mainWindowVM, string source)
+ {
+ this.MWVM = mainWindowVM;
+ this.Location = source;
+
+ this.BeginCommand = ReactiveCommand.CreateFromTask(
+ execute: this.ExecuteBegin,
+ canExecute: this.WhenAny(x => x.UIReady)
+ .ObserveOnGuiThread());
+
+ this._Image = this.WhenAny(x => x.ImagePath)
+ .Select(path =>
+ {
+ if (string.IsNullOrWhiteSpace(path)) return UIUtils.BitmapImageFromResource("Wabbajack.Resources.Banner_Dark.png");
+ if (UIUtils.TryGetBitmapImageFromFile(path, out var image))
+ {
+ return image;
+ }
+ return UIUtils.BitmapImageFromResource("Wabbajack.Resources.none.png");
+ })
+ .ToProperty(this, nameof(this.Image));
+
+ ConfigureForBuild(source);
+ }
+
+ private void ConfigureForBuild(string location)
+ {
+ var profile_folder = Path.GetDirectoryName(location);
+ this.Mo2Folder = Path.GetDirectoryName(Path.GetDirectoryName(profile_folder));
+ if (!File.Exists(Path.Combine(this.Mo2Folder, "ModOrganizer.exe")))
+ {
+ this.Log().Error($"Error! No ModOrganizer2.exe found in {this.Mo2Folder}");
+ }
+
+ this.MOProfile = Path.GetFileName(profile_folder);
+ this.ModListName = this.MOProfile;
+
+ var tmp_compiler = new Compiler(this.Mo2Folder);
+ this.DownloadLocation = tmp_compiler.MO2DownloadsFolder;
+ }
+
+ private async Task ExecuteBegin()
+ {
+ if (this.Mo2Folder != null)
+ {
+ var compiler = new Compiler(this.Mo2Folder)
+ {
+ MO2Profile = this.MOProfile,
+ ModListName = this.ModListName,
+ ModListAuthor = this.AuthorName,
+ ModListDescription = this.Summary,
+ ModListImage = this.ImagePath,
+ ModListWebsite = this.NexusSiteURL,
+ ModListReadme = this.ReadMeText,
+ };
+ await Task.Run(() =>
+ {
+ UIReady = false;
+ try
+ {
+ compiler.Compile();
+ if (compiler.ModList?.ReportHTML != null)
+ {
+ this.HTMLReport = compiler.ModList.ReportHTML;
+ }
+ }
+ catch (Exception ex)
+ {
+ while (ex.InnerException != null) ex = ex.InnerException;
+ this.Log().Warn(ex, "Can't continue");
+ }
+ finally
+ {
+ UIReady = true;
+ }
+ });
+ }
+ else
+ {
+ this.Log().Warn("Cannot compile modlist: no valid Mod Organizer profile directory selected.");
+ UIReady = true;
+ }
+ }
+ }
+}
diff --git a/Wabbajack/View Models/InstallerVM.cs b/Wabbajack/View Models/InstallerVM.cs
new file mode 100644
index 00000000..7da46ad6
--- /dev/null
+++ b/Wabbajack/View Models/InstallerVM.cs
@@ -0,0 +1,321 @@
+using Syroot.Windows.IO;
+using System;
+using ReactiveUI;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net.Http;
+using System.Reactive.Subjects;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reflection;
+using System.Threading;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Media.Imaging;
+using System.Windows.Threading;
+using Wabbajack.Common;
+using Wabbajack.Lib.Downloaders;
+using Wabbajack.Lib.NexusApi;
+using DynamicData;
+using DynamicData.Binding;
+using System.Reactive;
+using System.Text;
+using Wabbajack.Lib;
+using Splat;
+using ReactiveUI.Fody.Helpers;
+
+namespace Wabbajack
+{
+ public class InstallerVM : ViewModel
+ {
+ public SlideShow Slideshow { get; }
+
+ public MainWindowVM MWVM { get; }
+
+ public BitmapImage WabbajackLogo { get; } = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Wabba_Mouth.png");
+
+ private readonly ObservableAsPropertyHelper _ModList;
+ public ModList ModList => _ModList.Value;
+
+ [Reactive]
+ public string ModListPath { get; set; }
+
+ [Reactive]
+ public bool UIReady { get; set; }
+
+ private readonly ObservableAsPropertyHelper _HTMLReport;
+ public string HTMLReport => _HTMLReport.Value;
+
+ ///
+ /// Tracks whether an install is currently in progress
+ ///
+ [Reactive]
+ public bool Installing { get; set; }
+
+ ///
+ /// Tracks whether to show the installing pane
+ ///
+ [Reactive]
+ public bool InstallingMode { get; set; }
+
+ [Reactive]
+ public string Location { get; set; } = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+
+ [Reactive]
+ public string DownloadLocation { get; set; }
+
+ private readonly ObservableAsPropertyHelper _ProgressPercent;
+ public float ProgressPercent => _ProgressPercent.Value;
+
+ private readonly ObservableAsPropertyHelper _Image;
+ public BitmapImage Image => _Image.Value;
+
+ private readonly ObservableAsPropertyHelper _TitleText;
+ public string TitleText => _TitleText.Value;
+
+ private readonly ObservableAsPropertyHelper _AuthorText;
+ public string AuthorText => _AuthorText.Value;
+
+ private readonly ObservableAsPropertyHelper _Description;
+ public string Description => _Description.Value;
+
+ // Command properties
+ public IReactiveCommand BeginCommand { get; }
+ public IReactiveCommand ShowReportCommand { get; }
+ public IReactiveCommand OpenReadmeCommand { get; }
+ public IReactiveCommand VisitWebsiteCommand { get; }
+
+ public InstallerVM(MainWindowVM mainWindowVM)
+ {
+ if (Path.GetDirectoryName(Assembly.GetEntryAssembly().Location.ToLower()) == KnownFolders.Downloads.Path.ToLower())
+ {
+ MessageBox.Show(
+ "Wabbajack is running inside your Downloads folder. This folder is often highly monitored by antivirus software and these can often " +
+ "conflict with the operations Wabbajack needs to perform. Please move this executable outside of your Downloads folder and then restart the app.",
+ "Cannot run inside Downloads",
+ MessageBoxButton.OK,
+ MessageBoxImage.Error);
+ Environment.Exit(1);
+ }
+
+ this.MWVM = mainWindowVM;
+
+ this._ModList = this.WhenAny(x => x.ModListPath)
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .Select(source =>
+ {
+ if (source == null) return default;
+ var modlist = Installer.LoadFromFile(source);
+ if (modlist == null)
+ {
+ MessageBox.Show("Invalid Modlist, or file not found.", "Invalid Modlist", MessageBoxButton.OK,
+ MessageBoxImage.Error);
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.MWVM.MainWindow.ExitWhenClosing = false;
+ var window = new ModeSelectionWindow
+ {
+ ShowActivated = true
+ };
+ window.Show();
+ this.MWVM.MainWindow.Close();
+ });
+ return default;
+ }
+ return modlist;
+ })
+ .ObserveOnGuiThread()
+ .StartWith(default(ModList))
+ .ToProperty(this, nameof(this.ModList));
+ this._HTMLReport = this.WhenAny(x => x.ModList)
+ .Select(modList => modList?.ReportHTML)
+ .ToProperty(this, nameof(this.HTMLReport));
+ this._ProgressPercent = Observable.CombineLatest(
+ this.WhenAny(x => x.Installing),
+ this.WhenAny(x => x.InstallingMode),
+ resultSelector: (installing, mode) => !installing && mode)
+ .Select(show => show ? 1f : 0f)
+ // Disable for now, until more reliable
+ //this.WhenAny(x => x.MWVM.QueueProgress)
+ // .Select(i => i / 100f)
+ .ToProperty(this, nameof(this.ProgressPercent));
+
+ this.Slideshow = new SlideShow(this);
+
+ // Locate and create modlist image if it exists
+ var modListImage = Observable.CombineLatest(
+ this.WhenAny(x => x.ModList),
+ this.WhenAny(x => x.ModListPath),
+ (modList, modListPath) => (modList, modListPath))
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .Select(u =>
+ {
+ if (u.modList == null
+ || u.modListPath == null
+ || !File.Exists(u.modListPath)
+ || string.IsNullOrEmpty(u.modList.Image)
+ || u.modList.Image.Length != 36)
+ {
+ return WabbajackLogo;
+ }
+ try
+ {
+ using (var fs = new FileStream(u.modListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
+ using (var ms = new MemoryStream())
+ {
+ var entry = ar.GetEntry(u.modList.Image);
+ using (var e = entry.Open())
+ e.CopyTo(ms);
+ var image = new BitmapImage();
+ image.BeginInit();
+ image.CacheOption = BitmapCacheOption.OnLoad;
+ image.StreamSource = ms;
+ image.EndInit();
+ image.Freeze();
+
+ return image;
+ }
+ }
+ catch (Exception ex)
+ {
+ this.Log().Warn(ex, "Error loading modlist splash image.");
+ return WabbajackLogo;
+ }
+ })
+ .ObserveOnGuiThread()
+ .StartWith(default(BitmapImage))
+ .Replay(1)
+ .RefCount();
+
+ // Set display items to modlist if configuring or complete,
+ // or to the current slideshow data if installing
+ this._Image = Observable.CombineLatest(
+ modListImage
+ .StartWith(default(BitmapImage)),
+ this.WhenAny(x => x.Slideshow.Image)
+ .StartWith(default(BitmapImage)),
+ this.WhenAny(x => x.Installing),
+ resultSelector: (modList, slideshow, installing) => installing ? slideshow : modList)
+ .ToProperty(this, nameof(this.Image));
+ this._TitleText = Observable.CombineLatest(
+ this.WhenAny(x => x.ModList.Name),
+ this.WhenAny(x => x.Slideshow.ModName),
+ this.WhenAny(x => x.Installing),
+ resultSelector: (modList, mod, installing) => installing ? mod : modList)
+ .ToProperty(this, nameof(this.TitleText));
+ this._AuthorText = Observable.CombineLatest(
+ this.WhenAny(x => x.ModList.Author),
+ this.WhenAny(x => x.Slideshow.AuthorName),
+ this.WhenAny(x => x.Installing),
+ resultSelector: (modList, mod, installing) => installing ? mod : modList)
+ .ToProperty(this, nameof(this.AuthorText));
+ this._Description = Observable.CombineLatest(
+ this.WhenAny(x => x.ModList.Description),
+ this.WhenAny(x => x.Slideshow.Description),
+ this.WhenAny(x => x.Installing),
+ resultSelector: (modList, mod, installing) => installing ? mod : modList)
+ .ToProperty(this, nameof(this.Description));
+
+ // Define commands
+ this.ShowReportCommand = ReactiveCommand.Create(ShowReport);
+ this.OpenReadmeCommand = ReactiveCommand.Create(
+ execute: this.OpenReadmeWindow,
+ canExecute: this.WhenAny(x => x.ModList)
+ .Select(modList => !string.IsNullOrEmpty(modList?.Readme))
+ .ObserveOnGuiThread());
+ this.BeginCommand = ReactiveCommand.Create(
+ execute: this.ExecuteBegin,
+ canExecute: this.WhenAny(x => x.Installing)
+ .Select(installing => !installing)
+ .ObserveOnGuiThread());
+ this.VisitWebsiteCommand = ReactiveCommand.Create(
+ execute: () => Process.Start(this.ModList.Website),
+ canExecute: this.WhenAny(x => x.ModList.Website)
+ .Select(x => x?.StartsWith("https://") ?? false)
+ .ObserveOnGuiThread());
+
+ // Have Installation location updates modify the downloads location if empty
+ this.WhenAny(x => x.Location)
+ .Subscribe(installPath =>
+ {
+ if (string.IsNullOrWhiteSpace(this.DownloadLocation))
+ {
+ this.DownloadLocation = Path.Combine(installPath, "downloads");
+ }
+ })
+ .DisposeWith(this.CompositeDisposable);
+ }
+
+ private void ShowReport()
+ {
+ var file = Path.GetTempFileName() + ".html";
+ File.WriteAllText(file, HTMLReport);
+ Process.Start(file);
+ }
+
+ private void OpenReadmeWindow()
+ {
+ if (string.IsNullOrEmpty(this.ModList.Readme)) return;
+ using (var fs = new FileStream(this.ModListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
+ using (var ms = new MemoryStream())
+ {
+ var entry = ar.GetEntry(this.ModList.Readme);
+ if (entry == null)
+ {
+ Utils.Log($"Tried to open a non-existant readme: {this.ModList.Readme}");
+ return;
+ }
+ using (var e = entry.Open())
+ {
+ e.CopyTo(ms);
+ }
+ ms.Seek(0, SeekOrigin.Begin);
+ using (var reader = new StreamReader(ms))
+ {
+ var viewer = new TextViewer(reader.ReadToEnd(), this.ModList.Name);
+ viewer.Show();
+ }
+ }
+ }
+
+ private void ExecuteBegin()
+ {
+ this.Installing = true;
+ this.InstallingMode = true;
+ var installer = new Installer(this.ModListPath, this.ModList, Location)
+ {
+ DownloadFolder = DownloadLocation
+ };
+ var th = new Thread(() =>
+ {
+ try
+ {
+ installer.Install();
+ }
+ catch (Exception ex)
+ {
+ while (ex.InnerException != null) ex = ex.InnerException;
+ Utils.Log(ex.StackTrace);
+ Utils.Log(ex.ToString());
+ Utils.Log($"{ex.Message} - Can't continue");
+ }
+ finally
+ {
+
+ this.Installing = false;
+ }
+ })
+ {
+ Priority = ThreadPriority.BelowNormal
+ };
+ th.Start();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wabbajack/View Models/MainWindowVM.cs b/Wabbajack/View Models/MainWindowVM.cs
new file mode 100644
index 00000000..64993fd1
--- /dev/null
+++ b/Wabbajack/View Models/MainWindowVM.cs
@@ -0,0 +1,123 @@
+using DynamicData;
+using DynamicData.Binding;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Reactive.Subjects;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using Wabbajack.Common;
+using Wabbajack.Lib;
+
+namespace Wabbajack
+{
+ ///
+ /// Main View Model for the application.
+ /// Keeps track of which sub view is being shown in the window, and has some singleton wiring like WorkQueue and Logging.
+ ///
+ public class MainWindowVM : ViewModel
+ {
+ public MainWindow MainWindow { get; }
+
+ private readonly ObservableAsPropertyHelper _ActivePane;
+ public ViewModel ActivePane => _ActivePane.Value;
+
+ [Reactive]
+ public int QueueProgress { get; set; }
+
+ private readonly Subject _statusSubject = new Subject();
+ public IObservable StatusObservable => _statusSubject;
+ public ObservableCollectionExtended StatusList { get; } = new ObservableCollectionExtended();
+
+ private Subject _logSubj = new Subject();
+ public ObservableCollectionExtended Log { get; } = new ObservableCollectionExtended();
+
+ [Reactive]
+ public RunMode Mode { get; set; }
+
+ private readonly Lazy _Compiler;
+ private readonly Lazy _Installer;
+
+ public MainWindowVM(RunMode mode, string source, MainWindow mainWindow)
+ {
+ this.Mode = mode;
+ this.MainWindow = mainWindow;
+ this._Installer = new Lazy(() => new InstallerVM(this));
+ this._Compiler = new Lazy(() => new CompilerVM(this, source));
+
+ // Set up logging
+ _logSubj
+ .ToObservableChangeSet()
+ .Buffer(TimeSpan.FromMilliseconds(250))
+ .Where(l => l.Count > 0)
+ .FlattenBufferResult()
+ .Top(5000)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Bind(this.Log)
+ .Subscribe()
+ .DisposeWith(this.CompositeDisposable);
+ Utils.SetLoggerFn(s => _logSubj.OnNext(s));
+ Utils.SetStatusFn((msg, progress) => WorkQueue.Report(msg, progress));
+
+ // Wire mode to drive the active pane.
+ // Note: This is currently made into a derivative property driven by mode,
+ // but it can be easily changed into a normal property that can be set from anywhere if needed
+ this._ActivePane = this.WhenAny(x => x.Mode)
+ .Select(m =>
+ {
+ switch (m)
+ {
+ case RunMode.Compile:
+ return this._Compiler.Value;
+ case RunMode.Install:
+ return this._Installer.Value;
+ default:
+ return default;
+ }
+ })
+ .ToProperty(this, nameof(this.ActivePane));
+ this.WhenAny(x => x.ActivePane)
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .WhereCastable()
+ .Subscribe(vm => vm.ModListPath = source)
+ .DisposeWith(this.CompositeDisposable);
+
+ // Initialize work queue
+ WorkQueue.Init(
+ report_function: (id, msg, progress) => this._statusSubject.OnNext(new CPUStatus() { ID = id, Msg = msg, Progress = progress }),
+ report_queue_size: (max, current) => this.SetQueueSize(max, current));
+
+ // Compile progress updates and populate ObservableCollection
+ this._statusSubject
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .ToObservableChangeSet(x => x.ID)
+ .Batch(TimeSpan.FromMilliseconds(250))
+ .EnsureUniqueChanges()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Sort(SortExpressionComparer.Ascending(s => s.ID), SortOptimisations.ComparesImmutableValuesOnly)
+ .Bind(this.StatusList)
+ .Subscribe()
+ .DisposeWith(this.CompositeDisposable);
+ }
+
+ private void SetQueueSize(int max, int current)
+ {
+ if (max == 0)
+ max = 1;
+ QueueProgress = current * 100 / max;
+ }
+
+ public override void Dispose()
+ {
+ base.Dispose();
+ Utils.SetLoggerFn(s => { });
+ }
+ }
+}
diff --git a/Wabbajack/UI/ModListDefinition.cs b/Wabbajack/View Models/ModListDefinition.cs
similarity index 100%
rename from Wabbajack/UI/ModListDefinition.cs
rename to Wabbajack/View Models/ModListDefinition.cs
diff --git a/Wabbajack/UI/ModeSelectionWindowViewModel.cs b/Wabbajack/View Models/ModeSelectionWindowVM.cs
similarity index 51%
rename from Wabbajack/UI/ModeSelectionWindowViewModel.cs
rename to Wabbajack/View Models/ModeSelectionWindowVM.cs
index 6b86a7bc..c0f3eed4 100644
--- a/Wabbajack/UI/ModeSelectionWindowViewModel.cs
+++ b/Wabbajack/View Models/ModeSelectionWindowVM.cs
@@ -1,8 +1,11 @@
using Alphaleonis.Win32.Filesystem;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Wabbajack.Common;
@@ -11,40 +14,21 @@ using Wabbajack.Lib.ModListRegistry;
namespace Wabbajack.UI
{
- public class ModeSelectionWindowViewModel : ViewModel
+ public class ModeSelectionWindowVM : ViewModel
{
+ public ObservableCollection ModLists { get; } = new ObservableCollection(ModlistMetadata.LoadFromGithub());
+ [Reactive]
+ public ModlistMetadata SelectedModList { get; set; }
- public ModeSelectionWindowViewModel()
+ private readonly ObservableAsPropertyHelper _CanInstall;
+ public bool CanInstall => _CanInstall.Value;
+
+ public ModeSelectionWindowVM()
{
- _modLists = new ObservableCollection(ModlistMetadata.LoadFromGithub());
- }
-
- private ObservableCollection _modLists;
-
- public ObservableCollection ModLists
- {
- get => _modLists;
- }
-
-
- private ModlistMetadata _selectedModList;
- public ModlistMetadata SelectedModList
- {
- get => _selectedModList;
- set
- {
- CanInstall = true;
- RaiseAndSetIfChanged(ref _selectedModList, value);
- }
- }
-
- private bool _canInstall;
-
- public bool CanInstall
- {
- get => _canInstall;
- set => RaiseAndSetIfChanged(ref _canInstall, value);
+ this._CanInstall = this.WhenAny(x => x.SelectedModList)
+ .Select(x => x != null)
+ .ToProperty(this, nameof(this.CanInstall));
}
internal string Download()
@@ -60,7 +44,6 @@ namespace Wabbajack.UI
if (window.Result == DownloadWindow.WindowResult.Completed)
return dest;
return null;
-
}
}
}
diff --git a/Wabbajack/UI/SlideShow.cs b/Wabbajack/View Models/SlideShow.cs
similarity index 56%
rename from Wabbajack/UI/SlideShow.cs
rename to Wabbajack/View Models/SlideShow.cs
index 7c1393d1..1349c189 100644
--- a/Wabbajack/UI/SlideShow.cs
+++ b/Wabbajack/View Models/SlideShow.cs
@@ -1,4 +1,6 @@
-using ReactiveUI;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using Splat;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -13,6 +15,7 @@ using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using Wabbajack.Common;
using Wabbajack.Lib;
+using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.NexusApi;
namespace Wabbajack
@@ -30,45 +33,44 @@ namespace Wabbajack
public Queue SlidesQueue { get; }
- public AppState AppState { get; }
+ public InstallerVM Installer { get; }
- public BitmapImage NextIcon { get; } = UIUtils.BitmapImageFromResource("Wabbajack.UI.Icons.next.png");
- public BitmapImage WabbajackLogo { get; } = UIUtils.BitmapImageFromResource("Wabbajack.UI.Banner_Dark.png");
+ public BitmapImage NextIcon { get; } = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Icons.next.png");
- private bool _ShowNSFW;
- public bool ShowNSFW { get => _ShowNSFW; set => this.RaiseAndSetIfChanged(ref _ShowNSFW, value); }
+ [Reactive]
+ public bool ShowNSFW { get; set; }
- private bool _GCAfterUpdating = true;
- public bool GCAfterUpdating { get => _GCAfterUpdating; set => this.RaiseAndSetIfChanged(ref _GCAfterUpdating, value); }
+ [Reactive]
+ public bool GCAfterUpdating { get; set; } = true;
- private bool _Enable = true;
- public bool Enable { get => _Enable; set => this.RaiseAndSetIfChanged(ref _Enable, value); }
+ [Reactive]
+ public bool Enable { get; set; } = true;
- private BitmapImage _Image;
- public BitmapImage Image { get => _Image; set => this.RaiseAndSetIfChanged(ref _Image, value); }
+ [Reactive]
+ public BitmapImage Image { get; set; }
- private string _ModName = "Wabbajack";
- public string ModName { get => _ModName; set => this.RaiseAndSetIfChanged(ref _ModName, value); }
+ [Reactive]
+ public string ModName { get; set; } = "Wabbajack";
- private string _AuthorName = "Halgari & the Wabbajack Team";
- public string AuthorName { get => _AuthorName; set => this.RaiseAndSetIfChanged(ref _AuthorName, value); }
+ [Reactive]
+ public string AuthorName { get; set; } = "Halgari & the Wabbajack Team";
- private string _Summary;
- public string Summary { get => _Summary; set => this.RaiseAndSetIfChanged(ref _Summary, value); }
+ [Reactive]
+ public string Description { get; set; }
- private string _NexusSiteURL = "https://github.com/wabbajack-tools/wabbajack";
- public string NexusSiteURL { get => _NexusSiteURL; set => this.RaiseAndSetIfChanged(ref _NexusSiteURL, value); }
+ [Reactive]
+ public string NexusSiteURL { get; set; } = "https://github.com/wabbajack-tools/wabbajack";
public IReactiveCommand SlideShowNextItemCommand { get; } = ReactiveCommand.Create(() => { });
public IReactiveCommand VisitNexusSiteCommand { get; }
- public SlideShow(AppState appState)
+ public SlideShow(InstallerVM appState)
{
SlideShowElements = NexusApiClient.CachedSlideShow.ToList();
CachedSlides = new Dictionary();
SlidesQueue = new Queue();
_random = new Random();
- AppState = appState;
+ Installer = appState;
this.VisitNexusSiteCommand = ReactiveCommand.Create(
execute: () => Process.Start(this.NexusSiteURL),
@@ -77,91 +79,44 @@ namespace Wabbajack
.ObserveOnGuiThread());
// Apply modlist properties when it changes
- this.WhenAny(x => x.AppState.ModList)
+ this.WhenAny(x => x.Installer.ModList)
.NotNull()
- .Subscribe(modList =>
+ .ObserveOnGuiThread()
+ .Do(modList =>
{
- this.NexusSiteURL = modList.Website;
- this.ModName = modList.Name;
- this.AuthorName = modList.Author;
- this.Summary = modList.Description;
+ this.SlideShowElements = modList.Archives
+ .Select(m => m.State)
+ .OfType()
+ .Select(m =>
+ new Slide(NexusApiUtils.FixupSummary(m.ModName), m.ModID,
+ NexusApiUtils.FixupSummary(m.Summary), NexusApiUtils.FixupSummary(m.Author),
+ m.Adult, m.NexusURL, m.SlideShowPic)).ToList();
})
- .DisposeWith(this.CompositeDisposable);
-
- // Update splashscreen when modlist changes
- Observable.CombineLatest(
- (this).WhenAny(x => x.AppState.ModList),
- (this).WhenAny(x => x.AppState.ModListPath),
- (this).WhenAny(x => x.Enable),
- (modList, modListPath, enabled) => (modList, modListPath, enabled))
- // Do any potential unzipping on a background thread
.ObserveOn(RxApp.TaskpoolScheduler)
- .Select(u =>
+ .Do(modList =>
{
- if (u.enabled
- && u.modList != null
- && u.modListPath != null
- && File.Exists(u.modListPath)
- && !string.IsNullOrEmpty(u.modList.Image)
- && u.modList.Image.Length == 36)
- {
- try
- {
- using (var fs = new FileStream(u.modListPath, FileMode.Open, FileAccess.Read, FileShare.Read))
- using (var ar = new ZipArchive(fs, ZipArchiveMode.Read))
- using (var ms = new MemoryStream())
- {
- var entry = ar.GetEntry(u.modList.Image);
- using (var e = entry.Open())
- e.CopyTo(ms);
- var image = new BitmapImage();
- image.BeginInit();
- image.CacheOption = BitmapCacheOption.OnLoad;
- image.StreamSource = ms;
- image.EndInit();
- image.Freeze();
-
- return image;
- }
- }
- catch (Exception)
- {
- this.AppState.LogMsg("Error loading splash image.");
- }
- }
- return this.WabbajackLogo;
+ // This takes a while, and is currently blocking
+ this.PreloadSlideShow();
})
- .ObserveOn(RxApp.MainThreadScheduler)
- .StartWith(this.WabbajackLogo)
- .Subscribe(bitmap => this.Image = bitmap)
+ .Subscribe()
.DisposeWith(this.CompositeDisposable);
/// Wire slideshow updates
- var intervalSeconds = 10;
- // Compile all the sources that trigger a slideshow update
+ // Merge all the sources that trigger a slideshow update
Observable.Merge(
- // If user requests one manually
- this.SlideShowNextItemCommand.StartingExecution(),
// If the natural timer fires
- Observable.Merge(
- // Start with an initial timer
- Observable.Return(Observable.Interval(TimeSpan.FromSeconds(intervalSeconds))),
- // but reset timer if user requests one
- this.SlideShowNextItemCommand.StartingExecution()
- .Select(_ => Observable.Interval(TimeSpan.FromSeconds(intervalSeconds))))
- // When a new timer comes in, swap to it
- .Switch()
- .Unit())
- // When filter switch enabled, fire an initial signal
+ Observable.Interval(TimeSpan.FromSeconds(10))
+ .Unit()
+ // Only if enabled
+ .FilterSwitch(this.WhenAny(x => x.Enable)),
+ // If user requests one manually
+ this.SlideShowNextItemCommand.StartingExecution())
+ // When installing fire an initial signal
.StartWith(Unit.Default)
- // Only subscribe to slideshow triggers if enabled and installing
- .FilterSwitch(
- Observable.CombineLatest(
- this.WhenAny(x => x.Enable),
- this.WhenAny(x => x.AppState.Installing),
- resultSelector: (enabled, installing) => enabled && installing))
- // Don't ever update more than once every half second.
- .Debounce(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)
+ // Only subscribe to slideshow triggers if installing
+ .FilterSwitch(this.WhenAny(x => x.Installer.Installing))
+ // Don't ever update more than once every half second. ToDo: Update to debounce
+ .Throttle(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => this.UpdateSlideShowItem())
.DisposeWith(this.CompositeDisposable);
@@ -203,7 +158,7 @@ namespace Wabbajack
if (!slide.IsNSFW || (slide.IsNSFW && ShowNSFW))
{
- this.Image = AppState._noneImage;
+ this.Image = UIUtils.BitmapImageFromResource("Wabbajack.Resources.none.jpg");
if (slide.ImageURL != null && slide.Image != null)
{
if (!CachedSlides.ContainsKey(slide.ModID)) return;
@@ -212,7 +167,7 @@ namespace Wabbajack
this.ModName = slide.ModName;
this.AuthorName = slide.ModAuthor;
- this.Summary = slide.ModDescription;
+ this.Description = slide.ModDescription;
this.NexusSiteURL = slide.ModURL;
}
diff --git a/Wabbajack/UI/MainWindow.xaml b/Wabbajack/Views/CompilerView.xaml
similarity index 55%
rename from Wabbajack/UI/MainWindow.xaml
rename to Wabbajack/Views/CompilerView.xaml
index 944e224c..697fb259 100644
--- a/Wabbajack/UI/MainWindow.xaml
+++ b/Wabbajack/Views/CompilerView.xaml
@@ -1,21 +1,12 @@
-
@@ -30,8 +21,9 @@
-
-
+
+
+
+ Text="Compiling" />
-
+
-
+ Margin="0,8,0,8"
+ IsEnabled="{Binding UIReady}">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Value="{Binding MWVM.QueueProgress}" />
+ ItemsSource="{Binding MWVM.Log}" />
@@ -82,12 +147,11 @@
Grid.Row="5"
Grid.RowSpan="2"
Grid.Column="0"
- Margin="-4,10,2,10"
+ Margin="-4,10,0,10"
HorizontalAlignment="Stretch">
-
@@ -97,35 +161,27 @@
-
+
-
+ PathType="Folder"
+ SetTargetPathCommand="{Binding ChangePathCommand}"
+ TargetPath="{Binding Location}" />
-
-
+ PathType="Folder"
+ SetTargetPathCommand="{Binding ChangeDownloadPathCommand}"
+ TargetPath="{Binding DownloadLocation}" />
@@ -133,18 +189,17 @@
+ ItemsSource="{Binding MWVM.StatusList}">
@@ -185,8 +240,8 @@
+ Grid.Column="2"
+ Margin="0,10,0,10">
@@ -207,4 +262,4 @@
-
+
diff --git a/Wabbajack/UI/SlideshowView.xaml.cs b/Wabbajack/Views/CompilerView.xaml.cs
similarity index 79%
rename from Wabbajack/UI/SlideshowView.xaml.cs
rename to Wabbajack/Views/CompilerView.xaml.cs
index b183908a..d1a3eab5 100644
--- a/Wabbajack/UI/SlideshowView.xaml.cs
+++ b/Wabbajack/Views/CompilerView.xaml.cs
@@ -16,11 +16,11 @@ using System.Windows.Shapes;
namespace Wabbajack
{
///
- /// Interaction logic for SlideshowView.xaml
+ /// Interaction logic for CompilerView.xaml
///
- public partial class SlideshowView : UserControl
+ public partial class CompilerView : UserControl
{
- public SlideshowView()
+ public CompilerView()
{
InitializeComponent();
}
diff --git a/Wabbajack/UI/DownloadWindow.xaml b/Wabbajack/Views/DownloadWindow.xaml
similarity index 100%
rename from Wabbajack/UI/DownloadWindow.xaml
rename to Wabbajack/Views/DownloadWindow.xaml
diff --git a/Wabbajack/UI/DownloadWindow.xaml.cs b/Wabbajack/Views/DownloadWindow.xaml.cs
similarity index 100%
rename from Wabbajack/UI/DownloadWindow.xaml.cs
rename to Wabbajack/Views/DownloadWindow.xaml.cs
diff --git a/Wabbajack/Views/FilePicker.xaml b/Wabbajack/Views/FilePicker.xaml
new file mode 100644
index 00000000..c5877966
--- /dev/null
+++ b/Wabbajack/Views/FilePicker.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack/Views/FilePicker.xaml.cs b/Wabbajack/Views/FilePicker.xaml.cs
new file mode 100644
index 00000000..cdec4e08
--- /dev/null
+++ b/Wabbajack/Views/FilePicker.xaml.cs
@@ -0,0 +1,180 @@
+using Microsoft.WindowsAPICodePack.Dialogs;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Wabbajack
+{
+ ///
+ /// Interaction logic for FilePicker.xaml
+ ///
+ public partial class FilePicker : UserControlRx
+ {
+ public enum PathTypeOptions
+ {
+ Off,
+ Either,
+ File,
+ Folder
+ }
+
+ public ICommand SetTargetPathCommand
+ {
+ get => (ICommand)GetValue(SetTargetPathCommandProperty);
+ set => SetValue(SetTargetPathCommandProperty, value);
+ }
+ public static readonly DependencyProperty SetTargetPathCommandProperty = DependencyProperty.Register(nameof(SetTargetPathCommand), typeof(ICommand), typeof(FilePicker),
+ new FrameworkPropertyMetadata(default(ICommand)));
+
+ public string TargetPath
+ {
+ get { return (string)GetValue(TargetPathProperty); }
+ set { SetValue(TargetPathProperty, value); }
+ }
+ public static readonly DependencyProperty TargetPathProperty = DependencyProperty.Register(nameof(TargetPath), typeof(string), typeof(FilePicker),
+ new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WireNotifyPropertyChanged));
+
+ public bool ShowTextBoxInput
+ {
+ get => (bool)GetValue(ShowTextBoxInputProperty);
+ set => SetValue(ShowTextBoxInputProperty, value);
+ }
+ public static readonly DependencyProperty ShowTextBoxInputProperty = DependencyProperty.Register(nameof(ShowTextBoxInput), typeof(bool), typeof(FilePicker),
+ new FrameworkPropertyMetadata(true));
+
+ public PathTypeOptions PathType
+ {
+ get => (PathTypeOptions)GetValue(PathTypeProperty);
+ set => SetValue(PathTypeProperty, value);
+ }
+ public static readonly DependencyProperty PathTypeProperty = DependencyProperty.Register(nameof(PathType), typeof(PathTypeOptions), typeof(FilePicker),
+ new FrameworkPropertyMetadata(PathTypeOptions.Off, WireNotifyPropertyChanged));
+
+ public bool Exists
+ {
+ get => (bool)GetValue(ExistsProperty);
+ set => SetValue(ExistsProperty, value);
+ }
+ public static readonly DependencyProperty ExistsProperty = DependencyProperty.Register(nameof(Exists), typeof(bool), typeof(FilePicker),
+ new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, WireNotifyPropertyChanged));
+
+ public bool DoExistsCheck
+ {
+ get => (bool)GetValue(DoExistsCheckProperty);
+ set => SetValue(DoExistsCheckProperty, value);
+ }
+ public static readonly DependencyProperty DoExistsCheckProperty = DependencyProperty.Register(nameof(DoExistsCheck), typeof(bool), typeof(FilePicker),
+ new FrameworkPropertyMetadata(true, WireNotifyPropertyChanged));
+
+ public string PromptTitle
+ {
+ get => (string)GetValue(PromptTitleProperty);
+ set => SetValue(PromptTitleProperty, value);
+ }
+ public static readonly DependencyProperty PromptTitleProperty = DependencyProperty.Register(nameof(PromptTitle), typeof(string), typeof(FilePicker),
+ new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
+
+ public string Filter
+ {
+ get => (string)GetValue(FilterProperty);
+ set => SetValue(FilterProperty, value);
+ }
+ public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(nameof(Filter), typeof(string), typeof(FilePicker),
+ new FrameworkPropertyMetadata(default(string)));
+
+ public FilePicker()
+ {
+ InitializeComponent();
+ this.SetTargetPathCommand = ReactiveCommand.Create(
+ execute: () =>
+ {
+ string dirPath;
+ if (File.Exists(this.TargetPath))
+ {
+ dirPath = System.IO.Path.GetDirectoryName(this.TargetPath);
+ }
+ else
+ {
+ dirPath = this.TargetPath;
+ }
+ var dlg = new CommonOpenFileDialog();
+ dlg.Title = this.PromptTitle;
+ dlg.IsFolderPicker = this.PathType == PathTypeOptions.Folder;
+ dlg.InitialDirectory = this.TargetPath;
+
+ dlg.AddToMostRecentlyUsedList = false;
+ dlg.AllowNonFileSystemItems = false;
+ dlg.DefaultDirectory = this.TargetPath;
+ dlg.EnsureFileExists = true;
+ dlg.EnsurePathExists = true;
+ dlg.EnsureReadOnly = false;
+ if (!string.IsNullOrWhiteSpace(this.Filter))
+ {
+ var split = this.Filter.Split('|');
+ if (split.Length == 2)
+ {
+ dlg.Filters.Add(new CommonFileDialogFilter(split[0], split[1]));
+ }
+ }
+ dlg.EnsureValidNames = true;
+ dlg.Multiselect = false;
+ dlg.ShowPlacesList = true;
+ if (dlg.ShowDialog() != CommonFileDialogResult.Ok) return;
+ this.TargetPath = dlg.FileName;
+ });
+
+ // Check that file exists
+ Observable.Interval(TimeSpan.FromSeconds(3))
+ .FilterSwitch(
+ Observable.CombineLatest(
+ this.WhenAny(x => x.PathType),
+ this.WhenAny(x => x.DoExistsCheck),
+ resultSelector: (type, doExists) => type != PathTypeOptions.Off && doExists))
+ .Unit()
+ // Also do it when fields change
+ .Merge(this.WhenAny(x => x.PathType).Unit())
+ .Merge(this.WhenAny(x => x.DoExistsCheck).Unit())
+ .CombineLatest(
+ this.WhenAny(x => x.DoExistsCheck),
+ this.WhenAny(x => x.PathType),
+ this.WhenAny(x => x.TargetPath)
+ .Throttle(TimeSpan.FromMilliseconds(200)),
+ resultSelector: (_, DoExists, Type, Path) => (DoExists, Type, Path))
+ // Refresh exists
+ .Select(t =>
+ {
+ if (!t.DoExists) return true;
+ switch (t.Type)
+ {
+ case PathTypeOptions.Either:
+ return File.Exists(t.Path) || Directory.Exists(t.Path);
+ case PathTypeOptions.File:
+ return File.Exists(t.Path);
+ case PathTypeOptions.Folder:
+ return Directory.Exists(t.Path);
+ default:
+ return true;
+ }
+ })
+ .DistinctUntilChanged()
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(exists => this.Exists = exists)
+ .DisposeWith(this.CompositeDisposable);
+ }
+ }
+}
diff --git a/Wabbajack/Views/InstallationView.xaml b/Wabbajack/Views/InstallationView.xaml
new file mode 100644
index 00000000..b1b6a201
--- /dev/null
+++ b/Wabbajack/Views/InstallationView.xaml
@@ -0,0 +1,772 @@
+
+
+ #92000000
+
+ #DF000000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack/Views/InstallationView.xaml.cs b/Wabbajack/Views/InstallationView.xaml.cs
new file mode 100644
index 00000000..08c51bcb
--- /dev/null
+++ b/Wabbajack/Views/InstallationView.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Wabbajack
+{
+ ///
+ /// Interaction logic for InstallationView.xaml
+ ///
+ public partial class InstallationView : UserControl
+ {
+ public InstallationView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Wabbajack/Views/MainWindow.xaml b/Wabbajack/Views/MainWindow.xaml
new file mode 100644
index 00000000..8721b343
--- /dev/null
+++ b/Wabbajack/Views/MainWindow.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack/Views/MainWindow.xaml.cs b/Wabbajack/Views/MainWindow.xaml.cs
new file mode 100644
index 00000000..129793bf
--- /dev/null
+++ b/Wabbajack/Views/MainWindow.xaml.cs
@@ -0,0 +1,51 @@
+using System;
+using System.ComponentModel;
+using System.Threading;
+using System.Windows;
+using Wabbajack.Common;
+using Application = System.Windows.Application;
+
+namespace Wabbajack
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private MainWindowVM _mwvm;
+
+ public MainWindow()
+ {
+ string[] args = Environment.GetCommandLineArgs();
+
+ if (args.Length != 3) return;
+ var modlistPath = args[2];
+ Initialize(RunMode.Install, modlistPath);
+ }
+
+ public MainWindow(RunMode mode, string source)
+ {
+ Initialize(mode, source);
+ }
+
+ private void Initialize(RunMode mode, string source)
+ {
+ InitializeComponent();
+
+ _mwvm = new MainWindowVM(mode, source, this);
+ Utils.Log($"Wabbajack Build - {ThisAssembly.Git.Sha}");
+ this.DataContext = _mwvm;
+ }
+
+ internal bool ExitWhenClosing = true;
+
+ private void Window_Closing(object sender, CancelEventArgs e)
+ {
+ _mwvm.Dispose();
+ if (ExitWhenClosing)
+ {
+ Application.Current.Shutdown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wabbajack/Views/ModeSelectionWindow.xaml b/Wabbajack/Views/ModeSelectionWindow.xaml
new file mode 100644
index 00000000..dc16f6b8
--- /dev/null
+++ b/Wabbajack/Views/ModeSelectionWindow.xaml
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wabbajack/UI/ModeSelectionWindow.xaml.cs b/Wabbajack/Views/ModeSelectionWindow.xaml.cs
similarity index 90%
rename from Wabbajack/UI/ModeSelectionWindow.xaml.cs
rename to Wabbajack/Views/ModeSelectionWindow.xaml.cs
index 821d0695..7da4518a 100644
--- a/Wabbajack/UI/ModeSelectionWindow.xaml.cs
+++ b/Wabbajack/Views/ModeSelectionWindow.xaml.cs
@@ -21,16 +21,16 @@ namespace Wabbajack
public ModeSelectionWindow()
{
InitializeComponent();
- var bannerImage = UIUtils.BitmapImageFromResource("Wabbajack.UI.banner_small_dark.png");
+ var bannerImage = UIUtils.BitmapImageFromResource("Wabbajack.Resources.banner_small_dark.png");
Banner.Source = bannerImage;
- var patreonIcon = UIUtils.BitmapImageFromResource("Wabbajack.UI.Icons.patreon_light.png");
+ var patreonIcon = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Icons.patreon_light.png");
Patreon.Source = patreonIcon;
- var githubIcon = UIUtils.BitmapImageFromResource("Wabbajack.UI.Icons.github_light.png");
+ var githubIcon = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Icons.github_light.png");
GitHub.Source = githubIcon;
- var discordIcon = UIUtils.BitmapImageFromResource("Wabbajack.UI.Icons.discord.png");
+ var discordIcon = UIUtils.BitmapImageFromResource("Wabbajack.Resources.Icons.discord.png");
Discord.Source = discordIcon;
- DataContext = new ModeSelectionWindowViewModel();
+ DataContext = new ModeSelectionWindowVM();
}
private void CreateModlist_Click(object sender, RoutedEventArgs e)
@@ -46,7 +46,7 @@ namespace Wabbajack
// RunMode.Install,
// UIUtils.OpenFileDialog($"Wabbajack Modlist (*{Consts.ModlistExtension})|*{Consts.ModlistExtension}"));
- var result = ((ModeSelectionWindowViewModel)DataContext).Download();
+ var result = ((ModeSelectionWindowVM)DataContext).Download();
if (result != null)
{
OpenMainWindow(RunMode.Install, result);
diff --git a/Wabbajack/Views/TextViewer.xaml b/Wabbajack/Views/TextViewer.xaml
new file mode 100644
index 00000000..57a33777
--- /dev/null
+++ b/Wabbajack/Views/TextViewer.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/Wabbajack/UI/TextViewer.xaml.cs b/Wabbajack/Views/TextViewer.xaml.cs
similarity index 91%
rename from Wabbajack/UI/TextViewer.xaml.cs
rename to Wabbajack/Views/TextViewer.xaml.cs
index 792de20d..28d72054 100644
--- a/Wabbajack/UI/TextViewer.xaml.cs
+++ b/Wabbajack/Views/TextViewer.xaml.cs
@@ -1,6 +1,6 @@
using System.Windows;
-namespace Wabbajack.UI
+namespace Wabbajack
{
public partial class TextViewer : Window
{
diff --git a/Wabbajack/Views/UserControlRx.cs b/Wabbajack/Views/UserControlRx.cs
new file mode 100644
index 00000000..fb16b5b6
--- /dev/null
+++ b/Wabbajack/Views/UserControlRx.cs
@@ -0,0 +1,48 @@
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Wabbajack
+{
+ public class UserControlRx : UserControl, IDisposable, IReactiveObject
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+ public event PropertyChangingEventHandler PropertyChanging;
+
+ public void RaisePropertyChanging(PropertyChangingEventArgs args)
+ {
+ PropertyChanging?.Invoke(this, args);
+ }
+
+ public void RaisePropertyChanged(PropertyChangedEventArgs args)
+ {
+ PropertyChanged?.Invoke(this, args);
+ }
+
+ private readonly Lazy _CompositeDisposable = new Lazy();
+ public CompositeDisposable CompositeDisposable => _CompositeDisposable.Value;
+
+ public virtual void Dispose()
+ {
+ if (_CompositeDisposable.IsValueCreated)
+ {
+ _CompositeDisposable.Value.Dispose();
+ }
+ }
+
+ protected static void WireNotifyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (!(d is UserControlRx control)) return;
+ if (object.Equals(e.OldValue, e.NewValue)) return;
+ control.RaisePropertyChanged(e.Property.Name);
+ }
+ }
+}
diff --git a/Wabbajack/Wabbajack.csproj b/Wabbajack/Wabbajack.csproj
index a555ac17..31a67fc2 100644
--- a/Wabbajack/Wabbajack.csproj
+++ b/Wabbajack/Wabbajack.csproj
@@ -1,6 +1,5 @@
-
Debug
@@ -55,7 +54,7 @@
false
- UI\Icons\wabbajack.ico
+ Resources\Icons\wabbajack.ico
true
@@ -129,107 +128,18 @@
true
-
- ..\packages\AlphaFS.2.2.6\lib\net452\AlphaFS.dll
-
-
- ..\packages\CommonMark.NET.0.15.1\lib\net45\CommonMark.dll
-
-
- ..\packages\Costura.Fody.4.0.0\lib\net40\Costura.dll
-
-
- ..\packages\DynamicData.6.13.17\lib\net461\DynamicData.dll
-
-
- ..\packages\SharpZipLib.1.2.0\lib\net45\ICSharpCode.SharpZipLib.dll
-
-
- ..\packages\K4os.Compression.LZ4.1.1.11\lib\net46\K4os.Compression.LZ4.dll
-
-
- ..\packages\K4os.Compression.LZ4.Streams.1.1.11\lib\net46\K4os.Compression.LZ4.Streams.dll
-
-
- ..\packages\K4os.Hash.xxHash.1.0.6\lib\net46\K4os.Hash.xxHash.dll
-
-
- ..\packages\MegaApiClient.1.7.1\lib\net46\MegaApiClient.dll
-
-
- ..\packages\Microsoft-WindowsAPICodePack-Core.1.1.3.3\lib\net452\Microsoft.WindowsAPICodePack.dll
-
-
- ..\packages\Microsoft-WindowsAPICodePack-Shell.1.1.3.3\lib\net452\Microsoft.WindowsAPICodePack.Shell.dll
-
-
- ..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll
-
-
- ..\packages\Pharmacist.Common.1.2.2\lib\netstandard2.0\Pharmacist.Common.dll
-
-
- ..\packages\ReactiveUI.10.4.1\lib\net461\ReactiveUI.dll
-
-
- ..\packages\ReactiveUI.Events.WPF.10.4.1\lib\net461\ReactiveUI.Events.WPF.dll
-
-
- ..\packages\ReactiveUI.WPF.10.4.1\lib\net461\ReactiveUI.WPF.dll
-
-
- ..\packages\SharpCompress.0.23.0\lib\net45\SharpCompress.dll
-
-
- ..\packages\Syroot.Windows.IO.KnownFolders.1.2.1\lib\net452\Syroot.KnownFolders.dll
-
-
- ..\packages\Splat.9.1.1\lib\net461\Splat.dll
-
-
- ..\packages\Splat.Drawing.9.1.1\lib\net461\Splat.Drawing.dll
-
-
- ..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll
-
-
- ..\packages\System.Drawing.Primitives.4.3.0\lib\net45\System.Drawing.Primitives.dll
- True
- True
-
-
- ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll
-
-
- ..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll
-
-
- ..\packages\System.Reactive.4.2.0\lib\net46\System.Reactive.dll
-
-
- ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
-
-
- ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
-
-
- ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
-
@@ -242,53 +152,63 @@
4.0
-
- ..\packages\WebSocketSharpFork.1.0.4.0\lib\net35\websocket-sharp.dll
-
-
- ..\packages\YamlDotNet.8.0.0\lib\net45\YamlDotNet.dll
-
MSBuild:Compile
Designer
+
+
+ CompilerView.xaml
+
-
+
DownloadWindow.xaml
-
-
-
- SlideshowView.xaml
+
+
+
+ FilePicker.xaml
-
- ModlistPropertiesWindow.xaml
+
+ InstallationView.xaml
-
+
+
+
+
ModeSelectionWindow.xaml
-
-
-
-
+
+
+
+
TextViewer.xaml
-
+
+
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
-
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
MSBuild:Compile
Designer
@@ -296,17 +216,13 @@
App.xaml
Code
-
-
-
+
+
+
MainWindow.xaml
Code
-
- Designer
- MSBuild:Compile
-
-
+
Designer
MSBuild:Compile
@@ -314,9 +230,9 @@
MSBuild:Compile
Designer
-
- Designer
+
MSBuild:Compile
+ Designer
@@ -337,7 +253,6 @@
ResXFileCodeGenerator
Resources.Designer.cs
-
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -377,44 +292,115 @@
-
+
-
+
-
-
-
+
-
+
+
+
-
+
-
-
+
-
+
+
-
+
-
+
+
+
+
+ 2.2.6
+
+
+ 0.15.1
+
+
+ 4.0.0
+
+
+ 6.13.17
+
+
+ 5.1.1
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ 2.0.20
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ 1.1.11
+
+
+ 1.6.5
+
+
+ 2.3.0
+
+
+ 1.7.1
+
+
+ 1.1.3.3
+
+
+ 12.0.2
+
+
+ 1.0.2
+
+
+ 2.4.0
+
+
+ 10.4.1
+
+
+ 10.5.7
+
+
+ 10.4.1
+
+
+ 0.23.0
+
+
+ 1.2.0
+
+
+ 1.2.1
+
+
+ 4.2.0
+
+
+ 1.0.4
+
+
+ 1.0.8
+
+
+ 7.0.0
+
+
+
+
-
-
-
- This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wabbajack/packages.config b/Wabbajack/packages.config
deleted file mode 100644
index d56ee5fc..00000000
--- a/Wabbajack/packages.config
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file