UI: Move DLC RomFS dumping under normal RomFS dumping.

Also removed it from DLC manager.
This commit is contained in:
Evan Husted 2025-01-20 14:30:28 -06:00 committed by KeatonTheBot
parent c3bfce3378
commit 2471bb8ede
13 changed files with 202 additions and 39 deletions

View file

@ -6,7 +6,7 @@ namespace Ryujinx.UI.Common.Models
public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci"; public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci";
public string FileName => System.IO.Path.GetFileName(ContainerPath); public string FileName => System.IO.Path.GetFileName(ContainerPath);
public string TitleIdStr => TitleId.ToString("x16"); public string TitleIdStr => TitleId.ToString("x16").ToUpper();
public ulong TitleIdBase => TitleId & ~0x1FFFUL; public ulong TitleIdBase => TitleId & ~0x1FFFUL;
} }
} }

View file

@ -913,5 +913,8 @@
"FpsTurboStatusBarText": "{0} FPS ({1}ms), Turbo ({2}%)", "FpsTurboStatusBarText": "{0} FPS ({1}ms), Turbo ({2}%)",
"SettingsTabHotkeysTurboMode": "Turbo mode:", "SettingsTabHotkeysTurboMode": "Turbo mode:",
"SettingsTabHotkeysTurboModeToolTip": "The Turbo mode hotkey.\\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\\n\\nLeave Unbound if unsure.", "SettingsTabHotkeysTurboModeToolTip": "The Turbo mode hotkey.\\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\\n\\nLeave Unbound if unsure.",
"SettingsTabHotkeysOnlyWhilePressed": "Only while pressed" "SettingsTabHotkeysOnlyWhilePressed": "Only while pressed",
"GameListContextMenuExtractDataAocRomFS": "DLC RomFS",
"GameListContextMenuExtractDataAocRomFSToolTip": "Extract the RomFS from a selected DLC file",
"ExtractAocListHeader": "Select a DLC to Extract"
} }

View file

@ -821,5 +821,8 @@
"FpsStatusBarText": "{0} FPS ({1}ms)", "FpsStatusBarText": "{0} FPS ({1}ms)",
"SettingsTabHotkeysTurboMode": "Mode Turbo :", "SettingsTabHotkeysTurboMode": "Mode Turbo :",
"SettingsTabHotkeysTurboModeToolTip": "Le raccourci clavier Mode Turbo.\\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\\n\\nLaisser Non Attribuée si incertain.", "SettingsTabHotkeysTurboModeToolTip": "Le raccourci clavier Mode Turbo.\\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\\n\\nLaisser Non Attribuée si incertain.",
"SettingsTabHotkeysOnlyWhilePressed": "Seulement quand le raccourci est maintenu" "SettingsTabHotkeysOnlyWhilePressed": "Seulement quand le raccourci est maintenu",
"GameListContextMenuExtractDataAocRomFS": "RomFS de DLC",
"GameListContextMenuExtractDataAocRomFSToolTip": "Extraire les RomFS d'un fichier DLC choisi",
"ExtractAocListHeader": "Choisissez un DLC à extraire"
} }

View file

@ -297,13 +297,13 @@ namespace Ryujinx.Ava.Common
extractorThread.Start(); extractorThread.Start();
} }
public static void ExtractAoc(string destination, NcaSectionType ncaSectionType, string updateFilePath, string updateName) public static void ExtractAoc(string destination, string updateFilePath, string updateName)
{ {
var cancellationToken = new CancellationTokenSource(); var cancellationToken = new CancellationTokenSource();
UpdateWaitWindow waitingDialog = new( UpdateWaitWindow waitingDialog = new(
$"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]}", $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]}",
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(updateFilePath)), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, NcaSectionType.Data, Path.GetFileName(updateFilePath)),
cancellationToken); cancellationToken);
Thread extractorThread = new(() => Thread extractorThread = new(() =>
@ -353,7 +353,7 @@ namespace Ryujinx.Ava.Common
? IntegrityCheckLevel.ErrorOnInvalid ? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None; : IntegrityCheckLevel.None;
int index = Nca.GetSectionIndexFromType(ncaSectionType, publicDataNca.Header.ContentType); int index = Nca.GetSectionIndexFromType(NcaSectionType.Data, publicDataNca.Header.ContentType);
try try
{ {
@ -411,14 +411,13 @@ namespace Ryujinx.Ava.Common
} }
}) })
{ {
Name = "GUI.NcaSectionExtractorThread", Name = "GUI.AocExtractorThread",
IsBackground = true, IsBackground = true,
}; };
extractorThread.Start(); extractorThread.Start();
} }
public static async Task ExtractAoc(IStorageProvider storageProvider, NcaSectionType ncaSectionType, public static async Task ExtractAoc(IStorageProvider storageProvider, string updateFilePath, string updateName)
string updateFilePath, string updateName)
{ {
var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions var result = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{ {
@ -431,7 +430,7 @@ namespace Ryujinx.Ava.Common
return; return;
} }
ExtractAoc(result[0].Path.LocalPath, ncaSectionType, updateFilePath, updateName); ExtractAoc(result[0].Path.LocalPath, updateFilePath, updateName);
} }

