mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Merge remote-tracking branch 'origin/master' into compiler-update-streams
This commit is contained in:
commit
d92d05dbf8
30
Wabbajack.Common/Extensions/EnumExt.cs
Normal file
30
Wabbajack.Common/Extensions/EnumExt.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public static class EnumExt
|
||||
{
|
||||
public static IEnumerable<T> GetValues<T>()
|
||||
where T : struct, Enum
|
||||
{
|
||||
return Enum.GetValues(typeof(T)).Cast<T>();
|
||||
}
|
||||
|
||||
public static string ToDescriptionString<TEnum>(this TEnum val)
|
||||
where TEnum : struct, IConvertible
|
||||
{
|
||||
if (!typeof(TEnum).IsEnum)
|
||||
{
|
||||
throw new ArgumentException("T must be an Enum");
|
||||
}
|
||||
|
||||
DescriptionAttribute[] attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
@ -9,6 +10,7 @@ namespace Wabbajack.Common
|
||||
public int GameID;
|
||||
public string Path;
|
||||
public string GameName;
|
||||
public Game? Game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -16,6 +18,11 @@ namespace Wabbajack.Common
|
||||
/// </summary>
|
||||
public class GOGHandler
|
||||
{
|
||||
private static readonly Lazy<GOGHandler> instance = new Lazy<GOGHandler>(
|
||||
() => new GOGHandler(true),
|
||||
isThreadSafe: true);
|
||||
public static GOGHandler Instance => instance.Value;
|
||||
|
||||
private const string GOGRegKey = @"Software\GOG.com\Games";
|
||||
private const string GOG64RegKey = @"Software\WOW6432Node\GOG.com\Games";
|
||||
|
||||
@ -47,6 +54,7 @@ namespace Wabbajack.Common
|
||||
public void LoadAllGames()
|
||||
{
|
||||
Games = new HashSet<GOGGame>();
|
||||
if (this.GOGKey == null) return;
|
||||
string[] keys = GOGKey.GetSubKeyNames();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
@ -57,6 +65,9 @@ namespace Wabbajack.Common
|
||||
Path = GOGKey.OpenSubKey(key)?.GetValue("PATH").ToString()
|
||||
};
|
||||
|
||||
game.Game = GameRegistry.Games.Values
|
||||
.FirstOrDefault(g => g.GOGIDs.Contains(game.GameID))?.Game;
|
||||
|
||||
Games.Add(game);
|
||||
}
|
||||
}
|
||||
|
@ -1,29 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Alphaleonis.Win32.Filesystem;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
public enum Game {
|
||||
public enum Game
|
||||
{
|
||||
//MO2 GAMES
|
||||
Morrowind,
|
||||
Oblivion,
|
||||
[Description("Fallout 3")]
|
||||
Fallout3,
|
||||
[Description("Fallout New Vegas")]
|
||||
FalloutNewVegas,
|
||||
Skyrim,
|
||||
[Description("Skyrim Special Edition")]
|
||||
SkyrimSpecialEdition,
|
||||
[Description("Fallout 4")]
|
||||
Fallout4,
|
||||
[Description("Skyrim VR")]
|
||||
SkyrimVR,
|
||||
//VORTEX GAMES
|
||||
[Description("Darkest Dungeon")]
|
||||
DarkestDungeon,
|
||||
[Description("Divinity Original Sin 2")]
|
||||
DivinityOriginalSin2,
|
||||
[Description("Divinity Original Sin 2 Definitive Edition")]
|
||||
DivinityOriginalSin2DE, //definitive edition has its own nexus page but same Steam/GOG ids
|
||||
Starbound,
|
||||
[Description("Star Wars: Knights of the Old Republic")]
|
||||
SWKOTOR,
|
||||
[Description("Star Wars: Knights of the Old Republic 2")]
|
||||
SWKOTOR2,
|
||||
Witcher,
|
||||
[Description("Witcher 2")]
|
||||
Witcher2,
|
||||
[Description("Witcher 3")]
|
||||
Witcher3
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -6,11 +8,13 @@ using Microsoft.Win32;
|
||||
|
||||
namespace Wabbajack.Common
|
||||
{
|
||||
[DebuggerDisplay("{Name}")]
|
||||
public class SteamGame
|
||||
{
|
||||
public int AppId;
|
||||
public string Name;
|
||||
public string InstallDir;
|
||||
public Game? Game;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -18,6 +22,11 @@ namespace Wabbajack.Common
|
||||
/// </summary>
|
||||
public class SteamHandler
|
||||
{
|
||||
private static readonly Lazy<SteamHandler> instance = new Lazy<SteamHandler>(
|
||||
() => new SteamHandler(true),
|
||||
isThreadSafe: true);
|
||||
public static SteamHandler Instance => instance.Value;
|
||||
|
||||
private const string SteamRegKey = @"Software\Valve\Steam";
|
||||
|
||||
/// <summary>
|
||||
@ -95,6 +104,8 @@ namespace Wabbajack.Common
|
||||
steamGame.InstallDir = Path.Combine(p, "common", GetVdfValue(l));
|
||||
});
|
||||
|
||||
steamGame.Game = GameRegistry.Games.Values
|
||||
.FirstOrDefault(g => g.SteamIDs.Contains(steamGame.AppId))?.Game;
|
||||
games.Add(steamGame);
|
||||
});
|
||||
});
|
||||
|
@ -98,6 +98,7 @@
|
||||
<Compile Include="Enums\RunMode.cs" />
|
||||
<Compile Include="ExtensionManager.cs" />
|
||||
<Compile Include="Extensions\DictionaryExt.cs" />
|
||||
<Compile Include="Extensions\EnumExt.cs" />
|
||||
<Compile Include="Extensions\HashHelper.cs" />
|
||||
<Compile Include="Extensions\RxExt.cs" />
|
||||
<Compile Include="Extensions\TaskExt.cs" />
|
||||
|
@ -27,29 +27,21 @@ namespace Wabbajack.Lib
|
||||
|
||||
public bool IgnoreMissingFiles { get; set; }
|
||||
|
||||
public VortexCompiler(string gameName, string gamePath)
|
||||
public const string StagingMarkerName = "__vortex_staging_folder";
|
||||
public const string DownloadMarkerName = "__vortex_downloads_folder";
|
||||
|
||||
public VortexCompiler(Game game, string gamePath, string vortexFolder, string downloadsFolder, string stagingFolder)
|
||||
{
|
||||
ModManager = ModManager.Vortex;
|
||||
|
||||
// TODO: only for testing
|
||||
IgnoreMissingFiles = true;
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
|
||||
GamePath = gamePath;
|
||||
GameName = gameName;
|
||||
Game = GameRegistry.GetByNexusName(GameName).Game;
|
||||
|
||||
//args: wabbajacke.exe gameName gamePath vortexfolder stagingfolder downloadsfolder
|
||||
VortexFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vortex");
|
||||
if (File.Exists(Path.Combine(VortexFolder, gameName, "mods", "__vortex_staging_folder")))
|
||||
StagingFolder = Path.Combine(VortexFolder, gameName, "mods");
|
||||
if (File.Exists(Path.Combine(VortexFolder, "downloads", "__vortex_downloads_folder")))
|
||||
DownloadsFolder = Path.Combine(VortexFolder, "downloads", gameName);
|
||||
|
||||
if (args.Length >= 4)
|
||||
StagingFolder = args[3];
|
||||
if (args.Length == 5)
|
||||
DownloadsFolder = args[4];
|
||||
GameName = GameRegistry.Games[game].NexusName;
|
||||
this.VortexFolder = vortexFolder;
|
||||
this.DownloadsFolder = downloadsFolder;
|
||||
this.StagingFolder = stagingFolder;
|
||||
|
||||
ModListOutputFolder = "output_folder";
|
||||
|
||||
@ -110,7 +102,7 @@ namespace Wabbajack.Lib
|
||||
Directory.CreateDirectory(ModListOutputFolder);
|
||||
|
||||
IEnumerable<RawSourceFile> vortexStagingFiles = Directory.EnumerateFiles(StagingFolder, "*", SearchOption.AllDirectories)
|
||||
.Where(p => p.FileExists() && p != "__vortex_staging_folder")
|
||||
.Where(p => p.FileExists() && p != StagingMarkerName)
|
||||
.Select(p => new RawSourceFile(VFS.Index.ByRootPath[p])
|
||||
{Path = p.RelativeTo(StagingFolder)});
|
||||
|
||||
@ -423,8 +415,8 @@ namespace Wabbajack.Lib
|
||||
|
||||
Game == Game.DarkestDungeon ? new IncludeRegex(this, "project\\.xml$") : null,
|
||||
|
||||
new IgnoreStartsWith(this, " __vortex_staging_folder"),
|
||||
new IgnoreEndsWith(this, "__vortex_staging_folder"),
|
||||
new IgnoreStartsWith(this, StagingFolder),
|
||||
new IgnoreEndsWith(this, StagingFolder),
|
||||
|
||||
new IgnoreGameFiles(this),
|
||||
|
||||
@ -437,5 +429,47 @@ namespace Wabbajack.Lib
|
||||
new DropAll(this)
|
||||
};
|
||||
}
|
||||
|
||||
public static string TypicalVortexFolder()
|
||||
{
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vortex");
|
||||
}
|
||||
|
||||
public static string RetrieveDownloadLocation(Game game, string vortexFolderPath = null)
|
||||
{
|
||||
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
||||
return Path.Combine(vortexFolderPath, "downloads", GameRegistry.Games[game].NexusName);
|
||||
}
|
||||
|
||||
public static string RetrieveStagingLocation(Game game, string vortexFolderPath = null)
|
||||
{
|
||||
vortexFolderPath = vortexFolderPath ?? TypicalVortexFolder();
|
||||
var gameName = GameRegistry.Games[game].NexusName;
|
||||
return Path.Combine(vortexFolderPath, gameName, "mods");
|
||||
}
|
||||
|
||||
public static IErrorResponse IsValidBaseDownloadsFolder(string path)
|
||||
{
|
||||
if (!Directory.Exists(path)) return ErrorResponse.Fail($"Path does not exist: {path}");
|
||||
if (Directory.EnumerateFiles(path, DownloadMarkerName, SearchOption.TopDirectoryOnly).Any()) return ErrorResponse.Success;
|
||||
return ErrorResponse.Fail($"Folder must contain {DownloadMarkerName} file");
|
||||
}
|
||||
|
||||
public static IErrorResponse IsValidDownloadsFolder(string path)
|
||||
{
|
||||
return IsValidBaseDownloadsFolder(Path.GetDirectoryName(path));
|
||||
}
|
||||
|
||||
public static IErrorResponse IsValidBaseStagingFolder(string path)
|
||||
{
|
||||
if (!Directory.Exists(path)) return ErrorResponse.Fail($"Path does not exist: {path}");
|
||||
if (Directory.EnumerateFiles(path, StagingMarkerName, SearchOption.TopDirectoryOnly).Any()) return ErrorResponse.Success;
|
||||
return ErrorResponse.Fail($"Folder must contain {StagingMarkerName} file");
|
||||
}
|
||||
|
||||
public static IErrorResponse IsValidStagingFolder(string path)
|
||||
{
|
||||
return IsValidBaseStagingFolder(Path.GetDirectoryName(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace Wabbajack.Test
|
||||
Consts.TestMode = true;
|
||||
|
||||
utils = new TestUtils();
|
||||
utils.GameName = "Skyrim Special Edition";
|
||||
utils.Game = Game.SkyrimSpecialEdition;
|
||||
|
||||
Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f));
|
||||
|
||||
|
@ -19,7 +19,7 @@ namespace Wabbajack.Test
|
||||
|
||||
utils = new TestUtils
|
||||
{
|
||||
GameName = "darkestdungeon"
|
||||
Game = Game.DarkestDungeon
|
||||
};
|
||||
|
||||
Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f));
|
||||
@ -43,8 +43,12 @@ namespace Wabbajack.Test
|
||||
|
||||
protected VortexCompiler MakeCompiler()
|
||||
{
|
||||
var vortexCompiler = new VortexCompiler(utils.GameName, utils.GameFolder);
|
||||
return vortexCompiler;
|
||||
return new VortexCompiler(
|
||||
game: utils.Game,
|
||||
gamePath: utils.GameFolder,
|
||||
vortexFolder: VortexCompiler.TypicalVortexFolder(),
|
||||
downloadsFolder: VortexCompiler.RetrieveDownloadLocation(utils.Game),
|
||||
stagingFolder: VortexCompiler.RetrieveStagingLocation(utils.Game));
|
||||
}
|
||||
|
||||
protected ModList CompileAndInstall()
|
||||
|
@ -30,7 +30,7 @@ namespace Wabbajack.Test
|
||||
Consts.TestMode = true;
|
||||
|
||||
utils = new TestUtils();
|
||||
utils.GameName = "Skyrim Special Edition";
|
||||
utils.Game = Game.SkyrimSpecialEdition;
|
||||
|
||||
Utils.LogMessages.Subscribe(f => TestContext.WriteLine(f));
|
||||
|
||||
|
@ -19,7 +19,6 @@ namespace Wabbajack.Test
|
||||
{
|
||||
public class TestUtils : IDisposable
|
||||
{
|
||||
|
||||
public TestUtils()
|
||||
{
|
||||
RNG = new Random();
|
||||
@ -31,7 +30,7 @@ namespace Wabbajack.Test
|
||||
public string ID { get; }
|
||||
public Random RNG { get; }
|
||||
|
||||
public string GameName { get; set; }
|
||||
public Game Game { get; set; }
|
||||
|
||||
public string TestFolder => Path.Combine(WorkingDirectory, ID);
|
||||
public string GameFolder => Path.Combine(WorkingDirectory, ID, "game_folder");
|
||||
@ -42,13 +41,16 @@ namespace Wabbajack.Test
|
||||
|
||||
public string InstallFolder => Path.Combine(TestFolder, "installed");
|
||||
|
||||
public HashSet<string> Profiles = new HashSet<string>();
|
||||
|
||||
public List<string> Mods = new List<string>();
|
||||
|
||||
public void Configure()
|
||||
{
|
||||
File.WriteAllLines(Path.Combine(MO2Folder, "ModOrganizer.ini"), new []
|
||||
{
|
||||
"[General]",
|
||||
$"gameName={GameName}",
|
||||
$"gameName={GameRegistry.Games[this.Game].MO2Name}",
|
||||
$"gamePath={GameFolder.Replace("\\", "\\\\")}",
|
||||
$"download_directory={DownloadsFolder}"
|
||||
});
|
||||
@ -70,7 +72,6 @@ namespace Wabbajack.Test
|
||||
Profiles.Add(profile_name);
|
||||
return profile_name;
|
||||
}
|
||||
public HashSet<string> Profiles = new HashSet<string>();
|
||||
|
||||
public string AddMod(string name = null)
|
||||
{
|
||||
@ -82,8 +83,6 @@ namespace Wabbajack.Test
|
||||
return mod_name;
|
||||
}
|
||||
|
||||
public List<string> Mods = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a file to the given mod with a given path in the mod. Fills it with random data unless
|
||||
/// random_fill == 0;
|
||||
@ -129,11 +128,9 @@ namespace Wabbajack.Test
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random string name (with spaces)
|
||||
/// </summary>
|
||||
private char[] _nameChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'+=-_ ".ToCharArray();
|
||||
public string RandomName()
|
||||
{
|
||||
return Guid.NewGuid().ToString();
|
||||
@ -210,16 +207,12 @@ namespace Wabbajack.Test
|
||||
var fi_src = new FileInfo(src_file);
|
||||
var fi_dest = new FileInfo(dest_file);
|
||||
|
||||
|
||||
|
||||
if (!skip_extensions.Contains(Path.GetExtension(src_file)))
|
||||
{
|
||||
Assert.AreEqual(fi_src.Length, fi_dest.Length, $"Differing sizes {rel_file}");
|
||||
Assert.AreEqual(src_file.FileHash(), dest_file.FileHash(), $"Differing content hash {rel_file}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
Wabbajack/Resources/Icons/gog.png
Normal file
BIN
Wabbajack/Resources/Icons/gog.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 118 KiB |
BIN
Wabbajack/Resources/Icons/steam.png
Normal file
BIN
Wabbajack/Resources/Icons/steam.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -58,7 +58,6 @@ namespace Wabbajack
|
||||
public class ModlistInstallationSettings
|
||||
{
|
||||
public string InstallationLocation { get; set; }
|
||||
public string StagingLocation { get; set; }
|
||||
public string DownloadLocation { get; set; }
|
||||
}
|
||||
|
||||
@ -88,8 +87,15 @@ namespace Wabbajack
|
||||
|
||||
public class VortexCompilationSettings
|
||||
{
|
||||
public string StagingLocation { get; set; }
|
||||
public Game LastCompiledGame { get; set; }
|
||||
public Dictionary<Game, CompilationModlistSettings> ModlistSettings { get; } = new Dictionary<Game, CompilationModlistSettings>();
|
||||
public Dictionary<Game, VortexGameSettings> ModlistSettings { get; } = new Dictionary<Game, VortexGameSettings>();
|
||||
}
|
||||
|
||||
public class VortexGameSettings
|
||||
{
|
||||
public string GameLocation { get; set; }
|
||||
public string DownloadLocation { get; set; }
|
||||
public string StagingLocation { get; set; }
|
||||
public CompilationModlistSettings ModlistSettings { get; } = new CompilationModlistSettings();
|
||||
}
|
||||
}
|
||||
|
@ -54,11 +54,18 @@ namespace Wabbajack
|
||||
case ModManager.MO2:
|
||||
return new MO2CompilerVM(this);
|
||||
case ModManager.Vortex:
|
||||
return new VortexCompilerVM();
|
||||
return new VortexCompilerVM(this);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})
|
||||
// Unload old VM
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
pair.Previous?.Unload();
|
||||
})
|
||||
.Select(p => p.Current)
|
||||
.ToProperty(this, nameof(this.Compiler));
|
||||
|
||||
// Let sub VM determine what settings we're displaying and when
|
||||
@ -71,7 +78,7 @@ namespace Wabbajack
|
||||
.DistinctUntilChanged()
|
||||
.Select(path =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path)) return UIUtils.BitmapImageFromResource("Wabbajack.Resources.Banner_Dark.png");
|
||||
if (string.IsNullOrWhiteSpace(path)) return UIUtils.BitmapImageFromResource("Wabbajack.Resources.Wabba_Mouth.png");
|
||||
if (UIUtils.TryGetBitmapImageFromFile(path, out var image))
|
||||
{
|
||||
return image;
|
||||
|
@ -13,5 +13,6 @@ namespace Wabbajack
|
||||
IReactiveCommand BeginCommand { get; }
|
||||
bool Compiling { get; }
|
||||
ModlistSettingsEditorVM ModlistSettings { get; }
|
||||
void Unload();
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ namespace Wabbajack
|
||||
{
|
||||
public class MO2CompilerVM : ViewModel, ISubCompilerVM
|
||||
{
|
||||
private readonly MO2CompilationSettings settings;
|
||||
|
||||
private readonly ObservableAsPropertyHelper<string> _Mo2Folder;
|
||||
public string Mo2Folder => _Mo2Folder.Value;
|
||||
|
||||
@ -139,19 +141,14 @@ namespace Wabbajack
|
||||
.ToProperty(this, nameof(this.Compiling));
|
||||
|
||||
// Load settings
|
||||
var settings = parent.MWVM.Settings.Compiler.MO2Compilation;
|
||||
this.settings = parent.MWVM.Settings.Compiler.MO2Compilation;
|
||||
this.ModlistLocation.TargetPath = settings.LastCompiledProfileLocation;
|
||||
if (!string.IsNullOrWhiteSpace(settings.DownloadLocation))
|
||||
{
|
||||
this.DownloadLocation.TargetPath = settings.DownloadLocation;
|
||||
}
|
||||
parent.MWVM.Settings.SaveSignal
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
settings.DownloadLocation = this.DownloadLocation.TargetPath;
|
||||
settings.LastCompiledProfileLocation = this.ModlistLocation.TargetPath;
|
||||
this.ModlistSettings?.Save();
|
||||
})
|
||||
.Subscribe(_ => Unload())
|
||||
.DisposeWith(this.CompositeDisposable);
|
||||
|
||||
// Load custom modlist settings per MO2 profile
|
||||
@ -165,7 +162,10 @@ namespace Wabbajack
|
||||
{
|
||||
if (u.State.Failed) return null;
|
||||
var modlistSettings = settings.ModlistSettings.TryCreate(u.Path);
|
||||
return new ModlistSettingsEditorVM(modlistSettings, this.MOProfile);
|
||||
return new ModlistSettingsEditorVM(modlistSettings)
|
||||
{
|
||||
ModListName = this.MOProfile
|
||||
};
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
@ -199,8 +199,13 @@ namespace Wabbajack
|
||||
}
|
||||
})
|
||||
.DisposeWith(this.CompositeDisposable);
|
||||
}
|
||||
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
settings.DownloadLocation = this.DownloadLocation.TargetPath;
|
||||
settings.LastCompiledProfileLocation = this.ModlistLocation.TargetPath;
|
||||
this.ModlistSettings?.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ namespace Wabbajack
|
||||
public class ModlistSettingsEditorVM : ViewModel
|
||||
{
|
||||
private CompilationModlistSettings settings;
|
||||
private string mo2Profile;
|
||||
|
||||
[Reactive]
|
||||
public string ModListName { get; set; }
|
||||
@ -32,10 +31,9 @@ namespace Wabbajack
|
||||
[Reactive]
|
||||
public string Website { get; set; }
|
||||
|
||||
public ModlistSettingsEditorVM(CompilationModlistSettings settings, string mo2Profile)
|
||||
public ModlistSettingsEditorVM(CompilationModlistSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
this.mo2Profile = mo2Profile;
|
||||
this.ImagePath = new FilePickerVM()
|
||||
{
|
||||
DoExistsCheck = false,
|
||||
@ -48,19 +46,14 @@ namespace Wabbajack
|
||||
this.ReadMeText = new FilePickerVM()
|
||||
{
|
||||
PathType = FilePickerVM.PathTypeOptions.File,
|
||||
DoExistsCheck = true,
|
||||
DoExistsCheck = false,
|
||||
};
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
this.AuthorText = settings.Author;
|
||||
if (string.IsNullOrWhiteSpace(settings.ModListName))
|
||||
{
|
||||
// Set ModlistName initially off just the MO2Profile
|
||||
this.ModListName = mo2Profile;
|
||||
}
|
||||
else
|
||||
if (!string.IsNullOrWhiteSpace(settings.ModListName))
|
||||
{
|
||||
this.ModListName = settings.ModListName;
|
||||
}
|
||||
|
@ -1,19 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Wabbajack.Common;
|
||||
using Wabbajack.Lib;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class VortexCompilerVM : ViewModel, ISubCompilerVM
|
||||
{
|
||||
public IReactiveCommand BeginCommand => throw new NotImplementedException();
|
||||
private readonly VortexCompilationSettings settings;
|
||||
|
||||
public bool Compiling => throw new NotImplementedException();
|
||||
public IReactiveCommand BeginCommand { get; }
|
||||
|
||||
public ModlistSettingsEditorVM ModlistSettings => throw new NotImplementedException();
|
||||
private readonly ObservableAsPropertyHelper<bool> _Compiling;
|
||||
public bool Compiling => _Compiling.Value;
|
||||
|
||||
private readonly ObservableAsPropertyHelper<ModlistSettingsEditorVM> _ModlistSettings;
|
||||
public ModlistSettingsEditorVM ModlistSettings => _ModlistSettings.Value;
|
||||
|
||||
private static ObservableCollectionExtended<GameVM> gameOptions = new ObservableCollectionExtended<GameVM>(
|
||||
EnumExt.GetValues<Game>()
|
||||
.Select(g => new GameVM(g))
|
||||
.OrderBy(g => g.DisplayName));
|
||||
|
||||
public ObservableCollectionExtended<GameVM> GameOptions => gameOptions;
|
||||
|
||||
[Reactive]
|
||||
public GameVM SelectedGame { get; set; } = gameOptions.First(x => x.Game == Game.SkyrimSpecialEdition);
|
||||
|
||||
[Reactive]
|
||||
public FilePickerVM GameLocation { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public FilePickerVM DownloadsLocation { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public FilePickerVM StagingLocation { get; set; }
|
||||
|
||||
public ICommand FindGameInSteamCommand { get; }
|
||||
|
||||
public ICommand FindGameInGogCommand { get; }
|
||||
|
||||
public VortexCompilerVM(CompilerVM parent)
|
||||
{
|
||||
this.GameLocation = new FilePickerVM()
|
||||
{
|
||||
DoExistsCheck = true,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Game Folder Location"
|
||||
};
|
||||
this.DownloadsLocation = new FilePickerVM()
|
||||
{
|
||||
DoExistsCheck = true,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Downloads Folder"
|
||||
};
|
||||
this.StagingLocation = new FilePickerVM()
|
||||
{
|
||||
DoExistsCheck = true,
|
||||
PathType = FilePickerVM.PathTypeOptions.Folder,
|
||||
PromptTitle = "Select Staging Folder"
|
||||
};
|
||||
|
||||
// Wire start command
|
||||
this.BeginCommand = ReactiveCommand.CreateFromTask(
|
||||
canExecute: Observable.CombineLatest(
|
||||
this.WhenAny(x => x.GameLocation.InError),
|
||||
this.WhenAny(x => x.DownloadsLocation.InError),
|
||||
this.WhenAny(x => x.StagingLocation.InError),
|
||||
resultSelector: (g, d, s) => !g && !d && !s)
|
||||
.ObserveOnGuiThread(),
|
||||
execute: async () =>
|
||||
{
|
||||
VortexCompiler compiler;
|
||||
try
|
||||
{
|
||||
compiler = new VortexCompiler(
|
||||
game: this.SelectedGame.Game,
|
||||
gamePath: this.GameLocation.TargetPath,
|
||||
vortexFolder: VortexCompiler.TypicalVortexFolder(),
|
||||
downloadsFolder: this.DownloadsLocation.TargetPath,
|
||||
stagingFolder: this.StagingLocation.TargetPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Compiler error: {ex.ExceptionToString()}");
|
||||
return;
|
||||
}
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
compiler.Compile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
while (ex.InnerException != null) ex = ex.InnerException;
|
||||
Utils.Log($"Compiler error: {ex.ExceptionToString()}");
|
||||
}
|
||||
});
|
||||
});
|
||||
this._Compiling = this.BeginCommand.IsExecuting
|
||||
.ToProperty(this, nameof(this.Compiling));
|
||||
|
||||
// Load settings
|
||||
this.settings = parent.MWVM.Settings.Compiler.VortexCompilation;
|
||||
this.SelectedGame = gameOptions.First(x => x.Game == settings.LastCompiledGame);
|
||||
parent.MWVM.Settings.SaveSignal
|
||||
.Subscribe(_ => Unload())
|
||||
.DisposeWith(this.CompositeDisposable);
|
||||
|
||||
// Load custom game settings when game type changes
|
||||
this.WhenAny(x => x.SelectedGame)
|
||||
.Select(game => settings.ModlistSettings.TryCreate(game.Game))
|
||||
.Pairwise()
|
||||
.Subscribe(pair =>
|
||||
{
|
||||
// Save old
|
||||
if (pair.Previous != null)
|
||||
{
|
||||
pair.Previous.GameLocation = this.GameLocation.TargetPath;
|
||||
}
|
||||
|
||||
// Load new
|
||||
this.GameLocation.TargetPath = pair.Current?.GameLocation ?? null;
|
||||
if (string.IsNullOrWhiteSpace(this.GameLocation.TargetPath))
|
||||
{
|
||||
this.SetGameToSteamLocation();
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(this.GameLocation.TargetPath))
|
||||
{
|
||||
this.SetGameToGogLocation();
|
||||
}
|
||||
this.DownloadsLocation.TargetPath = pair.Current?.DownloadLocation ?? null;
|
||||
if (string.IsNullOrWhiteSpace(this.DownloadsLocation.TargetPath))
|
||||
{
|
||||
this.DownloadsLocation.TargetPath = VortexCompiler.RetrieveDownloadLocation(this.SelectedGame.Game);
|
||||
}
|
||||
this.StagingLocation.TargetPath = pair.Current?.StagingLocation ?? null;
|
||||
if (string.IsNullOrWhiteSpace(this.StagingLocation.TargetPath))
|
||||
{
|
||||
this.StagingLocation.TargetPath = VortexCompiler.RetrieveStagingLocation(this.SelectedGame.Game);
|
||||
}
|
||||
})
|
||||
.DisposeWith(this.CompositeDisposable);
|
||||
|
||||
// Load custom modlist settings when game type changes
|
||||
this._ModlistSettings = this.WhenAny(x => x.SelectedGame)
|
||||
.Select(game =>
|
||||
{
|
||||
var gameSettings = settings.ModlistSettings.TryCreate(game.Game);
|
||||
return new ModlistSettingsEditorVM(gameSettings.ModlistSettings);
|
||||
})
|
||||
// Interject and save old while loading new
|
||||
.Pairwise()
|
||||
.Do(pair =>
|
||||
{
|
||||
pair.Previous?.Save();
|
||||
pair.Current?.Init();
|
||||
})
|
||||
.Select(x => x.Current)
|
||||
// Save to property
|
||||
.ObserveOnGuiThread()
|
||||
.ToProperty(this, nameof(this.ModlistSettings));
|
||||
|
||||
// Find game commands
|
||||
this.FindGameInSteamCommand = ReactiveCommand.Create(SetGameToSteamLocation);
|
||||
this.FindGameInGogCommand = ReactiveCommand.Create(SetGameToGogLocation);
|
||||
|
||||
// Add additional criteria to download/staging folders
|
||||
this.DownloadsLocation.AdditionalError = this.WhenAny(x => x.DownloadsLocation.TargetPath)
|
||||
.Select(path =>
|
||||
{
|
||||
if (path == null) return ErrorResponse.Success;
|
||||
return VortexCompiler.IsValidDownloadsFolder(path);
|
||||
});
|
||||
this.StagingLocation.AdditionalError = this.WhenAny(x => x.StagingLocation.TargetPath)
|
||||
.Select(path =>
|
||||
{
|
||||
if (path == null) return ErrorResponse.Success;
|
||||
return VortexCompiler.IsValidBaseStagingFolder(path);
|
||||
});
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
settings.LastCompiledGame = this.SelectedGame.Game;
|
||||
this.ModlistSettings?.Save();
|
||||
}
|
||||
|
||||
private void SetGameToSteamLocation()
|
||||
{
|
||||
var steamGame = SteamHandler.Instance.Games.FirstOrDefault(g => g.Game.HasValue && g.Game == this.SelectedGame.Game);
|
||||
this.GameLocation.TargetPath = steamGame?.InstallDir;
|
||||
}
|
||||
|
||||
private void SetGameToGogLocation()
|
||||
{
|
||||
var gogGame = GOGHandler.Instance.Games.FirstOrDefault(g => g.Game.HasValue && g.Game == this.SelectedGame.Game);
|
||||
this.GameLocation.TargetPath = gogGame?.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
Wabbajack/View Models/GameVM.cs
Normal file
25
Wabbajack/View Models/GameVM.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Wabbajack.Common;
|
||||
|
||||
namespace Wabbajack
|
||||
{
|
||||
public class GameVM
|
||||
{
|
||||
public Game Game { get; }
|
||||
public string DisplayName { get; }
|
||||
|
||||
public GameVM(Game game)
|
||||
{
|
||||
this.Game = game;
|
||||
this.DisplayName = game.ToDescriptionString();
|
||||
if (string.IsNullOrWhiteSpace(this.DisplayName))
|
||||
{
|
||||
this.DisplayName = game.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -198,6 +198,9 @@
|
||||
<DataTemplate DataType="{x:Type local:MO2CompilerVM}">
|
||||
<local:MO2CompilerConfigView />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type local:VortexCompilerVM}">
|
||||
<local:VortexCompilerConfigView />
|
||||
</DataTemplate>
|
||||
</ContentPresenter.Resources>
|
||||
</ContentPresenter>
|
||||
<local:BeginButton
|
||||
|
132
Wabbajack/Views/Compilers/VortexCompilerConfigView.xaml
Normal file
132
Wabbajack/Views/Compilers/VortexCompilerConfigView.xaml
Normal file
@ -0,0 +1,132 @@
|
||||
<UserControl
|
||||
x:Class="Wabbajack.VortexCompilerConfigView"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Wabbajack"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DataContext="{d:DesignInstance local:VortexCompilerVM}"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="30" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="28" />
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="40" />
|
||||
<RowDefinition Height="28" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Game"
|
||||
TextAlignment="Center"
|
||||
ToolTip="The game you wish to target" />
|
||||
<ComboBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
FontSize="14"
|
||||
ItemsSource="{Binding GameOptions}"
|
||||
SelectedValue="{Binding SelectedGame}"
|
||||
ToolTip="The game you wish to target">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Game Folder"
|
||||
TextAlignment="Center"
|
||||
ToolTip="The install folder for the game" />
|
||||
<local:FilePicker
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding GameLocation}"
|
||||
FontSize="14"
|
||||
ToolTip="The install folder for the game" />
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Height="28"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
Margin="0,0,5,0"
|
||||
Background="Transparent"
|
||||
Command="{Binding FindGameInSteamCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}"
|
||||
ToolTip="Attempt to locate the game in Steam">
|
||||
<Image Margin="1" Source="../../Resources/Icons/steam.png" />
|
||||
</Button>
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Background="Transparent"
|
||||
Command="{Binding FindGameInGogCommand}"
|
||||
Style="{StaticResource CircleButtonStyle}"
|
||||
ToolTip="Attempt to locate game in GoG">
|
||||
<Image Margin="1" Source="../../Resources/Icons/gog.png" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Download Location"
|
||||
TextAlignment="Center"
|
||||
ToolTip="The folder to downloads your mods" />
|
||||
<local:FilePicker
|
||||
Grid.Row="1"
|
||||
Grid.Column="6"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding DownloadsLocation}"
|
||||
FontSize="14"
|
||||
ToolTip="The folder to downloads your mods" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="Staging Location"
|
||||
TextAlignment="Center" />
|
||||
<local:FilePicker
|
||||
Grid.Row="2"
|
||||
Grid.Column="6"
|
||||
Height="30"
|
||||
VerticalAlignment="Center"
|
||||
DataContext="{Binding StagingLocation}"
|
||||
FontSize="14" />
|
||||
</Grid>
|
||||
</UserControl>
|
28
Wabbajack/Views/Compilers/VortexCompilerConfigView.xaml.cs
Normal file
28
Wabbajack/Views/Compilers/VortexCompilerConfigView.xaml.cs
Normal file
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for VortexCompilerConfigView.xaml
|
||||
/// </summary>
|
||||
public partial class VortexCompilerConfigView : UserControl
|
||||
{
|
||||
public VortexCompilerConfigView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
@ -162,6 +162,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Converters\EqualsToBoolConverter.cs" />
|
||||
<Compile Include="View Models\GameVM.cs" />
|
||||
<Compile Include="Views\Compilers\MO2CompilerConfigView.xaml.cs">
|
||||
<DependentUpon>MO2CompilerConfigView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@ -226,6 +227,9 @@
|
||||
<DependentUpon>TextViewer.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\UserControlRx.cs" />
|
||||
<Compile Include="Views\Compilers\VortexCompilerConfigView.xaml.cs">
|
||||
<DependentUpon>VortexCompilerConfigView.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="Views\Compilers\MO2CompilerConfigView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@ -300,6 +304,10 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Compilers\VortexCompilerConfigView.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
@ -465,5 +473,9 @@
|
||||
<Resource Include="Resources\MO2Button.png" />
|
||||
<Resource Include="Resources\VortexButton.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\Icons\gog.png" />
|
||||
<Resource Include="Resources\Icons\steam.png" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user