Merge branch 'master' into master

This commit is contained in:
Vladimir Sokolov 2025-02-05 09:24:52 +10:00 committed by GitHub
commit e48b64a33b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 158 additions and 29 deletions

View File

@ -2522,6 +2522,56 @@
"zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式" "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", "ID": "GameListContextMenuOpenModsDirectory",
"Translations": { "Translations": {

View File

@ -19,6 +19,12 @@
Header="{ext:Locale GameListContextMenuCreateShortcut}" Header="{ext:Locale GameListContextMenuCreateShortcut}"
Icon="{ext:Icon fa-solid fa-bookmark}" Icon="{ext:Icon fa-solid fa-bookmark}"
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" /> ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
<MenuItem
IsVisible="{Binding HasCompatibilityEntry}"
Click="OpenApplicationCompatibility_Click"
Header="{ext:Locale GameListContextMenuShowCompatEntry}"
Icon="{ext:Icon mdi-gamepad}"
ToolTip.Tip="{ext:Locale GameListContextMenuShowCompatEntryToolTip}"/>
<Separator /> <Separator />
<MenuItem <MenuItem
Click="OpenUserSaveDirectory_Click" Click="OpenUserSaveDirectory_Click"
@ -74,7 +80,6 @@
Header="{ext:Locale GameListContextMenuTrimXCI}" Header="{ext:Locale GameListContextMenuTrimXCI}"
IsEnabled="{Binding TrimXCIEnabled}" IsEnabled="{Binding TrimXCIEnabled}"
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" /> ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
<Separator />
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}"> <MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
<MenuItem <MenuItem
Click="PurgePtcCache_Click" Click="PurgePtcCache_Click"
@ -112,6 +117,7 @@
Header="{ext:Locale GameListContextMenuExtractDataRomFS}" Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" /> ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem <MenuItem
IsVisible="{Binding HasDlc}"
Click="ExtractAocRomFs_Click" Click="ExtractAocRomFs_Click"
Header="{ext:Locale GameListContextMenuExtractDataAocRomFS}" Header="{ext:Locale GameListContextMenuExtractDataAocRomFS}"
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataAocRomFSToolTip}" /> ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataAocRomFSToolTip}" />

View File

