From d609e50afb9282e5468388d7e9f2dbd7c530ac18 Mon Sep 17 00:00:00 2001 From: Justin Swanson Date: Mon, 2 Dec 2019 20:38:33 -0600 Subject: [PATCH] Vortex Installer implemented --- Wabbajack.Lib/VortexCompiler.cs | 7 +- Wabbajack/Settings.cs | 4 +- .../View Models/Compilers/VortexCompilerVM.cs | 2 +- .../View Models/Installers/InstallerVM.cs | 65 ++----------- .../View Models/Installers/MO2InstallerVM.cs | 77 +++++++++++++++- .../Installers/VortexInstallerVM.cs | 92 +++++++++++++++++++ .../Views/Installers/InstallationView.xaml | 3 + .../Installers/VortexInstallerConfigView.xaml | 14 +++ .../VortexInstallerConfigView.xaml.cs | 28 ++++++ Wabbajack/Wabbajack.csproj | 10 +- 10 files changed, 234 insertions(+), 68 deletions(-) create mode 100644 Wabbajack/View Models/Installers/VortexInstallerVM.cs create mode 100644 Wabbajack/Views/Installers/VortexInstallerConfigView.xaml create mode 100644 Wabbajack/Views/Installers/VortexInstallerConfigView.xaml.cs diff --git a/Wabbajack.Lib/VortexCompiler.cs b/Wabbajack.Lib/VortexCompiler.cs index 19d94fd9..19351e32 100644 --- a/Wabbajack.Lib/VortexCompiler.cs +++ b/Wabbajack.Lib/VortexCompiler.cs @@ -336,7 +336,7 @@ namespace Wabbajack.Lib new IgnoreDisabledVortexMods(this), new IncludeVortexDeployment(this), new IgnoreVortex(this), - new IgnoreRegex(this, "^*__vortex_staging_folder$"), + new IgnoreRegex(this, $"^*{StagingMarkerName}$"), Game == Game.DarkestDungeon ? new IncludeRegex(this, "project\\.xml$") : null, @@ -396,6 +396,11 @@ namespace Wabbajack.Lib { return IsValidBaseStagingFolder(Path.GetDirectoryName(path)); } + + public static bool IsActiveVortexGame(Game g) + { + return GameRegistry.Games[g].SupportedModManager == ModManager.Vortex && !GameRegistry.Games[g].Disabled; + } } public class VortexDeployment diff --git a/Wabbajack/Settings.cs b/Wabbajack/Settings.cs index 522500ce..d4d6367d 100644 --- a/Wabbajack/Settings.cs +++ b/Wabbajack/Settings.cs @@ -47,10 +47,10 @@ namespace Wabbajack public class InstallerSettings { public string LastInstalledListLocation { get; set; } - public Dictionary ModlistSettings { get; } = new Dictionary(); + public Dictionary Mo2ModlistSettings { get; } = new Dictionary(); } - public class ModlistInstallationSettings + public class Mo2ModlistInstallationSettings { public string InstallationLocation { get; set; } public string DownloadLocation { get; set; } diff --git a/Wabbajack/View Models/Compilers/VortexCompilerVM.cs b/Wabbajack/View Models/Compilers/VortexCompilerVM.cs index b369a523..ea0e16ec 100644 --- a/Wabbajack/View Models/Compilers/VortexCompilerVM.cs +++ b/Wabbajack/View Models/Compilers/VortexCompilerVM.cs @@ -23,7 +23,7 @@ namespace Wabbajack private static readonly ObservableCollectionExtended _gameOptions = new ObservableCollectionExtended( EnumExt.GetValues() - .Where(g => GameRegistry.Games[g].SupportedModManager == ModManager.Vortex && !GameRegistry.Games[g].Disabled) + .Where(g => VortexCompiler.IsActiveVortexGame(g)) .Select(g => new GameVM(g)) .OrderBy(g => g.DisplayName)); diff --git a/Wabbajack/View Models/Installers/InstallerVM.cs b/Wabbajack/View Models/Installers/InstallerVM.cs index 987e09a2..a4b2bad7 100644 --- a/Wabbajack/View Models/Installers/InstallerVM.cs +++ b/Wabbajack/View Models/Installers/InstallerVM.cs @@ -1,4 +1,4 @@ -using Syroot.Windows.IO; +using Syroot.Windows.IO; using System; using ReactiveUI; using System.Diagnostics; @@ -49,10 +49,6 @@ namespace Wabbajack [Reactive] public bool InstallingMode { get; set; } - public FilePickerVM Location { get; } - - public FilePickerVM DownloadLocation { get; } - private readonly ObservableAsPropertyHelper _image; public ImageSource Image => _image.Value; @@ -77,9 +73,6 @@ namespace Wabbajack public ObservableCollectionExtended StatusList { get; } = new ObservableCollectionExtended(); public ObservableCollectionExtended Log => MWVM.Log; - private readonly ObservableAsPropertyHelper _CurrentSettings; - public ModlistInstallationSettings CurrentSettings => _CurrentSettings.Value; - private readonly ObservableAsPropertyHelper _TargetManager; public ModManager? TargetManager => _TargetManager.Value; @@ -104,22 +97,6 @@ namespace Wabbajack MWVM = mainWindowVM; - Location = new FilePickerVM() - { - ExistCheckOption = FilePickerVM.ExistCheckOptions.Off, - PathType = FilePickerVM.PathTypeOptions.Folder, - PromptTitle = "Select Installation Directory", - }; - Location.AdditionalError = this.WhenAny(x => x.Location.TargetPath) - .Select(x => Utils.IsDirectoryPathValid(x)); - DownloadLocation = new FilePickerVM() - { - ExistCheckOption = FilePickerVM.ExistCheckOptions.Off, - PathType = FilePickerVM.PathTypeOptions.Folder, - PromptTitle = "Select a location for MO2 downloads", - }; - DownloadLocation.AdditionalError = this.WhenAny(x => x.DownloadLocation.TargetPath) - .Select(x => Utils.IsDirectoryPathValid(x)); ModListPath = new FilePickerVM() { ExistCheckOption = FilePickerVM.ExistCheckOptions.On, @@ -138,7 +115,7 @@ namespace Wabbajack case ModManager.MO2: return new MO2InstallerVM(this); case ModManager.Vortex: - throw new NotImplementedException(); + return new VortexInstallerVM(this); default: return null; } @@ -153,21 +130,11 @@ namespace Wabbajack .ToProperty(this, nameof(Installer)); // Load settings - _CurrentSettings = this.WhenAny(x => x.ModListPath.TargetPath) - .Select(path => path == null ? null : MWVM.Settings.Installer.ModlistSettings.TryCreate(path)) - .ToProperty(this, nameof(CurrentSettings)); - this.WhenAny(x => x.CurrentSettings) - .Pairwise() - .Subscribe(settingsPair => - { - SaveSettings(settingsPair.Previous); - if (settingsPair.Current == null) return; - Location.TargetPath = settingsPair.Current.InstallationLocation; - DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation; - }) - .DisposeWith(CompositeDisposable); MWVM.Settings.SaveSignal - .Subscribe(_ => SaveSettings(CurrentSettings)) + .Subscribe(_ => + { + MWVM.Settings.Installer.LastInstalledListLocation = ModListPath.TargetPath; + }) .DisposeWith(CompositeDisposable); _modList = this.WhenAny(x => x.ModListPath.TargetPath) @@ -267,18 +234,6 @@ namespace Wabbajack .Select(x => x?.StartsWith("https://") ?? false) .ObserveOnGuiThread()); - // Have Installation location updates modify the downloads location if empty - this.WhenAny(x => x.Location.TargetPath) - .Skip(1) // Don't do it initially - .Subscribe(installPath => - { - if (string.IsNullOrWhiteSpace(DownloadLocation.TargetPath)) - { - DownloadLocation.TargetPath = Path.Combine(installPath, "downloads"); - } - }) - .DisposeWith(CompositeDisposable); - _progressTitle = Observable.CombineLatest( this.WhenAny(x => x.Installing), this.WhenAny(x => x.InstallingMode), @@ -346,13 +301,5 @@ namespace Wabbajack } } } - - private void SaveSettings(ModlistInstallationSettings settings) - { - MWVM.Settings.Installer.LastInstalledListLocation = ModListPath.TargetPath; - if (settings == null) return; - settings.InstallationLocation = Location.TargetPath; - settings.DownloadLocation = DownloadLocation.TargetPath; - } } } diff --git a/Wabbajack/View Models/Installers/MO2InstallerVM.cs b/Wabbajack/View Models/Installers/MO2InstallerVM.cs index ab4a0ae5..167c87c1 100644 --- a/Wabbajack/View Models/Installers/MO2InstallerVM.cs +++ b/Wabbajack/View Models/Installers/MO2InstallerVM.cs @@ -1,6 +1,8 @@ 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; @@ -13,17 +15,45 @@ namespace Wabbajack { public class MO2InstallerVM : ViewModel, ISubInstallerVM { + private InstallerVM _installerVM; + public IReactiveCommand BeginCommand { get; } [Reactive] public AInstaller ActiveInstallation { get; private set; } + private readonly ObservableAsPropertyHelper _CurrentSettings; + public Mo2ModlistInstallationSettings CurrentSettings => _CurrentSettings.Value; + + public FilePickerVM Location { get; } + + public FilePickerVM DownloadLocation { get; } + public MO2InstallerVM(InstallerVM installerVM) { + _installerVM = installerVM; + + Location = new FilePickerVM() + { + ExistCheckOption = FilePickerVM.ExistCheckOptions.Off, + PathType = FilePickerVM.PathTypeOptions.Folder, + PromptTitle = "Select Installation Directory", + }; + Location.AdditionalError = this.WhenAny(x => x.Location.TargetPath) + .Select(x => Utils.IsDirectoryPathValid(x)); + DownloadLocation = new FilePickerVM() + { + ExistCheckOption = FilePickerVM.ExistCheckOptions.Off, + PathType = FilePickerVM.PathTypeOptions.Folder, + PromptTitle = "Select a location for MO2 downloads", + }; + DownloadLocation.AdditionalError = this.WhenAny(x => x.DownloadLocation.TargetPath) + .Select(x => Utils.IsDirectoryPathValid(x)); + BeginCommand = ReactiveCommand.CreateFromTask( canExecute: Observable.CombineLatest( - installerVM.WhenAny(x => x.Location.InError), - installerVM.WhenAny(x => x.DownloadLocation.InError), + this.WhenAny(x => x.Location.InError), + this.WhenAny(x => x.DownloadLocation.InError), resultSelector: (loc, download) => { return !loc && !download; @@ -38,8 +68,8 @@ namespace Wabbajack installer = new MO2Installer( archive: installerVM.ModListPath.TargetPath, modList: installerVM.ModList.SourceModList, - outputFolder: installerVM.Location.TargetPath, - downloadFolder: installerVM.DownloadLocation.TargetPath); + outputFolder: Location.TargetPath, + downloadFolder: DownloadLocation.TargetPath); } catch (Exception ex) { @@ -75,10 +105,49 @@ namespace Wabbajack } }); }); + + // Have Installation location updates modify the downloads location if empty + this.WhenAny(x => x.Location.TargetPath) + .Skip(1) // Don't do it initially + .Subscribe(installPath => + { + if (string.IsNullOrWhiteSpace(DownloadLocation.TargetPath)) + { + DownloadLocation.TargetPath = Path.Combine(installPath, "downloads"); + } + }) + .DisposeWith(CompositeDisposable); + + // Load settings + _CurrentSettings = installerVM.WhenAny(x => x.ModListPath.TargetPath) + .Select(path => path == null ? null : installerVM.MWVM.Settings.Installer.Mo2ModlistSettings.TryCreate(path)) + .ToProperty(this, nameof(CurrentSettings)); + this.WhenAny(x => x.CurrentSettings) + .Pairwise() + .Subscribe(settingsPair => + { + SaveSettings(settingsPair.Previous); + if (settingsPair.Current == null) return; + Location.TargetPath = settingsPair.Current.InstallationLocation; + DownloadLocation.TargetPath = settingsPair.Current.DownloadLocation; + }) + .DisposeWith(CompositeDisposable); + installerVM.MWVM.Settings.SaveSignal + .Subscribe(_ => SaveSettings(CurrentSettings)) + .DisposeWith(CompositeDisposable); } public void Unload() { + SaveSettings(this.CurrentSettings); + } + + private void SaveSettings(Mo2ModlistInstallationSettings settings) + { + _installerVM.MWVM.Settings.Installer.LastInstalledListLocation = _installerVM.ModListPath.TargetPath; + if (settings == null) return; + settings.InstallationLocation = Location.TargetPath; + settings.DownloadLocation = DownloadLocation.TargetPath; } } } diff --git a/Wabbajack/View Models/Installers/VortexInstallerVM.cs b/Wabbajack/View Models/Installers/VortexInstallerVM.cs new file mode 100644 index 00000000..dccac18a --- /dev/null +++ b/Wabbajack/View Models/Installers/VortexInstallerVM.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Wabbajack.Common; +using Wabbajack.Lib; + +namespace Wabbajack +{ + public class VortexInstallerVM : ViewModel, ISubInstallerVM + { + public IReactiveCommand BeginCommand { get; } + + [Reactive] + public AInstaller ActiveInstallation { get; private set; } + + private readonly ObservableAsPropertyHelper _DownloadLocation; + public string DownloadLocation => _DownloadLocation.Value; + + private readonly ObservableAsPropertyHelper _StagingLocation; + public string StagingLocation => _StagingLocation.Value; + + private readonly ObservableAsPropertyHelper _TargetGame; + public Game TargetGame => _TargetGame.Value; + + public VortexInstallerVM(InstallerVM installerVM) + { + _TargetGame = installerVM.WhenAny(x => x.ModList.SourceModList.GameType) + .ToProperty(this, nameof(TargetGame)); + + BeginCommand = ReactiveCommand.CreateFromTask( + canExecute: this.WhenAny(x => x.TargetGame) + .Select(game => VortexCompiler.IsActiveVortexGame(game)), + execute: async () => + { + AInstaller installer; + + try + { + var download = VortexCompiler.RetrieveDownloadLocation(TargetGame); + var staging = VortexCompiler.RetrieveStagingLocation(TargetGame); + installer = new VortexInstaller( + archive: installerVM.ModListPath.TargetPath, + modList: installerVM.ModList.SourceModList, + outputFolder: staging, + downloadFolder: download); + } + 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"); + ActiveInstallation = null; + return; + } + + await Task.Run(async () => + { + IDisposable subscription = null; + try + { + var workTask = installer.Begin(); + ActiveInstallation = installer; + await workTask; + } + 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 + { + // Dispose of CPU tracking systems + subscription?.Dispose(); + ActiveInstallation = null; + } + }); + }); + } + + public void Unload() + { + } + } +} diff --git a/Wabbajack/Views/Installers/InstallationView.xaml b/Wabbajack/Views/Installers/InstallationView.xaml index b1594119..e03b4aec 100644 --- a/Wabbajack/Views/Installers/InstallationView.xaml +++ b/Wabbajack/Views/Installers/InstallationView.xaml @@ -324,6 +324,9 @@ + + + diff --git a/Wabbajack/Views/Installers/VortexInstallerConfigView.xaml b/Wabbajack/Views/Installers/VortexInstallerConfigView.xaml new file mode 100644 index 00000000..a6336541 --- /dev/null +++ b/Wabbajack/Views/Installers/VortexInstallerConfigView.xaml @@ -0,0 +1,14 @@ + + + + + diff --git a/Wabbajack/Views/Installers/VortexInstallerConfigView.xaml.cs b/Wabbajack/Views/Installers/VortexInstallerConfigView.xaml.cs new file mode 100644 index 00000000..0b27d923 --- /dev/null +++ b/Wabbajack/Views/Installers/VortexInstallerConfigView.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 VortexInstallerConfigView.xaml + /// + public partial class VortexInstallerConfigView : UserControl + { + public VortexInstallerConfigView() + { + InitializeComponent(); + } + } +} diff --git a/Wabbajack/Wabbajack.csproj b/Wabbajack/Wabbajack.csproj index 889dd7ef..afe4918e 100644 --- a/Wabbajack/Wabbajack.csproj +++ b/Wabbajack/Wabbajack.csproj @@ -1,4 +1,4 @@ - + @@ -174,6 +174,7 @@ + @@ -246,6 +247,9 @@ VortexCompilerConfigView.xaml + + VortexInstallerConfigView.xaml + Designer MSBuild:Compile @@ -332,6 +336,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile +