diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 18842ce72..3da0b1728 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -2522,6 +2522,56 @@ "zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式" } }, + { + "ID": "GameListContextMenuShowCompatEntry", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Show Compatibility Entry", + "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": "GameListContextMenuShowCompatEntryToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Show the selected game in the Compatibility List you can normally access via the Help menu.", + "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": "GameListContextMenuOpenModsDirectory", "Translations": { diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml index 475b26787..2804485fe 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml @@ -19,6 +19,12 @@ Header="{ext:Locale GameListContextMenuCreateShortcut}" Icon="{ext:Icon fa-solid fa-bookmark}" ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" /> + - diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs index 3531e847b..337684491 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs @@ -12,6 +12,7 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities.AppLibrary; +using Ryujinx.Ava.Utilities.Compat; using Ryujinx.Common.Configuration; using Ryujinx.Common.Helper; using Ryujinx.HLE.HOS; @@ -333,7 +334,7 @@ namespace Ryujinx.Ava.UI.Controls if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) return; - DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.IdBase, viewModel.ApplicationLibrary); + DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.Id, viewModel.ApplicationLibrary); if (selectedDlc is not null) { @@ -381,6 +382,12 @@ namespace Ryujinx.Ava.UI.Controls await new ArgumentsConfigWindows(viewModel).ShowDialog((Window)viewModel.TopLevel); } + + public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args) + { + if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) + await CompatibilityList.Show(viewModel.SelectedApplication.IdString); + } public async void RunApplication_Click(object sender, RoutedEventArgs args) { diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml b/src/Ryujinx/UI/Controls/ApplicationListView.axaml index af7b5ccd0..151bf5b32 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationListView.axaml @@ -86,13 +86,29 @@ Text="{Binding Version}" TextAlignment="Start" TextWrapping="Wrap" /> - + Background="{DynamicResource AppListBackgroundColor}" + Margin="-1, 0, 0, 0" + Padding="0" > + + + + + x.Dlc.TitleIdBase == titleId) - .Select(x => x.Dlc) + _dlcs = appLibrary.FindDlcsFor(titleId) .OrderBy(it => it.IsBundled ? 0 : 1) .ThenBy(it => it.TitleId) .ToArray(); diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 3a6fc8d2f..632e3b4f0 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -349,6 +349,19 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool HasCompatibilityEntry + { + get + { + DynamicData.Kernel.Optional appData = + ApplicationLibrary.Applications.Lookup(SelectedApplication.Id); + + return appData.HasValue && appData.Value.HasPlayabilityInfo; + } + } + + public bool HasDlc => ApplicationLibrary.HasDlcs(SelectedApplication.Id); + public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index f6c43aade..a0bcd1aa2 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -50,7 +50,7 @@ namespace Ryujinx.Ava.UI.Views.Main UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes); XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show); AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show); - CompatibilityListMenuItem.Command = Commands.Create(CompatibilityList.Show); + CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show()); UpdateMenuItem.Command = Commands.Create(async () => { diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs index dec265623..75737c3e5 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationLibrary.cs @@ -135,6 +135,32 @@ namespace Ryujinx.Ava.Utilities.AppLibrary return id.ToString("X16"); } + public bool FindApplication(ulong id, out ApplicationData foundData) + { + DynamicData.Kernel.Optional appData = Applications.Lookup(id); + foundData = appData.HasValue ? appData.Value : null; + + return appData.HasValue; + } + + public bool FindUpdate(ulong id, out TitleUpdateModel foundData) + { + Gommon.Optional appData = + TitleUpdates.Keys.FindFirst(x => x.TitleId == id); + foundData = appData.HasValue ? appData.Value : null; + + return appData.HasValue; + } + + public TitleUpdateModel[] FindUpdatesFor(ulong id) + => TitleUpdates.Keys.Where(x => x.TitleIdBase == (id & ~0x1FFFUL)).ToArray(); + + public DownloadableContentModel[] FindDlcsFor(ulong id) + => DownloadableContents.Keys.Where(x => x.TitleIdBase == (id & ~0x1FFFUL)).ToArray(); + + public bool HasDlcs(ulong id) + => DownloadableContents.Keys.Any(x => x.TitleIdBase == (id & ~0x1FFFUL)); + /// The configured key set is missing a key. /// The NCA header could not be decrypted. /// The NCA version is not supported. diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index d0e251fe0..c3fcf99ca 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -113,20 +113,17 @@ namespace Ryujinx.Ava.Utilities.Compat .Select(FormatLabelName) .JoinToString(", "); - public override string ToString() - { - StringBuilder sb = new("CompatibilityEntry: {"); - sb.Append($"{nameof(GameName)}=\"{GameName}\", "); - sb.Append($"{nameof(TitleId)}={TitleId}, "); - sb.Append($"{nameof(Labels)}={ - Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]") - }, "); - sb.Append($"{nameof(Status)}=\"{Status}\", "); - sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\""); - sb.Append('}'); - - return sb.ToString(); - } + public override string ToString() => + new StringBuilder("CompatibilityEntry: {") + .Append($"{nameof(GameName)}=\"{GameName}\", ") + .Append($"{nameof(TitleId)}={TitleId}, ") + .Append($"{nameof(Labels)}={ + Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]") + }, ") + .Append($"{nameof(Status)}=\"{Status}\", ") + .Append($"{nameof(LastUpdated)}=\"{LastUpdated}\"") + .Append('}') + .ToString(); public static string FormatLabelName(string labelName) => labelName.ToLower() switch { diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml index 73ec84c53..132b10e26 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml +++ b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml @@ -34,7 +34,7 @@ Text="{ext:Locale CompatibilityListWarning}" /> - + diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs index e0d3b0c56..30d2649bc 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Ava.Utilities.Compat { public partial class CompatibilityList : UserControl { - public static async Task Show() + public static async Task Show(string titleId = null) { ContentDialog contentDialog = new() { @@ -18,7 +18,10 @@ namespace Ryujinx.Ava.Utilities.Compat CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], Content = new CompatibilityList { - DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary) + DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary), + SearchBox = { + Text = titleId ?? "" + } } };