Compare commits

...

4 Commits

Author SHA1 Message Date
rna0
952d013c67 Avalonia changes (#3497)
Co-authored-by: RNA <wQSZSQS2UQf5zun>
2022-07-29 01:14:37 +00:00
Ac_K
46c8129bf5 Avalonia: Another Cleanup (#3494)
* Avalonia: Another Cleanup

This PR is a cleanup to the avalonia code recently added:

- Some XAML file are autoformatted like a previous PR.
- Dlc is renamed to DownloadableContent (Locale exclude).
- DownloadableContentManagerWindow is a bit improved (Fixes #3491).
- Some nits here and there.

* Fix GTK

* Remove AttachDebugDevTools

* Fix last warning

* Fix JSON fields
2022-07-29 00:41:34 +02:00
Ac_K
8cfec5de4b Avalonia: Cleanup UserEditor a bit (#3492)
This PR cleanup the UserEditor code a bit, 2 texts are added for "Name" and "User Id", because when you create a new profile, the textbox is empty without any hints. `axaml` files are autoformated too.
2022-07-28 14:16:23 -03:00
gdkchan
37b6e081da Fix DMA linear texture copy fast path (#3496)
* Fix DMA linear texture copy fast path

* Formatting
2022-07-28 13:46:12 -03:00
50 changed files with 610 additions and 595 deletions

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " - Können Fehler verursachen", "SettingsTabSystemHacksNote": " (Können Fehler verursachen)",
"SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GB", "SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GB",
"SettingsTabSystemIgnoreMissingServices": "Ignoriere fehlende Dienste", "SettingsTabSystemIgnoreMissingServices": "Ignoriere fehlende Dienste",
"SettingsTabGraphics": "Grafik", "SettingsTabGraphics": "Grafik",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Μικροδιορθώσεις", "SettingsTabSystemHacks": "Μικροδιορθώσεις",
"SettingsTabSystemHacksNote": " - Μπορεί να προκαλέσουν αστάθεια", "SettingsTabSystemHacksNote": " (Μπορεί να προκαλέσουν αστάθεια)",
"SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GB", "SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GB",
"SettingsTabSystemIgnoreMissingServices": "Αγνόηση υπηρεσιών που λείπουν", "SettingsTabSystemIgnoreMissingServices": "Αγνόηση υπηρεσιών που λείπουν",
"SettingsTabGraphics": "Γραφικά", "SettingsTabGraphics": "Γραφικά",

View File

@@ -577,5 +577,7 @@
"UserProfileNoImageError": "Profile image must be set", "UserProfileNoImageError": "Profile image must be set",
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]", "GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:" "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
"UserProfilesName": "Name:",
"UserProfilesUserId" : "User Id:"
} }

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " - Pueden causar inestabilidad", "SettingsTabSystemHacksNote": " (Pueden causar inestabilidad)",
"SettingsTabSystemExpandDramSize": "Expandir DRAM a 6GB", "SettingsTabSystemExpandDramSize": "Expandir DRAM a 6GB",
"SettingsTabSystemIgnoreMissingServices": "Ignorar servicios no implementados", "SettingsTabSystemIgnoreMissingServices": "Ignorar servicios no implementados",
"SettingsTabGraphics": "Gráficos", "SettingsTabGraphics": "Gráficos",

View File

@@ -111,7 +111,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " - Cela peut causer des instabilitées", "SettingsTabSystemHacksNote": " (Cela peut causer des instabilitées)",
"SettingsTabSystemExpandDramSize": "Augmenter la taille de la DRAM à 6GB", "SettingsTabSystemExpandDramSize": "Augmenter la taille de la DRAM à 6GB",
"SettingsTabSystemIgnoreMissingServices": "Ignorer les services manquant", "SettingsTabSystemIgnoreMissingServices": "Ignorer les services manquant",
"SettingsTabGraphics": "Graphique", "SettingsTabGraphics": "Graphique",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " - Possono causare instabilità", "SettingsTabSystemHacksNote": " (Possono causare instabilità)",
"SettingsTabSystemExpandDramSize": "Espandi dimensione DRAM a 6GB", "SettingsTabSystemExpandDramSize": "Espandi dimensione DRAM a 6GB",
"SettingsTabSystemIgnoreMissingServices": "Ignora servizi mancanti", "SettingsTabSystemIgnoreMissingServices": "Ignora servizi mancanti",
"SettingsTabGraphics": "Grafica", "SettingsTabGraphics": "Grafica",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "해킹", "SettingsTabSystemHacks": "해킹",
"SettingsTabSystemHacksNote": " - 불안정을 일으킬 수 있음", "SettingsTabSystemHacksNote": " (불안정을 일으킬 수 있음)",
"SettingsTabSystemExpandDramSize": "DRAM 크기를 6GB로 확장", "SettingsTabSystemExpandDramSize": "DRAM 크기를 6GB로 확장",
"SettingsTabSystemIgnoreMissingServices": "누락된 서비스 무시", "SettingsTabSystemIgnoreMissingServices": "누락된 서비스 무시",
"SettingsTabGraphics": "제도법", "SettingsTabGraphics": "제도법",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " - Pode causar instabilidade", "SettingsTabSystemHacksNote": " (Pode causar instabilidade)",
"SettingsTabSystemExpandDramSize": "Expandir memória para 6GB", "SettingsTabSystemExpandDramSize": "Expandir memória para 6GB",
"SettingsTabSystemIgnoreMissingServices": "Ignorar serviços não implementados", "SettingsTabSystemIgnoreMissingServices": "Ignorar serviços não implementados",
"SettingsTabGraphics": "Gráficos", "SettingsTabGraphics": "Gráficos",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Хаки", "SettingsTabSystemHacks": "Хаки",
"SettingsTabSystemHacksNote": " - Эти многие настройки вызывают нестабильность", "SettingsTabSystemHacksNote": " (Эти многие настройки вызывают нестабильность)",
"SettingsTabSystemExpandDramSize": "Увеличение размера DRAM до 6GB", "SettingsTabSystemExpandDramSize": "Увеличение размера DRAM до 6GB",
"SettingsTabSystemIgnoreMissingServices": "Игнорировать отсутствующие службы", "SettingsTabSystemIgnoreMissingServices": "Игнорировать отсутствующие службы",
"SettingsTabGraphics": "Графика", "SettingsTabGraphics": "Графика",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacklar", "SettingsTabSystemHacks": "Hacklar",
"SettingsTabSystemHacksNote": " - Bunlar birçok dengesizlik oluşturabilir", "SettingsTabSystemHacksNote": " (Bunlar birçok dengesizlik oluşturabilir)",
"SettingsTabSystemExpandDramSize": "DRAM boyutunu 6GB'a genişlet", "SettingsTabSystemExpandDramSize": "DRAM boyutunu 6GB'a genişlet",
"SettingsTabSystemIgnoreMissingServices": "Eksik Servisleri Görmezden Gel", "SettingsTabSystemIgnoreMissingServices": "Eksik Servisleri Görmezden Gel",
"SettingsTabGraphics": "Grafikler", "SettingsTabGraphics": "Grafikler",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "修正", "SettingsTabSystemHacks": "修正",
"SettingsTabSystemHacksNote": " - 会引起模拟器不稳定", "SettingsTabSystemHacksNote": " (会引起模拟器不稳定)",
"SettingsTabSystemExpandDramSize": "将模拟RAM大小扩展到 6GB", "SettingsTabSystemExpandDramSize": "将模拟RAM大小扩展到 6GB",
"SettingsTabSystemIgnoreMissingServices": "忽略缺少的服务", "SettingsTabSystemIgnoreMissingServices": "忽略缺少的服务",
"SettingsTabGraphics": "图像", "SettingsTabGraphics": "图像",

View File

@@ -118,7 +118,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "修正", "SettingsTabSystemHacks": "修正",
"SettingsTabSystemHacksNote": " - 會引起模擬器不穩定", "SettingsTabSystemHacksNote": " (會引起模擬器不穩定)",
"SettingsTabSystemExpandDramSize": "將模擬記憶體大小擴充至 6GB", "SettingsTabSystemExpandDramSize": "將模擬記憶體大小擴充至 6GB",
"SettingsTabSystemIgnoreMissingServices": "忽略缺少的服務", "SettingsTabSystemIgnoreMissingServices": "忽略缺少的服務",
"SettingsTabGraphics": "圖形", "SettingsTabGraphics": "圖形",

View File

@@ -37,7 +37,7 @@
Header="{locale:Locale GameListContextMenuManageTitleUpdates}" Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDlcManager}" Command="{Binding OpenDownloadableContentManager}"
Header="{locale:Locale GameListContextMenuManageDlc}" Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem <MenuItem

View File

@@ -37,7 +37,7 @@
Header="{locale:Locale GameListContextMenuManageTitleUpdates}" Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDlcManager}" Command="{Binding OpenDownloadableContentManager}"
Header="{locale:Locale GameListContextMenuManageDlc}" Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem <MenuItem

