From e2f406f07052a1a9acc62a2d8eb9bced05547e2e Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sun, 7 Dec 2025 00:27:46 -0600 Subject: [PATCH] game dir setup known bugs are a missing prod.keys popup after setup (how), as well as the dialog for autoload kinda cluttering up the screen after you hit next on the game dir page --- assets/locales.json | 25 ++++ src/Ryujinx.Common/SharedConstants.cs | 3 + .../Pages/Fw/SetupFirmwarePageContext.cs | 8 ++ .../Pages/GameDirs/SetupGameDirsPage.axaml | 107 ++++++++++++++++++ .../Pages/GameDirs/SetupGameDirsPage.axaml.cs | 80 +++++++++++++ .../GameDirs/SetupGameDirsPageContext.cs | 73 ++++++++++++ .../Pages/Keys/SetupKeysPageContext.cs | 24 ++-- .../SetupWizard/RyujinxSetupWizard.Steps.cs | 30 ++++- .../UI/SetupWizard/RyujinxSetupWizard.cs | 6 +- .../RyujinxSetupWizardWindow.axaml.cs | 4 +- 10 files changed, 347 insertions(+), 13 deletions(-) create mode 100644 src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml create mode 100644 src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml.cs create mode 100644 src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPageContext.cs diff --git a/assets/locales.json b/assets/locales.json index 351b36e6e..0d71c95bd 100644 --- a/assets/locales.json +++ b/assets/locales.json @@ -25217,6 +25217,31 @@ "zh_TW": "" } }, + { + "ID": "SetupWizardGameDirsPageTitle", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Game, Update, and DLC Paths", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SetupWizardFinalPageTitle", "Translations": { diff --git a/src/Ryujinx.Common/SharedConstants.cs b/src/Ryujinx.Common/SharedConstants.cs index cb6f1ef36..198c1c3e1 100644 --- a/src/Ryujinx.Common/SharedConstants.cs +++ b/src/Ryujinx.Common/SharedConstants.cs @@ -19,6 +19,9 @@ namespace Ryujinx.Common public const string DumpFirmwareWikiUrl = "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Dumping/Firmware"; + public const string DumpContentWikiUrl = + "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Dumping/Games,-Updates-&-DLC"; + public const string MultiplayerWikiUrl = "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Multiplayer-(LDN-Local-Wireless)-Guide"; } diff --git a/src/Ryujinx/UI/SetupWizard/Pages/Fw/SetupFirmwarePageContext.cs b/src/Ryujinx/UI/SetupWizard/Pages/Fw/SetupFirmwarePageContext.cs index 1f621c1e4..8d1d4439e 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/Fw/SetupFirmwarePageContext.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/Fw/SetupFirmwarePageContext.cs @@ -99,6 +99,14 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages public override Result CompleteStep() { + if (string.IsNullOrEmpty(FirmwareSourcePath) && RyujinxSetupWizard.HasFirmware) + { + NotificationManager.Information( + title: LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], + "Skipped setting up firmware as you already have a valid firmware installation and did not choose a folder or file to install from.\n\nClick 'Back' if you wish to overwrite your firmware."); + return Result.Success; // This handles the user selecting no file/dir and just hitting Next. + } + if (!Directory.Exists(FirmwareSourcePath)) return Result.Fail; diff --git a/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml new file mode 100644 index 000000000..8d05e0656 --- /dev/null +++ b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml.cs b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml.cs new file mode 100644 index 000000000..eb4851073 --- /dev/null +++ b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPage.axaml.cs @@ -0,0 +1,80 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Platform.Storage; +using Ryujinx.Ava; +using Ryujinx.Ava.UI.Controls; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.Utilities; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Ryujinx.UI.SetupWizard.Pages +{ + public partial class SetupGameDirsPage : RyujinxControl + { + public SetupGameDirsPage() + { + InitializeComponent(); + AddGameDirButton.Command = + Commands.Create(() => AddDirButton(GameDirPathBox, ViewModel.GameDirs)); + AddAutoloadDirButton.Command = + Commands.Create(() => AddDirButton(AutoloadDirPathBox, ViewModel.UpdateAndDlcDirs)); + } + + private async Task AddDirButton(TextBox addDirBox, ObservableCollection directories) + { + string path = addDirBox.Text; + + if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !directories.Contains(path)) + { + directories.Add(path); + + addDirBox.Clear(); + } + else + { + Gommon.Optional folder = await RyujinxApp.MainWindow.ViewModel.StorageProvider.OpenSingleFolderPickerAsync(); + + if (folder.HasValue) + { + directories.Add(folder.Value.Path.LocalPath); + } + } + } + + private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e) + { + int oldIndex = GameDirsList.SelectedIndex; + + foreach (string path in new List(GameDirsList.SelectedItems.Cast())) + { + ViewModel.GameDirs.Remove(path); + } + + if (GameDirsList.ItemCount > 0) + { + GameDirsList.SelectedIndex = oldIndex < GameDirsList.ItemCount ? oldIndex : 0; + } + } + + private void RemoveAutoloadDirButton_OnClick(object sender, RoutedEventArgs e) + { + int oldIndex = AutoloadDirsList.SelectedIndex; + + foreach (string path in new List(AutoloadDirsList.SelectedItems.Cast())) + { + ViewModel.UpdateAndDlcDirs.Remove(path); + } + + if (AutoloadDirsList.ItemCount > 0) + { + AutoloadDirsList.SelectedIndex = oldIndex < AutoloadDirsList.ItemCount ? oldIndex : 0; + } + } + } +} + diff --git a/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPageContext.cs b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPageContext.cs new file mode 100644 index 000000000..2e9e85473 --- /dev/null +++ b/src/Ryujinx/UI/SetupWizard/Pages/GameDirs/SetupGameDirsPageContext.cs @@ -0,0 +1,73 @@ +using Avalonia.Controls; +using Avalonia.Layout; +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.ObjectModel; +using Gommon; +using Ryujinx.Ava; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.Systems.Configuration; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.SetupWizard; +using Ryujinx.Common; +using System; +using System.Linq; + +namespace Ryujinx.UI.SetupWizard.Pages +{ + public partial class SetupGameDirsPageContext() : SetupWizardPageContext(LocaleKeys.SetupWizardGameDirsPageTitle) + { + [ObservableProperty] + public partial ObservableCollection GameDirs { get; set; } + = new(ConfigurationState.Instance.UI.GameDirs); + [ObservableProperty] + public partial ObservableCollection UpdateAndDlcDirs { get; set; } + = new(ConfigurationState.Instance.UI.AutoloadDirs); + + public override Result CompleteStep() + { + if (GameDirs.Count is 0) + { + NotificationManager.Error("At least one folder for games must be selected; otherwise the UI will be empty."); + return Result.Failure(RetryError.Shared); + } + + ConfigurationState.Instance.UI.GameDirs.Value = GameDirs.ToList(); + ConfigurationState.Instance.UI.AutoloadDirs.Value = UpdateAndDlcDirs.ToList(); + OwningWizard.SignalConfigModified(); + RyujinxApp.MainWindow.LoadApplications(); + + return Result.Success; + } + + public override object CreateHelpContent() + { + Grid grid = new() + { + RowDefinitions = [new(GridLength.Auto), new(GridLength.Auto)], + HorizontalAlignment = HorizontalAlignment.Center + }; + + grid.Children.Add(new TextBlock + { + Text = "Not sure how to get your games, updates, and/or DLC onto your PC?", + HorizontalAlignment = HorizontalAlignment.Center, + GridRow = 0 + }); + + grid.Children.Add(new HyperlinkButton + { + Content = "Click here to view a guide.", + HorizontalAlignment = HorizontalAlignment.Center, + NavigateUri = new Uri(SharedConstants.DumpFirmwareWikiUrl), + GridRow = 1 + }); + + return grid; + } + } + + public struct RetryError : IErrorState + { + public static readonly RetryError Shared = new(); + } +} diff --git a/src/Ryujinx/UI/SetupWizard/Pages/Keys/SetupKeysPageContext.cs b/src/Ryujinx/UI/SetupWizard/Pages/Keys/SetupKeysPageContext.cs index 7dd5f1105..3deed115d 100644 --- a/src/Ryujinx/UI/SetupWizard/Pages/Keys/SetupKeysPageContext.cs +++ b/src/Ryujinx/UI/SetupWizard/Pages/Keys/SetupKeysPageContext.cs @@ -21,11 +21,6 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages { public partial class SetupKeysPageContext() : SetupWizardPageContext(LocaleKeys.SetupWizardKeysPageTitle) { - public override Result CompleteStep() => - Directory.Exists(KeysFolderPath) - ? InstallKeys(KeysFolderPath) - : Result.Fail; - public override object CreateHelpContent() { Grid grid = new() @@ -70,8 +65,19 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages } } - private Result InstallKeys(string directory) + public override Result CompleteStep() { + if (string.IsNullOrEmpty(KeysFolderPath) && RyujinxApp.MainWindow.VirtualFileSystem.HasKeySet) + { + NotificationManager.Information( + title: LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], + "Skipped setting up keys as you already have a valid key installation and did not choose a folder to install from.\n\nClick 'Back' if you wish to reinstall your keys."); + return Result.Success; // This handles the user selecting no folder and just hitting Next. + } + + if (!Directory.Exists(KeysFolderPath)) + return Result.Fail; + try { string systemDirectory = AppDataManager.KeysDirPath; @@ -81,9 +87,9 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages systemDirectory = AppDataManager.KeysDirPathUser; } - Logger.Info?.Print(LogClass.Application, $"Installing keys from {directory}"); + Logger.Info?.Print(LogClass.Application, $"Installing keys from {KeysFolderPath}"); - ContentManager.InstallKeys(directory, systemDirectory); + ContentManager.InstallKeys(KeysFolderPath, systemDirectory); NotificationManager.Information( title: LocaleManager.Instance[LocaleKeys.RyujinxInfo], @@ -105,7 +111,7 @@ namespace Ryujinx.Ava.UI.SetupWizard.Pages if (ex is FormatException) { message = LocaleManager.Instance.UpdateAndGetDynamicValue( - LocaleKeys.DialogKeysInstallerKeysNotFoundErrorMessage, directory); + LocaleKeys.DialogKeysInstallerKeysNotFoundErrorMessage, KeysFolderPath); } NotificationManager.Error(message, waitingExit: true); diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Steps.cs b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Steps.cs index c40e8c02c..b3fd68e3a 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Steps.cs +++ b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.Steps.cs @@ -1,5 +1,5 @@ -using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.SetupWizard.Pages; +using Ryujinx.UI.SetupWizard.Pages; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.SetupWizard @@ -51,6 +51,34 @@ namespace Ryujinx.Ava.UI.SetupWizard return true; } + private async ValueTask SetupGameDirs() + { + + if (!HasFirmware) + { + NotificationManager.Error("Firmware still seems to not be installed. Please try again."); + return false; + } + + Retry: + bool result = + await NextPage(out SetupGameDirsPageContext gdContext) + .Show(); + + if (!result) + return false; + + var res = gdContext.CompleteStep(); + + if (res.IsOf()) + return false; + + if (!res) + goto Retry; + + return true; + } + private ValueTask Finish() => NextPage(out _) .WithHelpButtonVisible(false) diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs index e34e62e2f..5db3f724f 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs +++ b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizard.cs @@ -82,9 +82,13 @@ namespace Ryujinx.Ava.UI.SetupWizard Firmware: if (!await SetupFirmware()) goto Keys; + + GameDirs: + if (!await SetupGameDirs()) + goto Firmware; if (!await Finish()) - goto Firmware; + goto GameDirs; Return: if (_configWasModified) diff --git a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs index ec5f39743..f3118f1b5 100644 --- a/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs +++ b/src/Ryujinx/UI/SetupWizard/RyujinxSetupWizardWindow.axaml.cs @@ -40,8 +40,8 @@ namespace Ryujinx.Ava.UI.SetupWizard { RyujinxSetupWizardWindow window = new(); window.DataContext = setupWizard = new RyujinxSetupWizard(window, overwriteMode); - window.Height = 600; - window.Width = 750; + window.Height = 700; + window.Width = 825; return window; }