diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d1e4b92..72e76022 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,24 @@ ### Changelog +#### Version - TBD +* Updated GameFinder to 4.1.0 + * Fixes Wabbajack not Launching on some systems +* Added the GameFinder module for EA Desktop + * Only `Dragon Age: Origins` & `Dragon Age: Inquisition` supported for now. + * For other games we still need to collect the right store IDs +* Fixed Final Fantasy 7: Remake Intergrade meta data +* Fixed new WebView2 instances being created constantly causing a memory leak +* Fixed the Nexus API key no longer being picked up when logging in +* Fixed Baldur's Gate 3 having to be named 'badlursgate3' instead of 'baldursgate3' when defining a modlist JSON +* Fixed wabbajack-cli.bat pointing to the wrong CLI executable path + #### Version - 3.4.1.0 - 12/21/2023 * Added Support for Final Fantasy 7: Remake Intergrade * Update CLI to .NET 8.0 (was missed in the last update) * Added Support for Baldur's Gate 3 * Very Work in Progress * **NOT** Plug and Play for compiling and installing! +* Fixed a logging error when closing the App without a `temp` folder to delete #### Version - 3.4.0.0 - 11/19/2023 * Fixed `--outputPath` not being used for the CLI `compile` (thanks to @majcosta for fixing that) diff --git a/Wabbajack.App.Wpf/App.xaml.cs b/Wabbajack.App.Wpf/App.xaml.cs index dc9d2cd0..1ad25eee 100644 --- a/Wabbajack.App.Wpf/App.xaml.cs +++ b/Wabbajack.App.Wpf/App.xaml.cs @@ -8,6 +8,7 @@ using System.Windows.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Web.WebView2.Wpf; using NLog.Extensions.Logging; using NLog.Targets; using Orc.FileAssociation; @@ -164,6 +165,7 @@ namespace Wabbajack services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/Wabbajack.App.Wpf/LoginManagers/LoversLabLoginManager.cs b/Wabbajack.App.Wpf/LoginManagers/LoversLabLoginManager.cs index 985fec98..8e982af7 100644 --- a/Wabbajack.App.Wpf/LoginManagers/LoversLabLoginManager.cs +++ b/Wabbajack.App.Wpf/LoginManagers/LoversLabLoginManager.cs @@ -66,7 +66,7 @@ public class LoversLabLoginManager : ViewModel, ILoginFor private void StartLogin() { - var view = new BrowserWindow(); + var view = new BrowserWindow(_serviceProvider); view.Closed += (sender, args) => { RefreshTokenState(); }; var provider = _serviceProvider.GetRequiredService(); view.DataContext = provider; diff --git a/Wabbajack.App.Wpf/LoginManagers/NexusLoginManager.cs b/Wabbajack.App.Wpf/LoginManagers/NexusLoginManager.cs index a7115a76..440b60fe 100644 --- a/Wabbajack.App.Wpf/LoginManagers/NexusLoginManager.cs +++ b/Wabbajack.App.Wpf/LoginManagers/NexusLoginManager.cs @@ -62,7 +62,7 @@ public class NexusLoginManager : ViewModel, ILoginFor private void StartLogin() { - var view = new BrowserWindow(); + var view = new BrowserWindow(_serviceProvider); view.Closed += (sender, args) => { RefreshTokenState(); }; var provider = _serviceProvider.GetRequiredService(); view.DataContext = provider; diff --git a/Wabbajack.App.Wpf/LoginManagers/VectorPlexusLoginManager.cs b/Wabbajack.App.Wpf/LoginManagers/VectorPlexusLoginManager.cs index 9e8f691d..62a13e26 100644 --- a/Wabbajack.App.Wpf/LoginManagers/VectorPlexusLoginManager.cs +++ b/Wabbajack.App.Wpf/LoginManagers/VectorPlexusLoginManager.cs @@ -66,7 +66,7 @@ public class VectorPlexusLoginManager : ViewModel, ILoginFor { RefreshTokenState(); }; var provider = _serviceProvider.GetRequiredService(); view.DataContext = provider; diff --git a/Wabbajack.App.Wpf/UserIntervention/NexusLoginHandler.cs b/Wabbajack.App.Wpf/UserIntervention/NexusLoginHandler.cs index 617885eb..aa9e2f36 100644 --- a/Wabbajack.App.Wpf/UserIntervention/NexusLoginHandler.cs +++ b/Wabbajack.App.Wpf/UserIntervention/NexusLoginHandler.cs @@ -45,7 +45,7 @@ public class NexusLoginHandler : BrowserWindowViewModel Instructions = "Getting API Key..."; - await NavigateTo(new Uri("https://www.nexusmods.com/users/myaccount?tab=api")); + await NavigateTo(new Uri("https://next.nexusmods.com/settings/api-keys")); var key = ""; @@ -53,12 +53,7 @@ public class NexusLoginHandler : BrowserWindowViewModel { try { - key = (await GetDom(token)) - .DocumentNode - .QuerySelectorAll("input[value=wabbajack]") - .SelectMany(p => p.ParentNode.ParentNode.QuerySelectorAll("textarea.application-key")) - .Select(node => node.InnerHtml) - .FirstOrDefault() ?? ""; + key = (await GetDom(token)).DocumentNode.QuerySelectorAll("img[alt='Wabbajack']").SelectMany(p => p.ParentNode.ParentNode.QuerySelectorAll("input[aria-label='api key']")).Select(node => node.Attributes["value"]).FirstOrDefault()?.Value; } catch (Exception) { @@ -71,11 +66,10 @@ public class NexusLoginHandler : BrowserWindowViewModel try { await EvaluateJavaScript( - "var found = document.querySelector(\"input[value=wabbajack]\").parentElement.parentElement.querySelector(\"form button[type=submit]\");" + + "var found = document.querySelector(\"img[alt='Wabbajack']\").parentElement.parentElement.querySelector(\"button[aria-label='Request Api Key']\");" + "found.onclick= function() {return true;};" + "found.class = \" \"; " + - "found.click();" + - "found.remove(); found = undefined;" + "found.click();" ); Instructions = "Generating API Key, Please Wait..."; } diff --git a/Wabbajack.App.Wpf/Verbs/NexusLogin.cs b/Wabbajack.App.Wpf/Verbs/NexusLogin.cs index b4140cec..b91148fe 100644 --- a/Wabbajack.App.Wpf/Verbs/NexusLogin.cs +++ b/Wabbajack.App.Wpf/Verbs/NexusLogin.cs @@ -25,7 +25,7 @@ public class NexusLogin public async Task Run(CancellationToken token) { var tcs = new TaskCompletionSource(); - var view = new BrowserWindow(); + var view = new BrowserWindow(_services); view.Closed += (sender, args) => { tcs.TrySetResult(0); }; var provider = _services.GetRequiredService(); view.DataContext = provider; diff --git a/Wabbajack.App.Wpf/Views/BrowserWindow.xaml b/Wabbajack.App.Wpf/Views/BrowserWindow.xaml index 62a13a0f..a1fa5033 100644 --- a/Wabbajack.App.Wpf/Views/BrowserWindow.xaml +++ b/Wabbajack.App.Wpf/Views/BrowserWindow.xaml @@ -23,7 +23,7 @@ WindowTitleBrush="{StaticResource MahApps.Brushes.Accent}" ContentRendered="BrowserWindow_OnActivated" mc:Ignorable="d"> - + @@ -46,8 +46,5 @@ - - - diff --git a/Wabbajack.App.Wpf/Views/BrowserWindow.xaml.cs b/Wabbajack.App.Wpf/Views/BrowserWindow.xaml.cs index 175de016..a77c5b78 100644 --- a/Wabbajack.App.Wpf/Views/BrowserWindow.xaml.cs +++ b/Wabbajack.App.Wpf/Views/BrowserWindow.xaml.cs @@ -1,11 +1,15 @@ using System; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using MahApps.Metro.Controls; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Web.WebView2.Wpf; using ReactiveUI; using Wabbajack.Common; @@ -14,12 +18,27 @@ namespace Wabbajack; public partial class BrowserWindow : MetroWindow { private readonly CompositeDisposable _disposable; + private readonly IServiceProvider _serviceProvider; + public WebView2 Browser { get; set; } - public BrowserWindow() + public BrowserWindow(IServiceProvider serviceProvider) { InitializeComponent(); + _disposable = new CompositeDisposable(); + _serviceProvider = serviceProvider; + Browser = _serviceProvider.GetRequiredService(); + RxApp.MainThreadScheduler.Schedule(() => + { + if(Browser.Parent != null) + { + ((Panel)Browser.Parent).Children.Remove(Browser); + } + MainGrid.Children.Add(Browser); + Grid.SetRow(Browser, 3); + Grid.SetColumnSpan(Browser, 3); + }); } private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e) @@ -58,7 +77,6 @@ public partial class BrowserWindow : MetroWindow .ContinueWith(_ => Dispatcher.Invoke(() => { Close(); - Browser = null; })); } } \ No newline at end of file diff --git a/Wabbajack.App.Wpf/Views/MainWindow.xaml.cs b/Wabbajack.App.Wpf/Views/MainWindow.xaml.cs index 6f324726..2135f5c0 100644 --- a/Wabbajack.App.Wpf/Views/MainWindow.xaml.cs +++ b/Wabbajack.App.Wpf/Views/MainWindow.xaml.cs @@ -52,27 +52,26 @@ namespace Wabbajack // Cleaning the temp folder when the app closes since it can take up multiple Gigabytes of Storage var tempDirectory = Environment.CurrentDirectory + "\\temp"; _logger.LogInformation("Clearing {TempDir}",tempDirectory); + var directoryInfo = new DirectoryInfo(tempDirectory); try { - var directoryInfo = new DirectoryInfo(tempDirectory); - foreach (var file in directoryInfo.EnumerateFiles()) { - file.Delete(); + file.Delete(); } + foreach (var dir in directoryInfo.EnumerateDirectories()) { - dir.Delete(true); + dir.Delete(true); } - - _logger.LogInformation("Finished clearing {TempDir}",tempDirectory); + + _logger.LogInformation("Finished clearing {TempDir}", tempDirectory); } - catch (Exception ex) + catch (DirectoryNotFoundException) { - _logger.LogError(ex,"Failed clearing {TempDir}",tempDirectory); + _logger.LogInformation("Unable to find {TempDir}", tempDirectory); } - Application.Current.Shutdown(); }; diff --git a/Wabbajack.DTOs/Game/Game.cs b/Wabbajack.DTOs/Game/Game.cs index c99153ef..dcd6fef9 100644 --- a/Wabbajack.DTOs/Game/Game.cs +++ b/Wabbajack.DTOs/Game/Game.cs @@ -54,5 +54,5 @@ public enum Game [Description("Modding Tools")] ModdingTools, [Description("Final Fantasy VII Remake")] FinalFantasy7Remake, - [Description("Baldur's Gate 3")] BadlursGate3 + [Description("Baldur's Gate 3")] BaldursGate3 } diff --git a/Wabbajack.DTOs/Game/GameMetaData.cs b/Wabbajack.DTOs/Game/GameMetaData.cs index 1b4a4061..79b9e81f 100644 --- a/Wabbajack.DTOs/Game/GameMetaData.cs +++ b/Wabbajack.DTOs/Game/GameMetaData.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices.JavaScript; using Wabbajack.Paths; namespace Wabbajack.DTOs; @@ -29,6 +30,8 @@ public class GameMetaData // EAPlay games may have @subscription appended to the file name public string[] OriginIDs { get; set; } = Array.Empty(); + public string[] EADesktopIDs { get; set; } = Array.Empty(); + public string[] EpicGameStoreIDs { get; internal init; } = Array.Empty(); // to get BethNet IDs: check the registry diff --git a/Wabbajack.DTOs/Game/GameRegistry.cs b/Wabbajack.DTOs/Game/GameRegistry.cs index e3310ebb..23a18faf 100644 --- a/Wabbajack.DTOs/Game/GameRegistry.cs +++ b/Wabbajack.DTOs/Game/GameRegistry.cs @@ -357,6 +357,22 @@ public static class GameRegistry MO2Name = "Dragon Age: Origins", SteamIDs = new[] {47810}, OriginIDs = new[] {"DR:169789300", "DR:208591800"}, + EADesktopIDs = new [] // Possibly Wrong + { + "9df89a8e-b201-4507-8a8d-bd6799fedb18", + "Origin.SFT.50.0000078", + "Origin.SFT.50.0000078", + "Origin.SFT.50.0000078", + "Origin.SFT.50.0000085", + "Origin.SFT.50.0000086", + "Origin.SFT.50.0000087", + "Origin.SFT.50.0000088", + "Origin.SFT.50.0000089", + "Origin.SFT.50.0000090", + "Origin.SFT.50.0000091", + "Origin.SFT.50.0000097", + "Origin.SFT.50.0000098" + }, GOGIDs = new long[] {1949616134}, RequiredFiles = new[] { @@ -374,6 +390,22 @@ public static class GameRegistry MO2Name = "Dragon Age 2", // Probably wrong SteamIDs = new[] {1238040}, OriginIDs = new[] {"OFB-EAST:59474", "DR:201797000"}, + EADesktopIDs = new [] // Possibly Wrong + { + "Origin.SFT.50.0000073", + "Origin.SFT.50.0000255", + "Origin.SFT.50.0000256", + "Origin.SFT.50.0000257", + "Origin.SFT.50.0000288", + "Origin.SFT.50.0000310", + "Origin.SFT.50.0000311", + "Origin.SFT.50.0000356", + "Origin.SFT.50.0000385", + "Origin.SFT.50.0000429", + "Origin.SFT.50.0000449", + "Origin.SFT.50.0000452", + "Origin.SFT.50.0000453" + }, RequiredFiles = new[] { @"bin_ship\DragonAge2.exe".ToRelativePath() @@ -540,21 +572,22 @@ public static class GameRegistry Game = Game.FinalFantasy7Remake, NexusName = "finalfantasy7remake", NexusGameId = 4202, - MO2Name = "FINAL FANTASY VII REMAKE", + MO2Name = "FINAL FANTASY VII REMAKE INTERGRADE", MO2ArchiveName = "finalfantasy7remake", SteamIDs = new[] { 1462040 }, IsGenericMO2Plugin = true, RequiredFiles = new [] { - @"ff7remake.exe".ToRelativePath() + @"End\Binaries\Win64\ff7remake_.exe".ToRelativePath(), + @"ff7remake_.exe".ToRelativePath() }, - MainExecutable = @"ff7remake.exe".ToRelativePath() + MainExecutable = @"End\Binaries\Win64\ff7remake_.exe".ToRelativePath() } }, { - Game.BadlursGate3, new GameMetaData + Game.BaldursGate3, new GameMetaData { - Game = Game.BadlursGate3, + Game = Game.BaldursGate3, NexusName = "baldursgate3", NexusGameId = 3474, MO2Name = "Baldur's Gate 3", diff --git a/Wabbajack.Downloaders.GameFile/GameLocator.cs b/Wabbajack.Downloaders.GameFile/GameLocator.cs index 8c2a9d94..f5e887a0 100644 --- a/Wabbajack.Downloaders.GameFile/GameLocator.cs +++ b/Wabbajack.Downloaders.GameFile/GameLocator.cs @@ -1,6 +1,8 @@ using System.Runtime.InteropServices; using GameFinder.Common; using GameFinder.RegistryUtils; +using GameFinder.StoreHandlers.EADesktop; +using GameFinder.StoreHandlers.EADesktop.Crypto.Windows; using GameFinder.StoreHandlers.EGS; using GameFinder.StoreHandlers.GOG; using GameFinder.StoreHandlers.Origin; @@ -20,12 +22,14 @@ public class GameLocator : IGameLocator private readonly GOGHandler? _gog; private readonly EGSHandler? _egs; private readonly OriginHandler? _origin; + private readonly EADesktopHandler? _eaDesktop; private readonly Dictionary _steamGames = new(); private readonly Dictionary _gogGames = new(); private readonly Dictionary _egsGames = new(); private readonly Dictionary _originGames = new(); - + private readonly Dictionary _eaDesktopGames = new(); + private readonly Dictionary _locationCache; private readonly ILogger _logger; @@ -42,6 +46,7 @@ public class GameLocator : IGameLocator _gog = new GOGHandler(windowsRegistry, fileSystem); _egs = new EGSHandler(windowsRegistry, fileSystem); _origin = new OriginHandler(fileSystem); + _eaDesktop = new EADesktopHandler(fileSystem, new HardwareInfoProvider()); } else { @@ -90,6 +95,14 @@ public class GameLocator : IGameLocator { _logger.LogError(e, "While finding games installed with Origin"); } + try + { + FindStoreGames(_eaDesktop, _eaDesktopGames, game => (AbsolutePath)game.BaseInstallPath.GetFullPath()); + } + catch (Exception e) + { + _logger.LogError(e, "While finding games installed with EADesktop"); + } } private void FindStoreGames( @@ -189,6 +202,13 @@ public class GameLocator : IGameLocator path = found; return true; } + + foreach (var id in metaData.EADesktopIDs) + { + if (!_eaDesktopGames.TryGetValue(EADesktopGameId.From(id), out var found)) continue; + path = found; + return true; + } path = default; return false; diff --git a/Wabbajack.Downloaders.GameFile/Wabbajack.Downloaders.GameFile.csproj b/Wabbajack.Downloaders.GameFile/Wabbajack.Downloaders.GameFile.csproj index 8f005484..0a03a0ad 100644 --- a/Wabbajack.Downloaders.GameFile/Wabbajack.Downloaders.GameFile.csproj +++ b/Wabbajack.Downloaders.GameFile/Wabbajack.Downloaders.GameFile.csproj @@ -18,10 +18,11 @@ - - - - + + + + + diff --git a/Wabbajack.Launcher/ViewModels/MainWindowViewModel.cs b/Wabbajack.Launcher/ViewModels/MainWindowViewModel.cs index 47e16982..2e8f7650 100644 --- a/Wabbajack.Launcher/ViewModels/MainWindowViewModel.cs +++ b/Wabbajack.Launcher/ViewModels/MainWindowViewModel.cs @@ -303,10 +303,13 @@ public class MainWindowViewModel : ViewModelBase { try { - filename = filename.Parent.Combine("wabbajack-cli.exe"); + filename = filename.Parent.Combine("cli", "wabbajack-cli.exe"); var data = $"\"{filename}\" %*"; var file = Path.Combine(Directory.GetCurrentDirectory(), "wabbajack-cli.bat"); if (File.Exists(file) && await File.ReadAllTextAsync(file) == data) return; + var parent = Directory.GetParent(file).FullName; + if (!Directory.Exists(file)) + Directory.CreateDirectory(parent); await File.WriteAllTextAsync(file, data); } catch (Exception ex)