View File

@@ -17,9 +17,6 @@ namespace Ryujinx.Ava.Ui.Controls
public UpdateWaitWindow() public UpdateWaitWindow()
{ {
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
} }
} }
} }

View File

@@ -1,55 +1,87 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Ryujinx.Ava.Ui.Controls.UserEditor"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns="https://github.com/avaloniaui"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
Padding="0" xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
Margin="0" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels" xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls" xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" Margin="0"
x:Class="Ryujinx.Ava.Ui.Controls.UserEditor"> Padding="0"
mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="0"> <Grid Margin="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto" />
<ColumnDefinition/> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*" />
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel Orientation="Vertical" VerticalAlignment="Stretch" HorizontalAlignment="Left"> <StackPanel
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Orientation="Vertical">
<Image <Image
Name="ProfileImage"
Width="96"
Height="96"
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Height="96" Width="96"
Name="ProfileImage"
Source="{Binding Image, Converter={StaticResource ByteImage}}" /> Source="{Binding Image, Converter={StaticResource ByteImage}}" />
<Button Margin="5" Content="{Locale:Locale UserProfilesChangeProfileImage}" <Button
Name="ChangePictureButton" Name="ChangePictureButton"
Click="ChangePictureButton_Click" Margin="5"
HorizontalAlignment="Stretch"/> HorizontalAlignment="Stretch"
<Button Margin="5" Content="{Locale:Locale UserProfilesSetProfileImage}" Click="ChangePictureButton_Click"
Name="AddPictureButton" Content="{Locale:Locale UserProfilesChangeProfileImage}" />
Click="ChangePictureButton_Click" <Button
HorizontalAlignment="Stretch"/> Name="AddPictureButton"
Margin="5"
HorizontalAlignment="Stretch"
Click="ChangePictureButton_Click"
Content="{Locale:Locale UserProfilesSetProfileImage}" />
</StackPanel> </StackPanel>
<StackPanel Grid.Row="0" Orientation="Vertical" HorizontalAlignment="Stretch" Grid.Column="1" Spacing="10" <StackPanel
Margin="5, 10"> Grid.Row="0"
<TextBox Name="NameBox" Width="300" Text="{Binding Name}" MaxLength="{Binding MaxProfileNameLength}" Grid.Column="1"
HorizontalAlignment="Stretch" /> Margin="5,10"
<TextBlock Text="{Binding UserId}" Name="IdLabel" /> HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Text="{Locale:Locale UserProfilesName}" />
<TextBox
Name="NameBox"
Width="300"
HorizontalAlignment="Stretch"
MaxLength="{Binding MaxProfileNameLength}"
Text="{Binding Name}" />
<TextBlock Text="{Locale:Locale UserProfilesUserId}" />
<TextBlock Name="IdLabel" Text="{Binding UserId}" />
</StackPanel> </StackPanel>
<StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Orientation="Horizontal" Spacing="10" HorizontalAlignment="Right"> <StackPanel
<Button Content="{Locale:Locale Save}" Name="SaveButton" Click="SaveButton_Click"/> Grid.Row="1"
<Button HorizontalAlignment="Right" Content="{Locale:Locale Discard}" Grid.Column="0"
Name="CloseButton" Click="CloseButton_Click"/> Grid.ColumnSpan="2"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="10">
<Button
Name="SaveButton"
Click="SaveButton_Click"
Content="{Locale:Locale Save}" />
<Button
Name="CloseButton"
HorizontalAlignment="Right"
Click="CloseButton_Click"
Content="{Locale:Locale Discard}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Ui.Controls
_parent?.GoBack(); _parent?.GoBack();
} }
private void SaveButton_Click(object sender, RoutedEventArgs e) private async void SaveButton_Click(object sender, RoutedEventArgs e)
{ {
DataValidationErrors.ClearErrors(NameBox); DataValidationErrors.ClearErrors(NameBox);
bool isInvalid = false; bool isInvalid = false;
@@ -77,7 +77,7 @@ namespace Ryujinx.Ava.Ui.Controls
if (TempProfile.Image == null) if (TempProfile.Image == null)
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], ""); await ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance["UserProfileNoImageError"], "");
isInvalid = true; isInvalid = true;
} }

View File

