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;
}