wabbajack/Wabbajack.App/Screens/CompilerConfigurationViewModel.cs
2021-11-05 16:58:08 -06:00

239 lines
8.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Text.Json;
using System.Threading.Tasks;
using Avalonia.Controls.Mixins;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Wabbajack.App.Messages;
using Wabbajack.App.Models;
using Wabbajack.App.ViewModels;
using Wabbajack.Common;
using Wabbajack.Compiler;
using Wabbajack.DTOs;
using Wabbajack.DTOs.JsonConverters;
using Wabbajack.Installer;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
using Consts = Wabbajack.Compiler.Consts;
namespace Wabbajack.App.Screens;
public class CompilerConfigurationViewModel : ViewModelBase
{
private readonly DTOSerializer _dtos;
private readonly SettingsManager _settingsManager;
public CompilerConfigurationViewModel(DTOSerializer dtos, SettingsManager settingsManager)
{
_settingsManager = settingsManager;
_dtos = dtos;
Activator = new ViewModelActivator();
AllGames = GameRegistry.Games.Values.ToArray();
StartCompilation = ReactiveCommand.Create(() => BeginCompilation().FireAndForget());
OutputFolder = KnownFolders.EntryPoint;
this.WhenActivated(disposables =>
{
LoadLastCompilation().FireAndForget();
this.WhenAnyValue(v => v.SettingsFile)
.Subscribe(location => { LoadNewSettingsFile(location).FireAndForget(); })
.DisposeWith(disposables);
});
}
[Reactive] public string Title { get; set; }
[Reactive] public AbsolutePath SettingsFile { get; set; }
[Reactive] public AbsolutePath Downloads { get; set; }
[Reactive] public GameMetaData BaseGame { get; set; }
[Reactive] public AbsolutePath Source { get; set; }
[Reactive] public AbsolutePath GamePath { get; set; }
[Reactive] public string SelectedProfile { get; set; }
[Reactive] public AbsolutePath OutputFolder { get; set; }
[Reactive] public IEnumerable<GameMetaData> AllGames { get; set; }
[Reactive] public ReactiveCommand<Unit, Unit> StartCompilation { get; set; }
[Reactive] public IEnumerable<RelativePath> AlwaysEnabled { get; set; } = Array.Empty<RelativePath>();
[Reactive]
public string[] OtherProfiles { get; set; } = Array.Empty<string>();
public AbsolutePath SettingsOutputLocation => Source.Combine(Title)
.WithExtension(IsMO2Compilation ? Ext.MO2CompilerSettings : Ext.CompilerSettings);
[Reactive] public bool IsMO2Compilation { get; set; }
private async Task LoadNewSettingsFile(AbsolutePath location)
{
if (location == default) return;
if (location.FileExists()) await LoadSettings(location);
}
private async Task LoadLastCompilation()
{
var location = await _settingsManager.Load<AbsolutePath>("last_compilation");
SettingsFile = location;
}
private async Task BeginCompilation()
{
var settings = GetSettings();
await SaveSettingsFile();
await _settingsManager.Save("last_compilation", SettingsOutputLocation);
MessageBus.Current.SendMessage(new StartCompilation(settings));
MessageBus.Current.SendMessage(new NavigateTo(typeof(CompilationViewModel)));
}
private CompilerSettings GetSettings()
{
return new MO2CompilerSettings
{
ModListName = Title,
Downloads = Downloads,
Source = Source,
Game = BaseGame.Game,
Profile = SelectedProfile,
UseGamePaths = true,
OutputFile = OutputFolder.Combine(SelectedProfile).WithExtension(Ext.Wabbajack),
AlwaysEnabled = AlwaysEnabled.ToArray(),
OtherProfiles = OtherProfiles.ToArray()
};
}
public bool AddOtherProfile(AbsolutePath path)
{
if (!path.InFolder(Source.Combine(Consts.MO2Profiles))) return false;
var relative = path.RelativeTo(Source.Combine(Consts.MO2Profiles)).ToString();
OtherProfiles = OtherProfiles.Append(relative).Distinct().ToArray();
return true;
}
public void RemoveOtherProfile(string profile)
{
OtherProfiles = OtherProfiles.Where(p => p != profile).ToArray();
}
public bool AddAlwaysExcluded(AbsolutePath path)
{
if (!path.InFolder(Source)) return false;
var relative = path.RelativeTo(Source);
AlwaysEnabled = AlwaysEnabled.Append(relative).Distinct().ToArray();
return true;
}
public void RemoveAlwaysExcluded(RelativePath path)
{
AlwaysEnabled = AlwaysEnabled.Where(p => p != path).ToArray();
}
public async Task InferSettingsFromModlistTxt(AbsolutePath settingsFile)
{
if (settingsFile.FileName == "modlist.txt".ToRelativePath() && settingsFile.Depth > 3)
{
var mo2Folder = settingsFile.Parent.Parent.Parent;
var mo2Ini = mo2Folder.Combine(Consts.MO2IniName);
if (mo2Ini.FileExists())
{
var iniData = mo2Ini.LoadIniFile();
var general = iniData["General"];
BaseGame = GameRegistry.GetByFuzzyName(general["gameName"].FromMO2Ini());
Source = mo2Folder;
SelectedProfile = general["selected_profile"].FromMO2Ini();
GamePath = general["gamePath"].FromMO2Ini().ToAbsolutePath();
Title = SelectedProfile;
var settings = iniData["Settings"];
Downloads = settings["download_directory"].FromMO2Ini().ToAbsolutePath();
IsMO2Compilation = true;
AlwaysEnabled = Array.Empty<RelativePath>();
// Find Always Enabled mods
foreach (var modFolder in mo2Folder.Combine("mods").EnumerateDirectories())
{
var iniFile = modFolder.Combine("meta.ini");
if (!iniFile.FileExists()) continue;
var data = iniFile.LoadIniFile();
var generalModData = data["General"];
if ((generalModData["notes"]?.Contains("WABBAJACK_ALWAYS_ENABLE") ?? false) ||
(generalModData["comments"]?.Contains("WABBAJACK_ALWAYS_ENABLE") ?? false))
AlwaysEnabled = AlwaysEnabled.Append(modFolder.RelativeTo(mo2Folder)).ToArray();
}
var otherProfilesFile = settingsFile.Parent.Combine("otherprofiles.txt");
if (otherProfilesFile.FileExists())
{
OtherProfiles = await otherProfilesFile.ReadAllLinesAsync().ToArray();
}
if (mo2Folder.Depth > 1)
OutputFolder = mo2Folder.Parent;
await SaveSettingsFile();
SettingsFile = SettingsOutputLocation;
}
}
}
private async Task SaveSettingsFile()
{
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);
}
private async Task LoadSettings(AbsolutePath path)
{
CompilerSettings s;
if (path.Extension == Ext.MO2CompilerSettings)
{
var mo2 = await LoadSettingsFile<MO2CompilerSettings>(path);
AlwaysEnabled = mo2.AlwaysEnabled;
OtherProfiles = mo2.OtherProfiles;
SelectedProfile = mo2.Profile;
s = mo2;
}
else
{
throw new NotImplementedException();
}
Title = s.ModListName;
Source = s.Source;
Downloads = s.Downloads;
OutputFolder = s.OutputFile.Depth > 1 ? s.OutputFile.Parent : s.OutputFile;
BaseGame = s.Game.MetaData();
}
private async Task<T> LoadSettingsFile<T>(AbsolutePath path)
{
await using var st = path.Open(FileMode.Open);
return (await JsonSerializer.DeserializeAsync<T>(st, _dtos.Options))!;
}
}