@@ -1,29 +1,35 @@
<UserControl xmlns="https://github.com/avaloniaui" <UserControl
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Ryujinx.Ava.Ui.Controls.UserSelector"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns="https://github.com/avaloniaui"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox" xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls" xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
x:Class="Ryujinx.Ava.Ui.Controls.UserSelector"> xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" /> <controls:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>
<Design.DataContext> <Design.DataContext>
<viewModels:UserProfileViewModel /> <viewModels:UserProfileViewModel />
</Design.DataContext> </Design.DataContext>
<Grid HorizontalAlignment="Stretch" <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition /> <RowDefinition />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5" Items="{Binding Profiles}" <ListBox
DoubleTapped="ProfilesList_DoubleTapped" Margin="5"
SelectionChanged="SelectingItemsControl_SelectionChanged"> HorizontalAlignment="Stretch"
VerticalAlignment="Center"
DoubleTapped="ProfilesList_DoubleTapped"
Items="{Binding Profiles}"
SelectionChanged="SelectingItemsControl_SelectionChanged">
<ListBox.ItemsPanel> <ListBox.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<flex:FlexPanel <flex:FlexPanel
@@ -49,10 +55,11 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image <Image
Grid.Row="0" Grid.Row="0"
Width="96"
Height="96"
Margin="0" Margin="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Top" VerticalAlignment="Top"
Height="96" Width="96"
Source="{Binding Image, Converter={StaticResource ByteImage}}" /> Source="{Binding Image, Converter={StaticResource ByteImage}}" />
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
@@ -68,23 +75,34 @@
</StackPanel> </StackPanel>
</Grid> </Grid>
</Border> </Border>
<Border HorizontalAlignment="Left" VerticalAlignment="Top" <Border
IsVisible="{Binding IsOpened}" Width="10"
Background="LimeGreen" Height="10"
Width="10" Margin="5"
Height="10" HorizontalAlignment="Left"
Margin="5" VerticalAlignment="Top"
CornerRadius="5" /> Background="LimeGreen"
CornerRadius="5"
IsVisible="{Binding IsOpened}" />
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0" Spacing="10" HorizontalAlignment="Center"> <StackPanel
<Button Content="{Locale:Locale UserProfilesAddNewProfile}" Command="{Binding AddUser}" /> Grid.Row="1"
<Button IsEnabled="{Binding IsSelectedProfiledEditable}" Margin="10,0"
Content="{Locale:Locale UserProfilesEditProfile}" Command="{Binding EditUser}" /> HorizontalAlignment="Center"
<Button IsEnabled="{Binding IsSelectedProfileDeletable}" Orientation="Horizontal"
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}" Command="{Binding DeleteUser}" /> Spacing="10">
<Button Command="{Binding AddUser}" Content="{Locale:Locale UserProfilesAddNewProfile}" />
<Button
Command="{Binding EditUser}"
Content="{Locale:Locale UserProfilesEditProfile}"
IsEnabled="{Binding IsSelectedProfiledEditable}" />
<Button
Command="{Binding DeleteUser}"
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}"
IsEnabled="{Binding IsSelectedProfileDeletable}" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Ui.Controls
AddHandler(Frame.NavigatedToEvent, (s, e) => AddHandler(Frame.NavigatedToEvent, (s, e) =>
{ {
NavigatedTo(e); NavigatedTo(e);
}, Avalonia.Interactivity.RoutingStrategies.Direct); }, RoutingStrategies.Direct);
} }
} }
@@ -29,12 +29,10 @@ namespace Ryujinx.Ava.Ui.Controls
{ {
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
switch (arg.NavigationMode) if (arg.NavigationMode == NavigationMode.New)
{ {
case NavigationMode.New: _parent = (NavigationDialogHost)arg.Parameter;
_parent = (NavigationDialogHost)arg.Parameter; ViewModel = _parent.ViewModel;
ViewModel = _parent.ViewModel;
break;
} }
DataContext = ViewModel; DataContext = ViewModel;

View File

@@ -11,8 +11,8 @@ namespace Ryujinx.Ava.Ui.Models
public CheatModel(string name, string buildId, bool isEnabled) public CheatModel(string name, string buildId, bool isEnabled)
{ {
Name = name; Name = name;
BuildId = buildId; BuildId = buildId;
IsEnabled = isEnabled; IsEnabled = isEnabled;
} }
@@ -22,7 +22,9 @@ namespace Ryujinx.Ava.Ui.Models
set set
{ {
_isEnabled = value; _isEnabled = value;
EnableToggled?.Invoke(this, _isEnabled); EnableToggled?.Invoke(this, _isEnabled);
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -30,6 +32,7 @@ namespace Ryujinx.Ava.Ui.Models
public string BuildId { get; } public string BuildId { get; }
public string BuildIdKey => $"{BuildId}-{Name}"; public string BuildIdKey => $"{BuildId}-{Name}";
public string Name { get; } public string Name { get; }
public string CleanName => Name.Substring(1, Name.Length - 8); public string CleanName => Name.Substring(1, Name.Length - 8);

View File

@@ -10,26 +10,13 @@ namespace Ryujinx.Ava.Ui.Models
public CheatsList(string buildId, string path) public CheatsList(string buildId, string path)
{ {
BuildId = buildId; BuildId = buildId;
Path = path; Path = path;
CollectionChanged += CheatsList_CollectionChanged; CollectionChanged += CheatsList_CollectionChanged;
} }
private void CheatsList_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
(e.NewItems[0] as CheatModel).EnableToggled += Item_EnableToggled;
}
}
private void Item_EnableToggled(object sender, bool e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
}
public string BuildId { get; } public string BuildId { get; }
public string Path { get; } public string Path { get; }
public bool IsEnabled public bool IsEnabled
{ {
@@ -47,5 +34,18 @@ namespace Ryujinx.Ava.Ui.Models
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled))); OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
} }
} }
private void CheatsList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
(e.NewItems[0] as CheatModel).EnableToggled += Item_EnableToggled;
}
}
private void Item_EnableToggled(object sender, bool e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsEnabled)));
}
} }
} }

View File

@@ -1,18 +0,0 @@
namespace Ryujinx.Ava.Ui.Models
{
public class DlcModel
{
public bool IsEnabled { get; set; }
public string TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
public DlcModel(string titleId, string containerPath, string fullPath, bool isEnabled)
{
TitleId = titleId;
ContainerPath = containerPath;
FullPath = fullPath;
IsEnabled = isEnabled;
}
}
}

View File

@@ -0,0 +1,18 @@
namespace Ryujinx.Ava.Ui.Models
{
public class DownloadableContentModel
{
public bool Enabled { get; set; }
public string TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
{
TitleId = titleId;
ContainerPath = containerPath;
FullPath = fullPath;
Enabled = enabled;
}
}
}

View File

@@ -382,9 +382,9 @@ namespace Ryujinx.Ava.Ui.ViewModels
{ {
string amiiboJsonString = await response.Content.ReadAsStringAsync(); string amiiboJsonString = await response.Content.ReadAsStringAsync();
using (FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough)) using (FileStream amiiboJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough))
{ {
dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString)); amiiboJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString));
} }
return amiiboJsonString; return amiiboJsonString;

View File

@@ -1261,15 +1261,15 @@ namespace Ryujinx.Ava.Ui.ViewModels
} }
} }
public async void OpenDlcManager() public async void OpenDownloadableContentManager()
{ {
var selection = SelectedApplication; var selection = SelectedApplication;
if (selection != null) if (selection != null)
{ {
DlcManagerWindow dlcManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName); DownloadableContentManagerWindow downloadableContentManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName);
await dlcManager.ShowDialog(_owner); await downloadableContentManager.ShowDialog(_owner);
} }
} }