View file

@ -97,6 +97,10 @@
Click="ExtractApplicationRomFs_Click" Click="ExtractApplicationRomFs_Click"
Header="{locale:Locale GameListContextMenuExtractDataRomFS}" Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
Click="ExtractAocRomFs_Click"
Header="{locale:Locale GameListContextMenuExtractDataAocRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataAocRomFSToolTip}" />
<MenuItem <MenuItem
Click="ExtractApplicationLogo_Click" Click="ExtractApplicationLogo_Click"
Header="{locale:Locale GameListContextMenuExtractDataLogo}" Header="{locale:Locale GameListContextMenuExtractDataLogo}"

View file

@ -13,6 +13,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.UI.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -371,6 +372,22 @@ namespace Ryujinx.Ava.UI.Controls
viewModel.SelectedApplication.Name); viewModel.SelectedApplication.Name);
} }
} }
public async void ExtractAocRomFs_Click(object sender, RoutedEventArgs args)
{
if (sender is not MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
return;
DownloadableContentModel selectedDlc = await DlcSelectView.Show(viewModel.SelectedApplication.IdBase, viewModel.ApplicationLibrary);
if (selectedDlc is not null)
{
await ApplicationHelper.ExtractAoc(
viewModel.StorageProvider,
selectedDlc.ContainerPath,
selectedDlc.FileName);
}
}
public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args) public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args)
{ {

View file

@ -4,8 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
Focusable="True" Focusable="True"

View file

@ -0,0 +1,67 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:helpers="using:Ryujinx.Ava.UI.Helpers"
xmlns:viewModels="using:Ryujinx.Ava.UI.ViewModels"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Controls.DlcSelectView"
x:DataType="viewModels:DlcSelectViewModel">
<Grid RowDefinitions="*,Auto,*">
<TextBlock
Classes="h1"
Margin="5, -5, 0, 15"
HorizontalAlignment="Center"
VerticalAlignment="Center"
TextWrapping="Wrap"
Text="{locale:Locale ExtractAocListHeader}" />
<ScrollViewer Grid.Row="2">
<ListBox
AutoScrollToSelectedItem="False"
SelectionMode="Single"
Background="Transparent"
SelectedItem="{Binding SelectedDlc}"
ItemsSource="{Binding Dlcs}">
<ListBox.DataTemplates>
<DataTemplate
DataType="models:DownloadableContentModel">
<Panel Margin="10" Background="Transparent">
<Grid ColumnDefinitions="*,Auto">
<Grid
Grid.Column="0" ColumnDefinitions="*,Auto">
<TextBlock
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
MaxLines="2"
TextWrapping="Wrap"
TextTrimming="CharacterEllipsis">
<TextBlock.Text>
<MultiBinding Converter="{x:Static helpers:DownloadableContentLabelConverter.Instance}">
<Binding Path="FileName" />
<Binding Path="IsBundled" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock
Grid.Column="1"
Margin="10, 0, 0, 0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding TitleIdStr}" />
</Grid>
</Grid>
</Panel>
</DataTemplate>
</ListBox.DataTemplates>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Background" Value="Transparent" />
</Style>
</ListBox.Styles>
</ListBox>
</ScrollViewer>
</Grid>
</UserControl>

View file

@ -0,0 +1,50 @@
using Avalonia.Controls;
using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Models;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Controls
{
public partial class DlcSelectView : UserControl
{
public DlcSelectView()
{
InitializeComponent();
}
#nullable enable
public static async Task<DownloadableContentModel?> Show(ulong selectedTitleId, ApplicationLibrary appLibrary)
#nullable disable
{
DlcSelectViewModel viewModel = new(selectedTitleId, appLibrary);
ContentDialog contentDialog = new()
{
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.Continue],
SecondaryButtonText = string.Empty,
CloseButtonText = string.Empty,
Content = new DlcSelectView { DataContext = viewModel }
};
Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty,
Avalonia.Layout.HorizontalAlignment.Right));
contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent);
await ContentDialogHelper.ShowAsync(contentDialog);
return viewModel.SelectedDlc;
}
}
}