@ -12,6 +12,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities; using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper; using Ryujinx.Common.Helper;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
@ -333,7 +334,7 @@ namespace Ryujinx.Ava.UI.Controls
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel }) if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
return; 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) if (selectedDlc is not null)
{ {
@ -381,6 +382,12 @@ namespace Ryujinx.Ava.UI.Controls
await new ArgumentsConfigWindows(viewModel).ShowDialog((Window)viewModel.TopLevel); 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) public async void RunApplication_Click(object sender, RoutedEventArgs args)
{ {

View File

@ -86,13 +86,29 @@
Text="{Binding Version}" Text="{Binding Version}"
TextAlignment="Start" TextAlignment="Start"
TextWrapping="Wrap" /> TextWrapping="Wrap" />
<TextBlock <Button
Click="PlayabilityStatus_OnClick"
HorizontalContentAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding HasPlayabilityInfo}" IsVisible="{Binding HasPlayabilityInfo}"
HorizontalAlignment="Stretch" Background="{DynamicResource AppListBackgroundColor}"
Text="{Binding LocalizedStatus}" Margin="-1, 0, 0, 0"
Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}" Padding="0" >
TextAlignment="Start" <TextBlock
TextWrapping="Wrap" /> Margin="1.5"
Tag="{Binding IdString}"
Text="{Binding LocalizedStatus}"
Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
TextAlignment="Start"
TextWrapping="Wrap" />
<Button.Styles>
<Style Selector="Button">
<Setter Property="MinWidth"
Value="0" />
<!-- avoids very wide buttons from the overall project avalonia style -->
</Style>
</Button.Styles>
</Button>
</StackPanel> </StackPanel>
</Border> </Border>
<StackPanel <StackPanel

View File

@ -5,7 +5,9 @@ using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.Compat;
using System; using System;
using System.Globalization;
using System.Linq; using System.Linq;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
@ -28,6 +30,17 @@ namespace Ryujinx.Ava.UI.Controls
if (sender is ListBox { SelectedItem: ApplicationData selected }) if (sender is ListBox { SelectedItem: ApplicationData selected })
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent)); RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
} }
private async void PlayabilityStatus_OnClick(object sender, RoutedEventArgs e)
{
if (DataContext is not MainWindowViewModel mwvm)
return;
if (sender is not Button { Content: TextBlock playabilityLabel })
return;
await CompatibilityList.Show((string)playabilityLabel.Tag);
}
private async void IdString_OnClick(object sender, RoutedEventArgs e) private async void IdString_OnClick(object sender, RoutedEventArgs e)
{ {

View File

@ -14,9 +14,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public DlcSelectViewModel(ulong titleId, ApplicationLibrary appLibrary) public DlcSelectViewModel(ulong titleId, ApplicationLibrary appLibrary)
{ {
_dlcs = appLibrary.DownloadableContents.Items _dlcs = appLibrary.FindDlcsFor(titleId)
.Where(x => x.Dlc.TitleIdBase == titleId)
.Select(x => x.Dlc)
.OrderBy(it => it.IsBundled ? 0 : 1) .OrderBy(it => it.IsBundled ? 0 : 1)
.ThenBy(it => it.TitleId) .ThenBy(it => it.TitleId)
.ToArray(); .ToArray();

View File

@ -349,6 +349,19 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool HasCompatibilityEntry
{
get
{
DynamicData.Kernel.Optional<ApplicationData> 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 OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;

View File

@ -50,7 +50,7 @@ namespace Ryujinx.Ava.UI.Views.Main
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes); UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show); XciTrimmerMenuItem.Command = Commands.Create(XCITrimmerWindow.Show);
AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show); AboutWindowMenuItem.Command = Commands.Create(AboutWindow.Show);
CompatibilityListMenuItem.Command = Commands.Create(CompatibilityList.Show); CompatibilityListMenuItem.Command = Commands.Create(() => CompatibilityList.Show());
UpdateMenuItem.Command = Commands.Create(async () => UpdateMenuItem.Command = Commands.Create(async () =>
{ {

View File

@ -135,6 +135,32 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
return id.ToString("X16"); return id.ToString("X16");
} }
public bool FindApplication(ulong id, out ApplicationData foundData)
{
DynamicData.Kernel.Optional<ApplicationData> appData = Applications.Lookup(id);
foundData = appData.HasValue ? appData.Value : null;
return appData.HasValue;
}
public bool FindUpdate(ulong id, out TitleUpdateModel foundData)
{
Gommon.Optional<TitleUpdateModel> 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));
/// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception> /// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception> /// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception> /// <exception cref="NotSupportedException">The NCA version is not supported.</exception>

View File

@ -113,20 +113,17 @@ namespace Ryujinx.Ava.Utilities.Compat
.Select(FormatLabelName) .Select(FormatLabelName)
.JoinToString(", "); .JoinToString(", ");
public override string ToString() public override string ToString() =>
{ new StringBuilder("CompatibilityEntry: {")
StringBuilder sb = new("CompatibilityEntry: {"); .Append($"{nameof(GameName)}=\"{GameName}\", ")
sb.Append($"{nameof(GameName)}=\"{GameName}\", "); .Append($"{nameof(TitleId)}={TitleId}, ")
sb.Append($"{nameof(TitleId)}={TitleId}, "); .Append($"{nameof(Labels)}={
sb.Append($"{nameof(Labels)}={ Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]")
Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]") }, ")
}, "); .Append($"{nameof(Status)}=\"{Status}\", ")
sb.Append($"{nameof(Status)}=\"{Status}\", "); .Append($"{nameof(LastUpdated)}=\"{LastUpdated}\"")
sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\""); .Append('}')
sb.Append('}'); .ToString();
return sb.ToString();
}
public static string FormatLabelName(string labelName) => labelName.ToLower() switch public static string FormatLabelName(string labelName) => labelName.ToLower() switch
{ {

View File

@ -34,7 +34,7 @@
Text="{ext:Locale CompatibilityListWarning}" /> Text="{ext:Locale CompatibilityListWarning}" />
</Grid> </Grid>
<Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto"> <Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
<TextBox Grid.Column="0" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" /> <TextBox Name="SearchBox" Grid.Column="0" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
<CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" /> <CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
<TextBlock Grid.Column="2" Margin="-10, 0, 0, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" /> <TextBlock Grid.Column="2" Margin="-10, 0, 0, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
</Grid> </Grid>

View File

@ -9,7 +9,7 @@ namespace Ryujinx.Ava.Utilities.Compat
{ {
public partial class CompatibilityList : UserControl public partial class CompatibilityList : UserControl
{ {
public static async Task Show() public static async Task Show(string titleId = null)
{ {
ContentDialog contentDialog = new() ContentDialog contentDialog = new()
{ {
@ -18,7 +18,10 @@ namespace Ryujinx.Ava.Utilities.Compat
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
Content = new CompatibilityList Content = new CompatibilityList
{ {
DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary) DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary),
SearchBox = {
Text = titleId ?? ""
}
} }
}; };