View File

@@ -43,11 +43,9 @@ namespace Ryujinx.Ava.Ui.ViewModels
} }
} }
public bool IsHighlightedProfileEditable => public bool IsHighlightedProfileEditable => _highlightedProfile != null;
_highlightedProfile != null;
public bool IsHighlightedProfileDeletable => public bool IsHighlightedProfileDeletable => _highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
_highlightedProfile != null && _highlightedProfile.UserId != AccountManager.DefaultUserId;
public UserProfile HighlightedProfile public UserProfile HighlightedProfile
{ {
@@ -62,16 +60,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
} }
} }
public void Dispose() public void Dispose() { }
{
}
public void LoadProfiles() public void LoadProfiles()
{ {
Profiles.Clear(); Profiles.Clear();
var profiles = _owner.AccountManager.GetAllUsers() var profiles = _owner.AccountManager.GetAllUsers().OrderByDescending(x => x.AccountState == AccountState.Open);
.OrderByDescending(x => x.AccountState == AccountState.Open);
foreach (var profile in profiles) foreach (var profile in profiles)
{ {
@@ -94,6 +89,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void AddUser() public void AddUser()
{ {
UserProfile userProfile = null; UserProfile userProfile = null;
_owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true)); _owner.Navigate(typeof(UserEditor), (this._owner, userProfile, true));
} }

View File

@@ -2,7 +2,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@@ -27,9 +26,6 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
_ = DownloadPatronsJson(); _ = DownloadPatronsJson();
} }

View File

@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels; using Ryujinx.Ava.Ui.ViewModels;
@@ -18,9 +17,7 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"];
} }
@@ -31,9 +28,7 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = ViewModel; DataContext = ViewModel;
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["Amiibo"];

View File

@@ -1,21 +1,24 @@
<window:StyleableWindow x:Class="Ryujinx.Ava.Ui.Windows.CheatWindow" <window:StyleableWindow
xmlns="https://github.com/avaloniaui" x:Class="Ryujinx.Ava.Ui.Windows.CheatWindow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="https://github.com/avaloniaui"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:model="clr-namespace:Ryujinx.Ava.Ui.Models" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows" xmlns:model="clr-namespace:Ryujinx.Ava.Ui.Models"
mc:Ignorable="d" xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Width="500" MinHeight="500" Height="500" Width="500"
WindowStartupLocation="CenterOwner" Height="500"
MinWidth="500"> MinWidth="500"
MinHeight="500"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<Window.Styles> <Window.Styles>
<Style Selector="TreeViewItem"> <Style Selector="TreeViewItem">
<Setter Property="IsExpanded" Value="True" /> <Setter Property="IsExpanded" Value="True" />
</Style> </Style>
</Window.Styles> </Window.Styles>
<Grid Name="DlcGrid" Margin="15"> <Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -24,14 +27,14 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
MaxWidth="500"
Margin="20,15,20,20" Margin="20,15,20,20"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
MaxWidth="500"
LineHeight="18" LineHeight="18"
TextWrapping="Wrap"
Text="{Binding Heading}" Text="{Binding Heading}"
TextAlignment="Center" /> TextAlignment="Center"
TextWrapping="Wrap" />
<Border <Border
Grid.Row="2" Grid.Row="2"
Margin="5" Margin="5"
@@ -39,32 +42,38 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="Gray" BorderBrush="Gray"
BorderThickness="1"> BorderThickness="1">
<TreeView Items="{Binding LoadedCheats}" <TreeView
HorizontalAlignment="Stretch" Name="CheatsView"
VerticalAlignment="Stretch" MinHeight="300"
Name="CheatsView" HorizontalAlignment="Stretch"
MinHeight="300"> VerticalAlignment="Stretch"
Items="{Binding LoadedCheats}">
<TreeView.Styles> <TreeView.Styles>
<Styles> <Styles>
<Style Selector="TreeViewItem:empty /template/ ItemsPresenter"> <Style Selector="TreeViewItem:empty /template/ ItemsPresenter">
<Setter Property="IsVisible" Value="False"/> <Setter Property="IsVisible" Value="False" />
</Style> </Style>
</Styles> </Styles>
</TreeView.Styles> </TreeView.Styles>
<TreeView.DataTemplates> <TreeView.DataTemplates>
<TreeDataTemplate DataType="model:CheatsList" ItemsSource="{Binding}"> <TreeDataTemplate DataType="model:CheatsList" ItemsSource="{Binding}">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal"> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsEnabled}" MinWidth="20" /> <CheckBox MinWidth="20" IsChecked="{Binding IsEnabled}" />
<TextBlock Width="150" <TextBlock Width="150" Text="{Binding BuildId}" />
Text="{Binding BuildId}" /> <TextBlock Text="{Binding Path}" />
<TextBlock
Text="{Binding Path}" />
</StackPanel> </StackPanel>
</TreeDataTemplate> </TreeDataTemplate>
<DataTemplate x:DataType="model:CheatModel"> <DataTemplate x:DataType="model:CheatModel">
<StackPanel Orientation="Horizontal" Margin="0" HorizontalAlignment="Left"> <StackPanel
<CheckBox IsChecked="{Binding IsEnabled}" Padding="0" Margin="5,0" MinWidth="20" /> Margin="0"
<TextBlock Text="{Binding CleanName}" VerticalAlignment="Center" /> HorizontalAlignment="Left"
Orientation="Horizontal">
<CheckBox
MinWidth="20"
Margin="5,0"
Padding="0"
IsChecked="{Binding IsEnabled}" />
<TextBlock VerticalAlignment="Center" Text="{Binding CleanName}" />
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>
</TreeView.DataTemplates> </TreeView.DataTemplates>
@@ -79,8 +88,8 @@
Name="SaveButton" Name="SaveButton"
MinWidth="90" MinWidth="90"
Margin="5" Margin="5"
IsVisible="{Binding !NoCheatsFound}" Command="{Binding Save}"
Command="{Binding Save}"> IsVisible="{Binding !NoCheatsFound}">
<TextBlock Text="{locale:Locale SettingsButtonSave}" /> <TextBlock Text="{locale:Locale SettingsButtonSave}" />
</Button> </Button>
<Button <Button

View File