View file

@ -73,7 +73,7 @@ namespace Ryujinx.Ava.UI.Controls
public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager, public static async Task Show(AccountManager ownerAccountManager, ContentManager ownerContentManager,
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient) VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
{ {
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); NavigationDialogHost content = new(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
ContentDialog contentDialog = new() ContentDialog contentDialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],

View file

@ -0,0 +1,45 @@
using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Models;
using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels
{
public partial class DlcSelectViewModel : BaseModel
{
private DownloadableContentModel[] _dlcs;
private DownloadableContentModel _selectedDlc;
public DownloadableContentModel[] Dlcs
{
get => _dlcs;
set
{
_dlcs = value;
OnPropertyChanged();
}
}
#nullable enable
public DownloadableContentModel? SelectedDlc
{
get => _selectedDlc;
set
{
_selectedDlc = value;
OnPropertyChanged();
}
}
#nullable disable
public DlcSelectViewModel(ulong titleId, ApplicationLibrary appLibrary)
{
_dlcs = appLibrary.DownloadableContents.Items
.Where(x => x.Dlc.TitleIdBase == titleId)
.Select(x => x.Dlc)
.OrderBy(it => it.IsBundled ? 0 : 1)
.ThenBy(it => it.TitleId)
.ToArray();
}
}
}

View file

@ -14,9 +14,6 @@
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:DownloadableContentManagerViewModel" x:DataType="viewModels:DownloadableContentManagerViewModel"
Focusable="True"> Focusable="True">
<UserControl.Resources>
<helpers:DownloadableContentLabelConverter x:Key="DownloadableContentLabel" />
</UserControl.Resources>
<Grid RowDefinitions="Auto,Auto,*,Auto"> <Grid RowDefinitions="Auto,Auto,*,Auto">
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
@ -102,7 +99,7 @@
TextWrapping="Wrap" TextWrapping="Wrap"
TextTrimming="CharacterEllipsis"> TextTrimming="CharacterEllipsis">
<TextBlock.Text> <TextBlock.Text>
<MultiBinding Converter="{StaticResource DownloadableContentLabel}"> <MultiBinding Converter="{x:Static helpers:DownloadableContentLabelConverter.Instance}">
<Binding Path="FileName" /> <Binding Path="FileName" />
<Binding Path="IsBundled" /> <Binding Path="IsBundled" />
</MultiBinding> </MultiBinding>
@ -113,22 +110,13 @@
Margin="10 0" Margin="10 0"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{Binding TitleId}" /> Text="{Binding TitleIdStr}" />
</Grid> </Grid>
<StackPanel <StackPanel
Grid.Column="1" Grid.Column="1"
Spacing="10" Spacing="10"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right"> HorizontalAlignment="Right">
<Button
VerticalAlignment="Center"
HorizontalAlignment="Right"
Padding="10"
MinWidth="0"
MinHeight="0"
Click="DlcItem_DumpRomfs">
<ui:SymbolIcon Symbol="MoveToFolder"/>
</Button>
<Button <Button
VerticalAlignment="Center" VerticalAlignment="Center"
HorizontalAlignment="Right" HorizontalAlignment="Right"

View file

@ -101,18 +101,5 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
} }
private async void DlcItem_DumpRomfs(object sender, RoutedEventArgs e)
{
if (sender is not Button { DataContext: DownloadableContentModel dlc }) return;
if (App.MainWindow.ViewModel is not { } viewModel)
return;
await ApplicationHelper.ExtractAoc(
viewModel.StorageProvider,
NcaSectionType.Data,
dlc.ContainerPath,
dlc.FileName);
}
} }
} }