diff --git a/Wabbajack.App.Wpf/View Models/Compilers/CompilerVM.cs b/Wabbajack.App.Wpf/View Models/Compilers/CompilerVM.cs index c164f246..37b89368 100644 --- a/Wabbajack.App.Wpf/View Models/Compilers/CompilerVM.cs +++ b/Wabbajack.App.Wpf/View Models/Compilers/CompilerVM.cs @@ -13,21 +13,32 @@ using ReactiveUI; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using System.Windows.Media; +using DynamicData; using DynamicData.Binding; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.WindowsAPICodePack.Dialogs; using ReactiveUI.Fody.Helpers; using Wabbajack.Common; using Wabbajack.Compiler; +using Wabbajack.Downloaders; +using Wabbajack.Downloaders.GameFile; using Wabbajack.DTOs; using Wabbajack.DTOs.Interventions; using Wabbajack.DTOs.JsonConverters; using Wabbajack.Installer; +using Wabbajack.Networking.WabbajackClientApi; using Wabbajack.Paths; using Wabbajack.Paths.IO; +using Wabbajack.Services.OSIntegrated; +using Wabbajack.VFS; namespace Wabbajack { + + public enum CompilerState { Configuration, @@ -37,7 +48,10 @@ namespace Wabbajack } public class CompilerVM : BackNavigatingVM { + private const string LastSavedCompilerSettings = "last-saved-compiler-settings"; private readonly DTOSerializer _dtos; + private readonly SettingsManager _settingsManager; + private readonly ServiceProvider _serviceProvider; [Reactive] public CompilerState State { get; set; } @@ -46,9 +60,9 @@ namespace Wabbajack public ISubCompilerVM SubCompilerVM { get; set; } // Paths - public FilePickerVM ModlistLocation { get; } = new(); - public FilePickerVM DownloadLocation { get; } = new(); - public FilePickerVM OutputLocation { get; } = new(); + public FilePickerVM ModlistLocation { get; } + public FilePickerVM DownloadLocation { get; } + public FilePickerVM OutputLocation { get; } // Modlist Settings @@ -67,19 +81,62 @@ namespace Wabbajack [Reactive] public string SelectedProfile { get; set; } [Reactive] public AbsolutePath GamePath { get; set; } [Reactive] public bool IsMO2Compilation { get; set; } - - [Reactive] public RelativePath[] AlwaysEnabled { get; set; } - [Reactive] public string[] OtherProfiles { get; set; } + + [Reactive] public RelativePath[] AlwaysEnabled { get; set; } = Array.Empty(); + [Reactive] public string[] OtherProfiles { get; set; } = Array.Empty(); [Reactive] public AbsolutePath Source { get; set; } - public AbsolutePath SettingsOutputLocation => ModlistLocation.TargetPath.Combine(ModListName) - .WithExtension(IsMO2Compilation ? Ext.MO2CompilerSettings : Ext.CompilerSettings); + public AbsolutePath SettingsOutputLocation => Source.Combine(ModListName).WithExtension(Ext.CompilerSettings); - public CompilerVM(ILogger logger, DTOSerializer dtos) : base(logger) + + public ReactiveCommand ExecuteCommand { get; } + + public CompilerVM(ILogger logger, DTOSerializer dtos, SettingsManager settingsManager, + ServiceProvider serviceProvider) : base(logger) { _dtos = dtos; + _settingsManager = settingsManager; + _serviceProvider = serviceProvider; + + BackCommand = + ReactiveCommand.CreateFromTask(async () => + { + await SaveSettingsFile(); + NavigateToGlobal.Send(NavigateToGlobal.ScreenType.ModeSelectionView); + }); + SubCompilerVM = new MO2CompilerVM(this); + + ExecuteCommand = ReactiveCommand.CreateFromTask(async () => await StartCompilation()); + + ModlistLocation = new FilePickerVM() + { + ExistCheckOption = FilePickerVM.CheckOptions.On, + PathType = FilePickerVM.PathTypeOptions.File, + PromptTitle = "Select a config file or a modlist.txt file" + }; + + DownloadLocation = new FilePickerVM() + { + ExistCheckOption = FilePickerVM.CheckOptions.On, + PathType = FilePickerVM.PathTypeOptions.Folder, + PromptTitle = "Location where the downloads for this list are stored" + }; + + OutputLocation = new FilePickerVM() + { + ExistCheckOption = FilePickerVM.CheckOptions.On, + PathType = FilePickerVM.PathTypeOptions.Folder, + PromptTitle = "Location where the compiled modlist will be stored" + }; + + ModlistLocation.Filters.AddRange(new [] + { + new CommonFileDialogFilter("MO2 Modlist", "*" + Ext.Txt), + new CommonFileDialogFilter("Compiler Settings File", "*" + Ext.CompilerSettings) + }); + this.WhenActivated(disposables => { @@ -89,11 +146,15 @@ namespace Wabbajack ModlistLocation.WhenAnyValue(vm => vm.TargetPath) .Subscribe(p => InferModListFromLocation(p).FireAndForget()) .DisposeWith(disposables); + + LoadLastSavedSettings().FireAndForget(); }); } private async Task InferModListFromLocation(AbsolutePath settingsFile) { + if (settingsFile == default) return; + using var ll = LoadingLock.WithLoading(); if (settingsFile.FileName == "modlist.txt".ToRelativePath() && settingsFile.Depth > 3) { @@ -113,7 +174,12 @@ namespace Wabbajack ModListName = SelectedProfile; var settings = iniData["Settings"]; - DownloadLocation.TargetPath = settings["download_directory"].FromMO2Ini().ToAbsolutePath(); + var downloadLocation = settings["download_directory"].FromMO2Ini().ToAbsolutePath(); + + if (downloadLocation == default) + downloadLocation = Source.Combine("downloads"); + + DownloadLocation.TargetPath = downloadLocation; IsMO2Compilation = true; @@ -146,15 +212,66 @@ namespace Wabbajack } } + } + + private async Task StartCompilation() + { + try + { + State = CompilerState.Compiling; + var mo2Settings = new MO2CompilerSettings + { + ModListName = ModListName, + ModListAuthor = Author, + ModlistReadme = Readme, + Source = Source, + Downloads = DownloadLocation.TargetPath, + OutputFile = OutputLocation.TargetPath, + Profile = SelectedProfile, + OtherProfiles = OtherProfiles, + AlwaysEnabled = AlwaysEnabled + }; + + var compiler = new MO2Compiler(_serviceProvider.GetRequiredService>(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + mo2Settings, + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService(), + _serviceProvider.GetRequiredService>(), + _serviceProvider.GetRequiredService()); + + await compiler.Begin(CancellationToken.None); + + State = CompilerState.Completed; + } + catch (Exception ex) + { + State = CompilerState.Errored; + } + + } private async Task SaveSettingsFile() { + if (Source == default) return; await using var st = SettingsOutputLocation.Open(FileMode.Create, FileAccess.Write, FileShare.None); - if (IsMO2Compilation) - await JsonSerializer.SerializeAsync(st, (MO2CompilerSettings) GetSettings(), _dtos.Options); - else - await JsonSerializer.SerializeAsync(st, GetSettings(), _dtos.Options); + await JsonSerializer.SerializeAsync(st, GetSettings(), _dtos.Options); + + await _settingsManager.Save(LastSavedCompilerSettings, Source); + } + + private async Task LoadLastSavedSettings() + { + var lastPath = await _settingsManager.Load(LastSavedCompilerSettings); + if (Source == default) return; + Source = lastPath; } diff --git a/Wabbajack.App.Wpf/Views/Compilers/CompilerView.xaml.cs b/Wabbajack.App.Wpf/Views/Compilers/CompilerView.xaml.cs index f3b6c35b..c9734f53 100644 --- a/Wabbajack.App.Wpf/Views/Compilers/CompilerView.xaml.cs +++ b/Wabbajack.App.Wpf/Views/Compilers/CompilerView.xaml.cs @@ -20,6 +20,15 @@ namespace Wabbajack this.WhenActivated(disposables => { + + + this.BindCommand(ViewModel, vm => vm.ExecuteCommand, view => view.BeginButton) + .DisposeWith(disposables); + + ViewModel.WhenAnyValue(vm => vm.BackCommand) + .BindToStrict(this, view => view.BackButton.Command) + .DisposeWith(disposables); + ViewModel.WhenAnyValue(vm => vm.State) .Select(v => v == CompilerState.Configuration ? Visibility.Visible : Visibility.Collapsed) .BindToStrict(this, view => view.BottomCompilerSettingsGrid.Visibility) @@ -44,8 +53,49 @@ namespace Wabbajack .BindToStrict(this, view => view.CompilerConfigView.ModListLocation.PickerVM) .DisposeWith(disposables); + ViewModel.WhenAnyValue(vm => vm.DownloadLocation) + .BindToStrict(this, view => view.CompilerConfigView.DownloadsLocation.PickerVM) + .DisposeWith(disposables); + + ViewModel.WhenAnyValue(vm => vm.OutputLocation) + .BindToStrict(this, view => view.CompilerConfigView.OutputLocation.PickerVM) + .DisposeWith(disposables); + UserInterventionsControl.Visibility = Visibility.Collapsed; + + + // Settings + + this.Bind(ViewModel, vm => vm.ModListName, view => view.ModListNameSetting.Text) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.Author, view => view.AuthorNameSetting.Text) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.Version, view => view.VersionSetting.Text) + .DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Description, view => view.DescriptionSetting.Text) + .DisposeWith(disposables); + + + this.Bind(ViewModel, vm => vm.ModListImagePath, view => view.ImageFilePicker.PickerVM) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.Website, view => view.WebsiteSetting.Text) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.Readme, view => view.ReadmeSetting.Text) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.IsNSFW, view => view.NSFWSetting.IsChecked) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.PublishUpdate, view => view.PublishUpdate.IsChecked) + .DisposeWith(disposables); + + this.Bind(ViewModel, vm => vm.MachineUrl, view => view.MachineUrl.Text) + .DisposeWith(disposables); }); diff --git a/Wabbajack.App.Wpf/Views/Compilers/MO2CompilerConfigView.xaml b/Wabbajack.App.Wpf/Views/Compilers/MO2CompilerConfigView.xaml index f8cc4efa..dba8fb91 100644 --- a/Wabbajack.App.Wpf/Views/Compilers/MO2CompilerConfigView.xaml +++ b/Wabbajack.App.Wpf/Views/Compilers/MO2CompilerConfigView.xaml @@ -42,10 +42,10 @@ TextAlignment="Center" ToolTip="The folder where MO2 downloads your mods." /> diff --git a/Wabbajack.Common/Ext.cs b/Wabbajack.Common/Ext.cs index fface41c..10c5304f 100644 --- a/Wabbajack.Common/Ext.cs +++ b/Wabbajack.Common/Ext.cs @@ -24,4 +24,5 @@ public static class Ext public static Extension MO2CompilerSettings = new(".mo2_compiler_settings"); public static Extension Temp = new(".temp"); public static Extension ModlistMetadataExtension = new(".modlist_metadata"); + public static Extension Txt = new(".txt"); } \ No newline at end of file