2022-03-13 22:47:30 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reactive;
|
|
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Wabbajack.Extensions;
|
|
|
|
|
using Wabbajack.Interventions;
|
|
|
|
|
using Wabbajack.Messages;
|
|
|
|
|
using Wabbajack.RateLimiter;
|
|
|
|
|
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.Models;
|
|
|
|
|
using Wabbajack.Networking.WabbajackClientApi;
|
|
|
|
|
using Wabbajack.Paths;
|
|
|
|
|
using Wabbajack.Paths.IO;
|
|
|
|
|
using Wabbajack.Services.OSIntegrated;
|
|
|
|
|
using Wabbajack.VFS;
|
|
|
|
|
|
|
|
|
|
namespace Wabbajack
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public enum CompilerState
|
|
|
|
|
{
|
|
|
|
|
Configuration,
|
|
|
|
|
Compiling,
|
|
|
|
|
Completed,
|
|
|
|
|
Errored
|
|
|
|
|
}
|
|
|
|
|
public class CompilerVM : BackNavigatingVM, ICpuStatusVM
|
|
|
|
|
{
|
|
|
|
|
private const string LastSavedCompilerSettings = "last-saved-compiler-settings";
|
|
|
|
|
private readonly DTOSerializer _dtos;
|
|
|
|
|
private readonly SettingsManager _settingsManager;
|
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
private readonly ILogger<CompilerVM> _logger;
|
|
|
|
|
private readonly ResourceMonitor _resourceMonitor;
|
2022-05-25 02:59:15 +00:00
|
|
|
|
private readonly CompilerSettingsInferencer _inferencer;
|
2022-03-13 22:47:30 +00:00
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public CompilerState State { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive]
|
|
|
|
|
public ISubCompilerVM SubCompilerVM { get; set; }
|
|
|
|
|
|
|
|
|
|
// Paths
|
|
|
|
|
public FilePickerVM ModlistLocation { get; }
|
|
|
|
|
public FilePickerVM DownloadLocation { get; }
|
|
|
|
|
public FilePickerVM OutputLocation { get; }
|
|
|
|
|
|
|
|
|
|
// Modlist Settings
|
|
|
|
|
|
|
|
|
|
[Reactive] public string ModListName { get; set; }
|
|
|
|
|
[Reactive] public string Version { get; set; }
|
|
|
|
|
[Reactive] public string Author { get; set; }
|
|
|
|
|
[Reactive] public string Description { get; set; }
|
|
|
|
|
public FilePickerVM ModListImagePath { get; } = new();
|
|
|
|
|
[Reactive] public ImageSource ModListImage { get; set; }
|
|
|
|
|
[Reactive] public string Website { get; set; }
|
|
|
|
|
[Reactive] public string Readme { get; set; }
|
|
|
|
|
[Reactive] public bool IsNSFW { get; set; }
|
|
|
|
|
[Reactive] public bool PublishUpdate { get; set; }
|
|
|
|
|
[Reactive] public string MachineUrl { get; set; }
|
|
|
|
|
[Reactive] public Game BaseGame { get; set; }
|
|
|
|
|
[Reactive] public string SelectedProfile { get; set; }
|
|
|
|
|
[Reactive] public AbsolutePath GamePath { get; set; }
|
|
|
|
|
[Reactive] public bool IsMO2Compilation { get; set; }
|
|
|
|
|
|
|
|
|
|
[Reactive] public RelativePath[] AlwaysEnabled { get; set; } = Array.Empty<RelativePath>();
|
2022-05-26 05:21:12 +00:00
|
|
|
|
[Reactive] public RelativePath[] NoMatchInclude { get; set; } = Array.Empty<RelativePath>();
|
2022-05-25 02:59:15 +00:00
|
|
|
|
|
2022-03-13 22:47:30 +00:00
|
|
|
|
[Reactive] public string[] OtherProfiles { get; set; } = Array.Empty<string>();
|
|
|
|
|
|
|
|
|
|
[Reactive] public AbsolutePath Source { get; set; }
|
|
|
|
|
|
|
|
|
|
public AbsolutePath SettingsOutputLocation => Source.Combine(ModListName).WithExtension(Ext.CompilerSettings);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ReactiveCommand<Unit, Unit> ExecuteCommand { get; }
|
|
|
|
|
|
2022-05-22 04:27:02 +00:00
|
|
|
|
public LogStream LoggerProvider { get; }
|
2022-03-13 22:47:30 +00:00
|
|
|
|
public ReadOnlyObservableCollection<CPUDisplayVM> StatusList => _resourceMonitor.Tasks;
|
|
|
|
|
|
|
|
|
|
public CompilerVM(ILogger<CompilerVM> logger, DTOSerializer dtos, SettingsManager settingsManager,
|
2022-05-25 02:59:15 +00:00
|
|
|
|
IServiceProvider serviceProvider, LogStream loggerProvider, ResourceMonitor resourceMonitor,
|
|
|
|
|
CompilerSettingsInferencer inferencer) : base(logger)
|
2022-03-13 22:47:30 +00:00
|
|
|
|
{
|
|
|
|
|
_logger = logger;
|
|
|
|
|
_dtos = dtos;
|
|
|
|
|
_settingsManager = settingsManager;
|
|
|
|
|
_serviceProvider = serviceProvider;
|
|
|
|
|
LoggerProvider = loggerProvider;
|
|
|
|
|
_resourceMonitor = resourceMonitor;
|
2022-05-25 02:59:15 +00:00
|
|
|
|
_inferencer = inferencer;
|
2022-03-13 22:47:30 +00:00
|
|
|
|
|
|
|
|
|
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 =>
|
|
|
|
|
{
|
|
|
|
|
State = CompilerState.Configuration;
|
|
|
|
|
Disposable.Empty.DisposeWith(disposables);
|
|
|
|
|
|
|
|
|
|
ModlistLocation.WhenAnyValue(vm => vm.TargetPath)
|
|
|
|
|
.Subscribe(p => InferModListFromLocation(p).FireAndForget())
|
|
|
|
|
.DisposeWith(disposables);
|
|
|
|
|
|
|
|
|
|
LoadLastSavedSettings().FireAndForget();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-25 02:59:15 +00:00
|
|
|
|
private async Task InferModListFromLocation(AbsolutePath path)
|
2022-03-13 22:47:30 +00:00
|
|
|
|
{
|
2022-05-25 02:59:15 +00:00
|
|
|
|
using var _ = LoadingLock.WithLoading();
|
|
|
|
|
if (path == default || path.FileName != "modlist.txt".ToRelativePath())
|
|
|
|
|
return;
|
2022-03-13 22:47:30 +00:00
|
|
|
|
|
2022-05-25 02:59:15 +00:00
|
|
|
|
var settings = await _inferencer.InferModListFromLocation(path);
|
|
|
|
|
if (settings == null) return;
|
2022-03-13 22:47:30 +00:00
|
|
|
|
|
2022-05-25 02:59:15 +00:00
|
|
|
|
BaseGame = settings.Game;
|
|
|
|
|
ModListName = settings.ModListName;
|
|
|
|
|
Source = settings.Source;
|
|
|
|
|
DownloadLocation.TargetPath = settings.Downloads;
|
|
|
|
|
OutputLocation.TargetPath = settings.OutputFile;
|
|
|
|
|
SelectedProfile = settings.Profile;
|
2022-05-30 20:50:24 +00:00
|
|
|
|
OtherProfiles = settings.AdditionalProfiles;
|
2022-05-25 02:59:15 +00:00
|
|
|
|
AlwaysEnabled = settings.AlwaysEnabled;
|
2022-05-26 05:21:12 +00:00
|
|
|
|
NoMatchInclude = settings.NoMatchInclude;
|
2022-03-13 22:47:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-25 02:59:15 +00:00
|
|
|
|
|
2022-03-13 22:47:30 +00:00
|
|
|
|
private async Task StartCompilation()
|
|
|
|
|
{
|
|
|
|
|
var tsk = Task.Run(async () =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
State = CompilerState.Compiling;
|
|
|
|
|
|
2022-05-27 05:41:11 +00:00
|
|
|
|
var mo2Settings = new CompilerSettings
|
2022-03-13 22:47:30 +00:00
|
|
|
|
{
|
|
|
|
|
Game = BaseGame,
|
|
|
|
|
ModListName = ModListName,
|
|
|
|
|
ModListAuthor = Author,
|
|
|
|
|
ModlistReadme = Readme,
|
|
|
|
|
Source = Source,
|
|
|
|
|
Downloads = DownloadLocation.TargetPath,
|
|
|
|
|
OutputFile = OutputLocation.TargetPath,
|
|
|
|
|
Profile = SelectedProfile,
|
2022-05-30 20:50:24 +00:00
|
|
|
|
AdditionalProfiles = OtherProfiles,
|
2022-05-25 21:54:46 +00:00
|
|
|
|
AlwaysEnabled = AlwaysEnabled,
|
2022-05-26 05:21:12 +00:00
|
|
|
|
NoMatchInclude = NoMatchInclude,
|
2022-05-25 21:54:46 +00:00
|
|
|
|
UseGamePaths = true
|
2022-03-13 22:47:30 +00:00
|
|
|
|
};
|
|
|
|
|
|
2022-05-27 05:41:11 +00:00
|
|
|
|
var compiler = MO2Compiler.Create(_serviceProvider, mo2Settings);
|
2022-03-13 22:47:30 +00:00
|
|
|
|
|
|
|
|
|
await compiler.Begin(CancellationToken.None);
|
|
|
|
|
|
|
|
|
|
State = CompilerState.Completed;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
State = CompilerState.Errored;
|
|
|
|
|
_logger.LogInformation(ex, "Failed Compilation : {Message}", ex.Message);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await tsk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task SaveSettingsFile()
|
|
|
|
|
{
|
|
|
|
|
if (Source == default) return;
|
|
|
|
|
await using var st = SettingsOutputLocation.Open(FileMode.Create, FileAccess.Write, FileShare.None);
|
|
|
|
|
await JsonSerializer.SerializeAsync(st, GetSettings(), _dtos.Options);
|
|
|
|
|
|
|
|
|
|
await _settingsManager.Save(LastSavedCompilerSettings, Source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task LoadLastSavedSettings()
|
|
|
|
|
{
|
|
|
|
|
var lastPath = await _settingsManager.Load<AbsolutePath>(LastSavedCompilerSettings);
|
|
|
|
|
if (Source == default) return;
|
|
|
|
|
Source = lastPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private CompilerSettings GetSettings()
|
|
|
|
|
{
|
|
|
|
|
return new CompilerSettings
|
|
|
|
|
{
|
|
|
|
|
ModListName = ModListName,
|
|
|
|
|
ModListAuthor = Author,
|
|
|
|
|
Downloads = DownloadLocation.TargetPath,
|
|
|
|
|
Source = ModlistLocation.TargetPath,
|
|
|
|
|
Game = BaseGame,
|
|
|
|
|
Profile = SelectedProfile,
|
|
|
|
|
UseGamePaths = true,
|
|
|
|
|
OutputFile = OutputLocation.TargetPath.Combine(SelectedProfile).WithExtension(Ext.Wabbajack),
|
2022-05-26 05:21:12 +00:00
|
|
|
|
AlwaysEnabled = AlwaysEnabled,
|
2022-05-30 20:50:24 +00:00
|
|
|
|
AdditionalProfiles = OtherProfiles,
|
2022-05-26 05:21:12 +00:00
|
|
|
|
NoMatchInclude = NoMatchInclude,
|
2022-03-13 22:47:30 +00:00
|
|
|
|
};
|
|
|
|
|
}
|
2022-05-25 02:59:15 +00:00
|
|
|
|
|
|
|
|
|
#region ListOps
|
|
|
|
|
|
|
|
|
|
public void AddOtherProfile(string profile)
|
|
|
|
|
{
|
|
|
|
|
OtherProfiles = (OtherProfiles ?? Array.Empty<string>()).Append(profile).Distinct().ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveProfile(string profile)
|
|
|
|
|
{
|
|
|
|
|
OtherProfiles = OtherProfiles.Where(p => p != profile).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddAlwaysEnabled(RelativePath path)
|
|
|
|
|
{
|
|
|
|
|
AlwaysEnabled = (AlwaysEnabled ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveAlwaysEnabled(RelativePath path)
|
|
|
|
|
{
|
|
|
|
|
AlwaysEnabled = AlwaysEnabled.Where(p => p != path).ToArray();
|
|
|
|
|
}
|
2022-05-26 05:21:12 +00:00
|
|
|
|
|
|
|
|
|
public void AddNoMatchInclude(RelativePath path)
|
|
|
|
|
{
|
|
|
|
|
NoMatchInclude = (NoMatchInclude ?? Array.Empty<RelativePath>()).Append(path).Distinct().ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RemoveNoMatchInclude(RelativePath path)
|
|
|
|
|
{
|
|
|
|
|
NoMatchInclude = NoMatchInclude.Where(p => p != path).ToArray();
|
|
|
|
|
}
|
2022-05-25 02:59:15 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
2022-03-13 22:47:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|