@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@@ -26,8 +25,7 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
AttachDebugDevTools();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"];
} }
@@ -38,9 +36,6 @@ namespace Ryujinx.Ava.Ui.Windows
Heading = string.Format(LocaleManager.Instance["CheatWindowHeading"], titleName, titleId.ToUpper()); Heading = string.Format(LocaleManager.Instance["CheatWindowHeading"], titleName, titleId.ToUpper());
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath(); string modsBasePath = virtualFileSystem.ModLoader.GetModsBasePath();
string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId); string titleModsPath = virtualFileSystem.ModLoader.GetTitleDir(modsBasePath, titleId);
@@ -96,12 +91,6 @@ namespace Ryujinx.Ava.Ui.Windows
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["CheatWindowTitle"];
} }
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
}
public void Save() public void Save()
{ {
if (NoCheatsFound) if (NoCheatsFound)

View File

@@ -3,24 +3,14 @@ using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels; using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.Assigner; using Ryujinx.Input.Assigner;
using Ryujinx.Ui.Common.Configuration;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Key = Ryujinx.Input.Key;
namespace Ryujinx.Ava.Ui.Windows namespace Ryujinx.Ava.Ui.Windows
{ {

View File

@@ -1,254 +0,0 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Threading;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Path = System.IO.Path;
namespace Ryujinx.Ava.Ui.Windows
{
public partial class DlcManagerWindow : StyleableWindow
{
private readonly List<DlcContainer> _dlcContainerList;
private readonly string _dlcJsonPath;
public VirtualFileSystem VirtualFileSystem { get; }
public AvaloniaList<DlcModel> Dlcs { get; set; }
public ulong TitleId { get; }
public string TitleName { get; }
public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16"));
public DlcManagerWindow()
{
DataContext = this;
InitializeComponent();
AttachDebugDevTools();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
}
public DlcManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{
VirtualFileSystem = virtualFileSystem;
TitleId = titleId;
TitleName = titleName;
_dlcJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
try
{
_dlcContainerList = JsonHelper.DeserializeFromFile<List<DlcContainer>>(_dlcJsonPath);
}
catch
{
_dlcContainerList = new List<DlcContainer>();
}
DataContext = this;
InitializeComponent();
AttachDebugDevTools();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
LoadDlcs();
}
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
}
private void LoadDlcs()
{
foreach (DlcContainer dlcContainer in _dlcContainerList)
{
using FileStream containerFile = File.OpenRead(dlcContainer.Path);
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
VirtualFileSystem.ImportTickets(pfs);
foreach (DlcNca dlcNca in dlcContainer.DlcNcaList)
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path);
if (nca != null)
{
Dlcs.Add(new DlcModel(nca.Header.TitleId.ToString("X16"), dlcContainer.Path, dlcNca.Path,
dlcNca.Enabled));
}
}
}
}
private Nca TryCreateNca(IStorage ncaStorage, string containerPath)
{
try
{
return new Nca(VirtualFileSystem.KeySet, ncaStorage);
}
catch (Exception ex)
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[
"DialogDlcLoadNcaErrorMessage"], ex.Message, containerPath));
});
}
return null;
}
private async Task AddDlc(string path)
{
if (!File.Exists(path) || Dlcs.FirstOrDefault(x => x.ContainerPath == path) != null)
{
return;
}
using (FileStream containerFile = File.OpenRead(path))
{
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
bool containsDlc = false;
VirtualFileSystem.ImportTickets(pfs);
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
if (nca == null)
{
continue;
}
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
{
break;
}
Dlcs.Add(new DlcModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
containsDlc = true;
}
}
if (!containsDlc)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
}
}
}
private void RemoveDlcs(bool removeSelectedOnly = false)
{
if (removeSelectedOnly)
{
Dlcs.RemoveAll(Dlcs.Where(x => x.IsEnabled).ToList());
}
else
{
Dlcs.Clear();
}
}
public void RemoveSelected()
{
RemoveDlcs(true);
}
public void RemoveAll()
{
RemoveDlcs();
}
public async void Add()
{
OpenFileDialog dialog = new OpenFileDialog() { Title = LocaleManager.Instance["SelectDlcDialogTitle"], AllowMultiple = true };
dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } });
string[] files = await dialog.ShowAsync(this);
if (files != null)
{
foreach (string file in files)
{
await AddDlc(file);
}
}
}
public void Save()
{
_dlcContainerList.Clear();
DlcContainer container = default;
foreach (DlcModel dlc in Dlcs)
{
if (container.Path != dlc.ContainerPath)
{
if (!string.IsNullOrWhiteSpace(container.Path))
{
_dlcContainerList.Add(container);
}
container = new DlcContainer { Path = dlc.ContainerPath, DlcNcaList = new List<DlcNca>() };
}
container.DlcNcaList.Add(new DlcNca
{
Enabled = dlc.IsEnabled,
TitleId = Convert.ToUInt64(dlc.TitleId, 16),
Path = dlc.FullPath
});
}
if (!string.IsNullOrWhiteSpace(container.Path))
{
_dlcContainerList.Add(container);
}
using (FileStream dlcJsonStream = File.Create(_dlcJsonPath, 4096, FileOptions.WriteThrough))
{
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_dlcContainerList, true)));
}
Close();
}
}
}

View File

@@ -1,5 +1,5 @@
<window:StyleableWindow <window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.DlcManagerWindow" x:Class="Ryujinx.Ava.Ui.Windows.DownloadableContentManagerWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
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"
@@ -11,7 +11,7 @@
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
MinWidth="600" MinWidth="600"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Name="DlcGrid" Margin="15"> <Grid Name="DownloadableContentGrid" Margin="15">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -40,7 +40,7 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
Items="{Binding Dlcs}" Items="{Binding DownloadableContents}"
VerticalScrollBarVisibility="Auto"> VerticalScrollBarVisibility="Auto">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTemplateColumn Width="90"> <DataGridTemplateColumn Width="90">
@@ -50,7 +50,7 @@
Width="50" Width="50"
MinWidth="40" MinWidth="40"
HorizontalAlignment="Right" HorizontalAlignment="Right"
IsChecked="{Binding IsEnabled}" /> IsChecked="{Binding Enabled}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.Header> <DataGridTemplateColumn.Header>
@@ -116,7 +116,7 @@
Name="SaveButton" Name="SaveButton"
MinWidth="90" MinWidth="90"
Margin="5" Margin="5"
Command="{Binding Save}"> Command="{Binding SaveAndClose}">
<TextBlock Text="{locale:Locale SettingsButtonSave}" /> <TextBlock Text="{locale:Locale SettingsButtonSave}" />
</Button> </Button>
<Button <Button

View File

