diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index e60fac09b..2ccb67c70 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 52; + public const int CurrentVersion = 53; /// /// Version of the configuration file format @@ -266,6 +266,11 @@ namespace Ryujinx.UI.Common.Configuration /// A list of directories containing games to be used to load games into the games list /// public List GameDirs { get; set; } + + /// + /// A list of directories containing DLC/updates the user wants to autoload during library refreshes + /// + public List AutoloadDirs { get; set; } /// /// A list of file types to be hidden in the games List diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index 4cd85aa01..f9179d0c0 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -121,6 +121,11 @@ namespace Ryujinx.UI.Common.Configuration /// A list of directories containing games to be used to load games into the games list /// public ReactiveObject> GameDirs { get; private set; } + + /// + /// A list of directories containing DLC/updates the user wants to autoload during library refreshes + /// + public ReactiveObject> AutoloadDirs { get; private set; } /// /// A list of file types to be hidden in the games List @@ -192,6 +197,7 @@ namespace Ryujinx.UI.Common.Configuration GuiColumns = new Columns(); ColumnSort = new ColumnSortSettings(); GameDirs = new ReactiveObject>(); + AutoloadDirs = new ReactiveObject>(); ShownFileTypes = new ShownFileTypeSettings(); WindowStartup = new WindowStartupSettings(); EnableCustomTheme = new ReactiveObject(); @@ -735,6 +741,7 @@ namespace Ryujinx.UI.Common.Configuration SortAscending = UI.ColumnSort.SortAscending, }, GameDirs = UI.GameDirs, + AutoloadDirs = UI.AutoloadDirs, ShownFileTypes = new ShownFileTypes { NSP = UI.ShownFileTypes.NSP, @@ -844,6 +851,7 @@ namespace Ryujinx.UI.Common.Configuration UI.ColumnSort.SortColumnId.Value = 0; UI.ColumnSort.SortAscending.Value = false; UI.GameDirs.Value = new List(); + UI.AutoloadDirs.Value = new List(); UI.ShownFileTypes.NSP.Value = true; UI.ShownFileTypes.PFS0.Value = true; UI.ShownFileTypes.XCI.Value = true; @@ -1493,6 +1501,15 @@ namespace Ryujinx.UI.Common.Configuration configurationFileUpdated = true; } + + if (configurationFileFormat.Version < 53) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 53."); + + configurationFileFormat.AutoloadDirs = new(); + + configurationFileUpdated = true; + } Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; @@ -1556,6 +1573,7 @@ namespace Ryujinx.UI.Common.Configuration UI.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; UI.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; UI.GameDirs.Value = configurationFileFormat.GameDirs; + UI.AutoloadDirs.Value = configurationFileFormat.AutoloadDirs; UI.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; UI.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; UI.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index 6f050ec1b..339842616 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -106,6 +106,7 @@ "SettingsTabGeneralHideCursorOnIdle": "On Idle", "SettingsTabGeneralHideCursorAlways": "Always", "SettingsTabGeneralGameDirectories": "Game Directories", + "SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories", "SettingsTabGeneralAdd": "Add", "SettingsTabGeneralRemove": "Remove", "SettingsTabSystem": "System", @@ -559,6 +560,9 @@ "AddGameDirBoxTooltip": "Enter a game directory to add to the list", "AddGameDirTooltip": "Add a game directory to the list", "RemoveGameDirTooltip": "Remove selected game directory", + "AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list", + "AddAutoloadDirTooltip": "Add an autoload directory to the list", + "RemoveAutoloadDirTooltip": "Remove selected autoload directory", "CustomThemeCheckTooltip": "Use a custom Avalonia theme for the GUI to change the appearance of the emulator menus", "CustomThemePathTooltip": "Path to custom GUI theme", "CustomThemeBrowseTooltip": "Browse for a custom GUI theme", diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 9223e578d..57c576e1f 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -44,7 +44,8 @@ namespace Ryujinx.Ava.UI.ViewModels private int _graphicsBackendMultithreadingIndex; private float _volume; private bool _isVulkanAvailable = true; - private bool _directoryChanged; + private bool _gameDirectoryChanged; + private bool _autoloadDirectoryChanged; private readonly List _gpuIds = new(); private int _graphicsBackendIndex; private int _scalingFilter; @@ -115,12 +116,23 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; - public bool DirectoryChanged + public bool GameDirectoryChanged { - get => _directoryChanged; + get => _gameDirectoryChanged; set { - _directoryChanged = value; + _gameDirectoryChanged = value; + + OnPropertyChanged(); + } + } + + public bool AutoloadDirectoryChanged + { + get => _autoloadDirectoryChanged; + set + { + _autoloadDirectoryChanged = value; OnPropertyChanged(); } @@ -231,6 +243,7 @@ namespace Ryujinx.Ava.UI.ViewModels internal AvaloniaList TimeZones { get; set; } public AvaloniaList GameDirectories { get; set; } + public AvaloniaList AutoloadDirectories { get; set; } public ObservableCollection AvailableGpus { get; set; } public AvaloniaList NetworkInterfaceList @@ -273,6 +286,7 @@ namespace Ryujinx.Ava.UI.ViewModels public SettingsViewModel() { GameDirectories = new AvaloniaList(); + AutoloadDirectories = new AvaloniaList(); TimeZones = new AvaloniaList(); AvailableGpus = new ObservableCollection(); _validTzRegions = new List(); @@ -398,6 +412,9 @@ namespace Ryujinx.Ava.UI.ViewModels GameDirectories.Clear(); GameDirectories.AddRange(config.UI.GameDirs.Value); + + AutoloadDirectories.Clear(); + AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value); BaseStyleIndex = config.UI.BaseStyle.Value switch { @@ -489,11 +506,17 @@ namespace Ryujinx.Ava.UI.ViewModels config.AutoloadContent.Value = AutoloadContent; config.HideCursor.Value = (HideCursorMode)HideCursor; - if (_directoryChanged) + if (_gameDirectoryChanged) { List gameDirs = new(GameDirectories); config.UI.GameDirs.Value = gameDirs; } + + if (_autoloadDirectoryChanged) + { + List autoloadDirs = new(AutoloadDirectories); + config.UI.AutoloadDirs.Value = autoloadDirs; + } config.UI.BaseStyle.Value = BaseStyleIndex switch { @@ -590,7 +613,8 @@ namespace Ryujinx.Ava.UI.ViewModels SaveSettingsEvent?.Invoke(); - _directoryChanged = false; + _gameDirectoryChanged = false; + _autoloadDirectoryChanged = false; } private static void RevertIfNotSaved() diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml index 9d26effcc..4f25cbebf 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml @@ -88,7 +88,7 @@ Orientation="Vertical" Spacing="10"> @@ -105,27 +105,78 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs index 996d15cdb..70ca32306 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs @@ -19,14 +19,14 @@ namespace Ryujinx.Ava.UI.Views.Settings InitializeComponent(); } - private async void AddButton_OnClick(object sender, RoutedEventArgs e) + private async void AddGameDirButton_OnClick(object sender, RoutedEventArgs e) { - string path = PathBox.Text; + string path = GameDirPathBox.Text; if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path)) { ViewModel.GameDirectories.Add(path); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } else { @@ -40,25 +40,68 @@ namespace Ryujinx.Ava.UI.Views.Settings if (result.Count > 0) { ViewModel.GameDirectories.Add(result[0].Path.LocalPath); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } } } } - private void RemoveButton_OnClick(object sender, RoutedEventArgs e) + private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e) { - int oldIndex = GameList.SelectedIndex; + int oldIndex = GameDirsList.SelectedIndex; - foreach (string path in new List(GameList.SelectedItems.Cast())) + foreach (string path in new List(GameDirsList.SelectedItems.Cast())) { ViewModel.GameDirectories.Remove(path); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } - if (GameList.ItemCount > 0) + if (GameDirsList.ItemCount > 0) { - GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0; + GameDirsList.SelectedIndex = oldIndex < GameDirsList.ItemCount ? oldIndex : 0; + } + } + + private async void AddAutoloadDirButton_OnClick(object sender, RoutedEventArgs e) + { + string path = AutoloadDirPathBox.Text; + + if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.AutoloadDirectories.Contains(path)) + { + ViewModel.AutoloadDirectories.Add(path); + ViewModel.AutoloadDirectoryChanged = true; + } + else + { + if (this.GetVisualRoot() is Window window) + { + var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions + { + AllowMultiple = false, + }); + + if (result.Count > 0) + { + ViewModel.AutoloadDirectories.Add(result[0].Path.LocalPath); + ViewModel.AutoloadDirectoryChanged = true; + } + } + } + } + + private void RemoveAutoloadDirButton_OnClick(object sender, RoutedEventArgs e) + { + int oldIndex = AutoloadDirsList.SelectedIndex; + + foreach (string path in new List(AutoloadDirsList.SelectedItems.Cast())) + { + ViewModel.AutoloadDirectories.Remove(path); + ViewModel.AutoloadDirectoryChanged = true; + } + + if (AutoloadDirsList.ItemCount > 0) + { + AutoloadDirsList.SelectedIndex = oldIndex < AutoloadDirsList.ItemCount ? oldIndex : 0; } } } diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 7947b4896..3b471fee8 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -642,17 +642,18 @@ namespace Ryujinx.Ava.UI.Windows TimeIt("updates", () => ApplicationLibrary.LoadTitleUpdates()); TimeIt("DLC", () => ApplicationLibrary.LoadDownloadableContents()); - if (ConfigurationState.Instance.AutoloadContent) + var autoloadDirs = ConfigurationState.Instance.UI.AutoloadDirs.Value; + if (autoloadDirs.Count > 0) { var updatesLoaded = 0; TimeIt("auto updates", () => updatesLoaded = - ApplicationLibrary.AutoLoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs)); + ApplicationLibrary.AutoLoadTitleUpdates(autoloadDirs)); var dlcLoaded = 0; TimeIt("auto dlc", () => dlcLoaded = - ApplicationLibrary.AutoLoadDownloadableContents(ConfigurationState.Instance.UI.GameDirs)); + ApplicationLibrary.AutoLoadDownloadableContents(autoloadDirs)); ShowNewContentAddedDialog(dlcLoaded, updatesLoaded); } diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index 314501c52..4d7871886 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Ava.UI.Windows { InputPage.InputView?.SaveCurrentProfile(); - if (Owner is MainWindow window && ViewModel.DirectoryChanged) + if (Owner is MainWindow window && (ViewModel.GameDirectoryChanged || ViewModel.AutoloadDirectoryChanged)) { window.LoadApplications(); }