@@ -0,0 +1,266 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Threading;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Path = System.IO.Path;
namespace Ryujinx.Ava.Ui.Windows
{
public partial class DownloadableContentManagerWindow : StyleableWindow
{
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
private readonly string _downloadableContentJsonPath;
public VirtualFileSystem VirtualFileSystem { get; }
public AvaloniaList<DownloadableContentModel> DownloadableContents { get; set; } = new AvaloniaList<DownloadableContentModel>();
public ulong TitleId { get; }
public string TitleName { get; }
public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16"));
public DownloadableContentManagerWindow()
{
DataContext = this;
InitializeComponent();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
}
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{
VirtualFileSystem = virtualFileSystem;
TitleId = titleId;
TitleName = titleName;
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
try
{
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath);
}
catch
{
_downloadableContentContainerList = new List<DownloadableContentContainer>();
}
DataContext = this;
InitializeComponent();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
LoadDownloadableContents();
}
private void LoadDownloadableContents()
{
foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList)
{
if (File.Exists(downloadableContentContainer.ContainerPath))
{
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
VirtualFileSystem.ImportTickets(pfs);
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
if (nca != null)
{
DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
downloadableContentContainer.ContainerPath,
downloadableContentNca.FullPath,
downloadableContentNca.Enabled));
}
}
}
}
// NOTE: Save the list again to remove leftovers.
Save();
}
private Nca TryCreateNca(IStorage ncaStorage, string containerPath)
{
try
{
return new Nca(VirtualFileSystem.KeySet, ncaStorage);
}
catch (Exception ex)
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, containerPath));
});
}
return null;
}
private async Task AddDownloadableContent(string path)
{
if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
{
return;
}
using (FileStream containerFile = File.OpenRead(path))
{
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
bool containsDownloadableContent = false;
VirtualFileSystem.ImportTickets(pfs);
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
if (nca == null)
{
continue;
}
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
{
break;
}
DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
containsDownloadableContent = true;
}
}
if (!containsDownloadableContent)
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
}
}
}
private void RemoveDownloadableContents(bool removeSelectedOnly = false)
{
if (removeSelectedOnly)
{
DownloadableContents.RemoveAll(DownloadableContents.Where(x => x.Enabled).ToList());
}
else
{
DownloadableContents.Clear();
}
}
public void RemoveSelected()
{
RemoveDownloadableContents(true);
}
public void RemoveAll()
{
RemoveDownloadableContents();
}
public async void Add()
{
OpenFileDialog dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance["SelectDlcDialogTitle"],
AllowMultiple = true
};
dialog.Filters.Add(new FileDialogFilter
{
Name = "NSP",
Extensions = { "nsp" }
});
string[] files = await dialog.ShowAsync(this);
if (files != null)
{
foreach (string file in files)
{
await AddDownloadableContent(file);
}
}
}
public void Save()
{
_downloadableContentContainerList.Clear();
DownloadableContentContainer container = default;
foreach (DownloadableContentModel downloadableContent in DownloadableContents)
{
if (container.ContainerPath != downloadableContent.ContainerPath)
{
if (!string.IsNullOrWhiteSpace(container.ContainerPath))
{
_downloadableContentContainerList.Add(container);
}
container = new DownloadableContentContainer
{
ContainerPath = downloadableContent.ContainerPath,
DownloadableContentNcaList = new List<DownloadableContentNca>()
};
}
container.DownloadableContentNcaList.Add(new DownloadableContentNca
{
Enabled = downloadableContent.Enabled,
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
FullPath = downloadableContent.FullPath
});
}
if (!string.IsNullOrWhiteSpace(container.ContainerPath))
{
_downloadableContentContainerList.Add(container);
}
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough))
{
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
}
}
public void SaveAndClose()
{
Save();
Close();
}
}
}

View File

@@ -257,7 +257,7 @@
</DockPanel> </DockPanel>
</StackPanel> </StackPanel>
<ContentControl <ContentControl
Name="Content" Name="MainContent"
Grid.Row="1" Grid.Row="1"
Padding="0" Padding="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"

View File

@@ -2,10 +2,8 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using Avalonia.Win32;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -33,7 +31,7 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using InputManager = Ryujinx.Input.HLE.InputManager; using InputManager = Ryujinx.Input.HLE.InputManager;
using ProgressBar = Avalonia.Controls.ProgressBar;
namespace Ryujinx.Ava.Ui.Windows namespace Ryujinx.Ava.Ui.Windows
{ {
public partial class MainWindow : StyleableWindow public partial class MainWindow : StyleableWindow
@@ -87,7 +85,6 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent(); InitializeComponent();
Load(); Load();
AttachDebugDevTools();
UiHandler = new AvaHostUiHandler(this); UiHandler = new AvaHostUiHandler(this);
@@ -110,12 +107,6 @@ namespace Ryujinx.Ava.Ui.Windows
_rendererWaitEvent = new AutoResetEvent(false); _rendererWaitEvent = new AutoResetEvent(false);
} }
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
}
public void LoadGameList() public void LoadGameList()
{ {
if (_isLoading) if (_isLoading)
@@ -244,7 +235,7 @@ namespace Ryujinx.Ava.Ui.Windows
PrepareLoadScreen(); PrepareLoadScreen();
_mainViewContent = Content.Content as Control; _mainViewContent = MainContent.Content as Control;
GlRenderer = new RendererControl(3, 3, ConfigurationState.Instance.Logger.GraphicsDebugLevel); GlRenderer = new RendererControl(3, 3, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
AppHost = new AppHost(GlRenderer, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); AppHost = new AppHost(GlRenderer, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
@@ -311,7 +302,7 @@ namespace Ryujinx.Ava.Ui.Windows
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
Content.Content = GlRenderer; MainContent.Content = GlRenderer;
if (startFullscreen && WindowState != WindowState.FullScreen) if (startFullscreen && WindowState != WindowState.FullScreen)
{ {
@@ -355,9 +346,9 @@ namespace Ryujinx.Ava.Ui.Windows
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
if (Content.Content != _mainViewContent) if (MainContent.Content != _mainViewContent)
{ {
Content.Content = _mainViewContent; MainContent.Content = _mainViewContent;
} }
ViewModel.ShowMenuAndStatusBar = true; ViewModel.ShowMenuAndStatusBar = true;

View File

@@ -1,5 +1,4 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;

View File

@@ -1,5 +1,4 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;

View File

@@ -1,19 +1,14 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using FluentAvalonia.Core; using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels; using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Input; using Ryujinx.Input;
@@ -23,8 +18,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone; using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
namespace Ryujinx.Ava.Ui.Windows namespace Ryujinx.Ava.Ui.Windows
@@ -44,7 +37,6 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent(); InitializeComponent();
Load(); Load();
AttachDebugDevTools();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray())); FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()));
MultiBinding tzMultiBinding = new() { Converter = converter }; MultiBinding tzMultiBinding = new() { Converter = converter };
@@ -62,13 +54,6 @@ namespace Ryujinx.Ava.Ui.Windows
InitializeComponent(); InitializeComponent();
Load(); Load();
AttachDebugDevTools();
}
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
} }
private void Load() private void Load()

View File

@@ -2,7 +2,6 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using FluentAvalonia.UI.Controls;
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;

View File

@@ -1,13 +1,14 @@
using Avalonia; using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Threading;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls; using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models; using Ryujinx.Ava.Ui.Models;
@@ -23,14 +24,12 @@ using System.Linq;
using System.Text; using System.Text;
using Path = System.IO.Path; using Path = System.IO.Path;
using SpanHelpers = LibHac.Common.SpanHelpers; using SpanHelpers = LibHac.Common.SpanHelpers;
using LibHac.Tools.FsSystem;
using Avalonia.Threading;
namespace Ryujinx.Ava.Ui.Windows namespace Ryujinx.Ava.Ui.Windows
{ {
public partial class TitleUpdateWindow : StyleableWindow public partial class TitleUpdateWindow : StyleableWindow
{ {
private readonly string _updateJsonPath; private readonly string _titleUpdateJsonPath;
private TitleUpdateMetadata _titleUpdateWindowData; private TitleUpdateMetadata _titleUpdateWindowData;
public VirtualFileSystem VirtualFileSystem { get; } public VirtualFileSystem VirtualFileSystem { get; }
@@ -46,7 +45,6 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
AttachDebugDevTools();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
} }
@@ -54,36 +52,33 @@ namespace Ryujinx.Ava.Ui.Windows
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName) public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
{ {
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
TitleId = titleId; TitleId = titleId;
TitleName = titleName; TitleName = titleName;
_updateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId, "updates.json"); _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId, "updates.json");
try try
{ {
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_updateJsonPath); _titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
} }
catch catch
{ {
_titleUpdateWindowData = new TitleUpdateMetadata {Selected = "", Paths = new List<string>()}; _titleUpdateWindowData = new TitleUpdateMetadata
{
Selected = "",
Paths = new List<string>()
};
} }
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
AttachDebugDevTools();
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"]; Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
LoadUpdates(); LoadUpdates();
} }
[Conditional("DEBUG")]
private void AttachDebugDevTools()
{
this.AttachDevTools();
}
private void LoadUpdates() private void LoadUpdates()
{ {
TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true)); TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
@@ -99,8 +94,8 @@ namespace Ryujinx.Ava.Ui.Windows
} }
else else
{ {
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected); TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected);
List<TitleUpdateModel> enabled = TitleUpdates.Where(x => x.IsEnabled).ToList(); List<TitleUpdateModel> enabled = TitleUpdates.Where(x => x.IsEnabled).ToList();
foreach (TitleUpdateModel update in enabled) foreach (TitleUpdateModel update in enabled)
{ {
@@ -126,8 +121,7 @@ namespace Ryujinx.Ava.Ui.Windows
try try
{ {
(Nca patchNca, Nca controlNca) = (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(VirtualFileSystem, nsp, TitleId, 0);
ApplicationLoader.GetGameUpdateDataFromPartition(VirtualFileSystem, nsp, TitleId, 0);
if (controlNca != null && patchNca != null) if (controlNca != null && patchNca != null)
{ {
@@ -135,11 +129,8 @@ namespace Ryujinx.Ava.Ui.Windows
using var nacpFile = new UniqueRef<IFile>(); using var nacpFile = new UniqueRef<IFile>();
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None) controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
.OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read) nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
.ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None)
.ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path)); TitleUpdates.Add(new TitleUpdateModel(controlData, path));
} }
@@ -190,9 +181,17 @@ namespace Ryujinx.Ava.Ui.Windows
public async void Add() public async void Add()
{ {
OpenFileDialog dialog = new OpenFileDialog() { Title = LocaleManager.Instance["SelectUpdateDialogTitle"], AllowMultiple = true }; OpenFileDialog dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance["SelectUpdateDialogTitle"],
AllowMultiple = true
};
dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); dialog.Filters.Add(new FileDialogFilter
{
Name = "NSP",
Extensions = { "nsp" }
});
string[] files = await dialog.ShowAsync(this); string[] files = await dialog.ShowAsync(this);
@@ -222,12 +221,10 @@ namespace Ryujinx.Ava.Ui.Windows
return 1; return 1;
} }
return Version.Parse(first.Control.DisplayVersionString.ToString()) return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
.CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
}); });
TitleUpdates.Clear(); TitleUpdates.Clear();
TitleUpdates.AddRange(list); TitleUpdates.AddRange(list);
} }
@@ -247,9 +244,9 @@ namespace Ryujinx.Ava.Ui.Windows
} }
} }
using (FileStream dlcJsonStream = File.Create(_updateJsonPath, 4096, FileOptions.WriteThrough)) using (FileStream titleUpdateJsonStream = File.Create(_titleUpdateJsonPath, 4096, FileOptions.WriteThrough))
{ {
dlcJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); titleUpdateJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true)));
} }
if (Owner is MainWindow window) if (Owner is MainWindow window)

View File

@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Modules; using Ryujinx.Modules;
using System; using System;
@@ -23,9 +22,7 @@ namespace Ryujinx.Ava.Ui.Windows
DataContext = this; DataContext = this;
InitializeComponent(); InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
Title = LocaleManager.Instance["RyujinxUpdater"]; Title = LocaleManager.Instance["RyujinxUpdater"];
} }

View File

@@ -1,10 +0,0 @@
using System.Collections.Generic;
namespace Ryujinx.Common.Configuration
{
public struct DlcContainer
{
public string Path { get; set; }
public List<DlcNca> DlcNcaList { get; set; }
}
}

View File

@@ -1,9 +0,0 @@
namespace Ryujinx.Common.Configuration
{
public struct DlcNca
{
public string Path { get; set; }
public ulong TitleId { get; set; }
public bool Enabled { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
public struct DownloadableContentContainer
{
[JsonPropertyName("path")]
public string ContainerPath { get; set; }
[JsonPropertyName("dlc_nca_list")]
public List<DownloadableContentNca> DownloadableContentNcaList { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
public struct DownloadableContentNca
{
[JsonPropertyName("path")]
public string FullPath { get; set; }
[JsonPropertyName("title_id")]
public ulong TitleId { get; set; }
[JsonPropertyName("is_enabled")]
public bool Enabled { get; set; }
}
}

View File

@@ -216,13 +216,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{ {
var target = memoryManager.Physical.TextureCache.FindTexture( var target = memoryManager.Physical.TextureCache.FindTexture(
memoryManager, memoryManager,
dst,
dstGpuVa, dstGpuVa,
dstBpp, dstBpp,
dstStride, dstStride,
dst.Height,
xCount, xCount,
yCount, yCount,
dstLinear); dstLinear,
dst.MemoryLayout);
if (target != null) if (target != null)
{ {

View File

@@ -900,23 +900,25 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
/// </summary> /// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param> /// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="tex">The texture information</param>
/// <param name="gpuVa">GPU virtual address of the texture</param> /// <param name="gpuVa">GPU virtual address of the texture</param>
/// <param name="bpp">Bytes per pixel</param> /// <param name="bpp">Bytes per pixel</param>
/// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param> /// <param name="stride">If <paramref name="linear"/> is true, should have the texture stride, otherwise ignored</param>
/// <param name="height">If <paramref name="linear"/> is false, should have the texture height, otherwise ignored</param>
/// <param name="xCount">Number of pixels to be copied per line</param> /// <param name="xCount">Number of pixels to be copied per line</param>
/// <param name="yCount">Number of lines to be copied</param> /// <param name="yCount">Number of lines to be copied</param>
/// <param name="linear">True if the texture has a linear layout, false otherwise</param> /// <param name="linear">True if the texture has a linear layout, false otherwise</param>
/// <param name="memoryLayout">If <paramref name="linear"/> is false, should have the memory layout, otherwise ignored</param>
/// <returns>A matching texture, or null if there is no match</returns> /// <returns>A matching texture, or null if there is no match</returns>
public Texture FindTexture( public Texture FindTexture(
MemoryManager memoryManager, MemoryManager memoryManager,
DmaTexture tex,
ulong gpuVa, ulong gpuVa,
int bpp, int bpp,
int stride, int stride,
int height,
int xCount, int xCount,
int yCount, int yCount,
bool linear) bool linear,
MemoryLayout memoryLayout)
{ {
ulong address = memoryManager.Translate(gpuVa); ulong address = memoryManager.Translate(gpuVa);
@@ -945,7 +947,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
// Size is not available for linear textures. Use the stride and end of the copy region instead. // Size is not available for linear textures. Use the stride and end of the copy region instead.
match = texture.Info.IsLinear && texture.Info.Stride == stride && tex.RegionY + yCount <= texture.Info.Height; match = texture.Info.IsLinear && texture.Info.Stride == stride && yCount == texture.Info.Height;
} }
else else
{ {
@@ -953,10 +955,10 @@ namespace Ryujinx.Graphics.Gpu.Image
// Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison. // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison.
// Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size. // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size.
bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && tex.Height == texture.Info.Height; bool sizeMatch = xCount * bpp == texture.Info.Width * format.BytesPerPixel && height == texture.Info.Height;
bool formatMatch = !texture.Info.IsLinear && bool formatMatch = !texture.Info.IsLinear &&
texture.Info.GobBlocksInY == tex.MemoryLayout.UnpackGobBlocksInY() && texture.Info.GobBlocksInY == memoryLayout.UnpackGobBlocksInY() &&
texture.Info.GobBlocksInZ == tex.MemoryLayout.UnpackGobBlocksInZ(); texture.Info.GobBlocksInZ == memoryLayout.UnpackGobBlocksInZ();
match = sizeMatch && formatMatch; match = sizeMatch && formatMatch;
} }

View File

@@ -422,19 +422,19 @@ namespace Ryujinx.HLE.HOS
if (File.Exists(titleAocMetadataPath)) if (File.Exists(titleAocMetadataPath))
{ {
List<DlcContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DlcContainer>>(titleAocMetadataPath); List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(titleAocMetadataPath);
foreach (DlcContainer dlcContainer in dlcContainerList) foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
{ {
foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
{ {
if (File.Exists(dlcContainer.Path)) if (File.Exists(downloadableContentContainer.ContainerPath))
{ {
_device.Configuration.ContentManager.AddAocItem(dlcNca.TitleId, dlcContainer.Path, dlcNca.Path, dlcNca.Enabled); _device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath, downloadableContentNca.Enabled);
} }
else else
{ {
Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {dlcContainer.Path}. It may have been moved or renamed."); Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed.");
} }
} }
} }

View File

@@ -21,10 +21,10 @@ namespace Ryujinx.Ui.Windows
{ {
public class DlcWindow : Window public class DlcWindow : Window
{ {
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _titleId; private readonly string _titleId;
private readonly string _dlcJsonPath; private readonly string _dlcJsonPath;
private readonly List<DlcContainer> _dlcContainerList; private readonly List<DownloadableContentContainer> _dlcContainerList;
#pragma warning disable CS0649, IDE0044 #pragma warning disable CS0649, IDE0044
[GUI] Label _baseTitleInfoLabel; [GUI] Label _baseTitleInfoLabel;
@@ -45,11 +45,11 @@ namespace Ryujinx.Ui.Windows
try try
{ {
_dlcContainerList = JsonHelper.DeserializeFromFile<List<DlcContainer>>(_dlcJsonPath); _dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_dlcJsonPath);
} }
catch catch
{ {
_dlcContainerList = new List<DlcContainer>(); _dlcContainerList = new List<DownloadableContentContainer>();
} }
_dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string));
@@ -75,37 +75,37 @@ namespace Ryujinx.Ui.Windows
_dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1); _dlcTreeView.AppendColumn("TitleId", new CellRendererText(), "text", 1);
_dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2);
foreach (DlcContainer dlcContainer in _dlcContainerList) foreach (DownloadableContentContainer dlcContainer in _dlcContainerList)
{ {
if (File.Exists(dlcContainer.Path)) if (File.Exists(dlcContainer.ContainerPath))
{ {
// The parent tree item has its own "enabled" check box, but it's the actual // The parent tree item has its own "enabled" check box, but it's the actual
// nca entries that store the enabled / disabled state. A bit of a UI inconsistency. // nca entries that store the enabled / disabled state. A bit of a UI inconsistency.
// Maybe a tri-state check box would be better, but for now we check the parent // Maybe a tri-state check box would be better, but for now we check the parent
// "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca.
bool areAllContentPacksEnabled = dlcContainer.DlcNcaList.TrueForAll((nca) => nca.Enabled); bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled);
TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.Path); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath);
using FileStream containerFile = File.OpenRead(dlcContainer.Path); using FileStream containerFile = File.OpenRead(dlcContainer.ContainerPath);
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage()); PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
_virtualFileSystem.ImportTickets(pfs); _virtualFileSystem.ImportTickets(pfs);
foreach (DlcNca dlcNca in dlcContainer.DlcNcaList) foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList)
{ {
using var ncaFile = new UniqueRef<IFile>(); using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), dlcNca.Path.ToU8Span(), OpenMode.Read).ThrowIfFailure(); pfs.OpenFile(ref ncaFile.Ref(), dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.Path); Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.ContainerPath);
if (nca != null) if (nca != null)
{ {
((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.Path); ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.FullPath);
} }
} }
} }
else else
{ {
// DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog. // DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog.
TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.Path}"); TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.ContainerPath}");
} }
} }
} }
@@ -237,19 +237,19 @@ namespace Ryujinx.Ui.Windows
{ {
if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter))
{ {
DlcContainer dlcContainer = new DlcContainer DownloadableContentContainer dlcContainer = new DownloadableContentContainer
{ {
Path = (string)_dlcTreeView.Model.GetValue(parentIter, 2), ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2),
DlcNcaList = new List<DlcNca>() DownloadableContentNcaList = new List<DownloadableContentNca>()
}; };
do do
{ {
dlcContainer.DlcNcaList.Add(new DlcNca dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca
{ {
Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0),
TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16),
Path = (string)_dlcTreeView.Model.GetValue(childIter, 2) FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2)
}); });
} }
while (_dlcTreeView.Model.IterNext(ref childIter)); while (_dlcTreeView.Model.IterNext(ref childIter));