Compare commits

..

6 Commits

Author SHA1 Message Date
625f5fb88a Account for pool change on texture bindings cache (#3420)
* Account for pool change on texture bindings cache

* Reduce the number of checks needed
2022-06-25 16:52:38 +02:00
2382717600 timezone: Fix regression caused by #3361 (#3418)
Because of that PR, TimeZoneRule was bigger than 0x4000 thanks to a
misuse of a constant.

This commit address this issue and add a new unit test to ensure the size of
TimeZoneRule is 0x4000 bytes.

Also address suggestions that were lost on the original PR.
2022-06-24 21:11:56 +02:00
30ee70a9bc time: Make TimeZoneRule blittable and avoid copies (#3361)
* time: Make TimeZoneRule blittable and avoid copies

This drastically reduce overhead of using TimeZoneRule around the
codebase.

Effect on games is unknown

* Add missing Box type

* Ensure we clean the structure still

This doesn't perform any copies

* Address gdkchan's comments

* Simplify Box
2022-06-24 19:04:57 +02:00
232b1012b0 Fix ThreadingLock deadlock on invalid access and TerminateProcess (#3407) 2022-06-24 02:53:16 +02:00
e747f5cd83 Ensure texture ID is valid before getting texture descriptor (#3406) 2022-06-24 02:41:57 +02:00
8aff17a93c UI: Some Avalonia cleanup (#3358) 2022-06-23 15:59:02 -03:00
33 changed files with 1375 additions and 914 deletions

View File

@ -1,9 +1,9 @@
<Application
x:Class="Ryujinx.Ava.App"
xmlns="https://github.com/avaloniaui"
xmlns:sty="using:FluentAvalonia.Styling"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sty="using:FluentAvalonia.Styling">
<Application.Styles>
<sty:FluentAvaloniaTheme UseSystemThemeOnWindows="False"/>
<sty:FluentAvaloniaTheme UseSystemThemeOnWindows="False" />
</Application.Styles>
</Application>

View File

@ -2,7 +2,6 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
@ -13,7 +12,7 @@ using System.IO;
namespace Ryujinx.Ava
{
public class App : Avalonia.Application
public class App : Application
{
public override void Initialize()
{
@ -46,7 +45,7 @@ namespace Ryujinx.Ava
private void ShowRestartDialog()
{
// TODO. Implement Restart Dialog when SettingsWindow is implemented.
// TODO: Implement Restart Dialog when SettingsWindow is implemented.
}
private void ThemeChanged_Event(object sender, ReactiveEventArgs<string> e)

View File

@ -57,7 +57,7 @@ namespace Ryujinx.Ava
private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None);
private readonly AccountManager _accountManager;
private UserChannelPersistence _userChannelPersistence;
private readonly UserChannelPersistence _userChannelPersistence;
private readonly InputManager _inputManager;
@ -82,7 +82,6 @@ namespace Ryujinx.Ava
private bool _dialogShown;
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private KeyboardStateSnapshot _lastKeyboardSnapshot;
private readonly CancellationTokenSource _gpuCancellationTokenSource;
@ -126,7 +125,6 @@ namespace Ryujinx.Ava
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(renderer));
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
_lastKeyboardSnapshot = _keyboardInterface.GetKeyboardStateSnapshot();
NpadManager = _inputManager.CreateNpadManager();
TouchScreenManager = _inputManager.CreateTouchScreenManager();
@ -722,9 +720,7 @@ namespace Ryujinx.Ava
}
}
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value
? HLE.MemoryConfiguration.MemoryConfiguration6GB
: HLE.MemoryConfiguration.MemoryConfiguration4GB;
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GB : HLE.MemoryConfiguration.MemoryConfiguration4GB;
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
@ -898,7 +894,7 @@ namespace Ryujinx.Ava
}
}
private void HandleScreenState(KeyboardStateSnapshot keyboard, KeyboardStateSnapshot lastKeyboard)
private void HandleScreenState()
{
if (ConfigurationState.Instance.Hid.EnableMouse)
{
@ -935,19 +931,12 @@ namespace Ryujinx.Ava
{
Dispatcher.UIThread.Post(() =>
{
KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot();
HandleScreenState();
HandleScreenState(keyboard, _lastKeyboardSnapshot);
if (keyboard.IsPressed(Key.Delete))
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen)
{
if (_parent.WindowState != WindowState.FullScreen)
{
Ptc.Continue();
}
Ptc.Continue();
}
_lastKeyboardSnapshot = keyboard;
});
}

View File

@ -1,9 +1,7 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
<Design.PreviewWith>
<Border Padding="20" Height="2000">
<Border Height="2000" Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
@ -27,8 +25,12 @@
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
<TextBox Width="100" VerticalAlignment="Center" Text="Rrrrr" Watermark="Hello"
UseFloatingWatermark="True" />
<TextBox
Width="100"
VerticalAlignment="Center"
Text="Rrrrr"
UseFloatingWatermark="True"
Watermark="Hello" />
<CheckBox>Test Check</CheckBox>
</StackPanel>
</Grid>

View File

@ -1,9 +1,7 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StyleInclude Source="avares://Ryujinx.Ava/Assets/Styles/Styles.xaml" />
<Design.PreviewWith>
<Border Padding="20" Height="2000">
<Border Height="2000" Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
@ -27,8 +25,12 @@
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
<TextBox Width="100" VerticalAlignment="Center" Text="Rrrrr" Watermark="Hello"
UseFloatingWatermark="True" />
<TextBox
Width="100"
VerticalAlignment="Center"
Text="Rrrrr"
UseFloatingWatermark="True"
Watermark="Hello" />
<CheckBox>Test Check</CheckBox>
</StackPanel>
</Grid>

View File

@ -1,10 +1,10 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:sys="clr-namespace:System;assembly=netstandard">
xmlns:sys="clr-namespace:System;assembly=netstandard"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
<Design.PreviewWith>
<Border Padding="20" Height="2000">
<Border Height="2000" Padding="20">
<StackPanel Spacing="5">
<TextBlock Text="Code Font Family" />
<Grid RowDefinitions="*,Auto">
@ -22,15 +22,19 @@
<StackPanel Orientation="Horizontal">
<ToggleButton
Name="btnAdd"
HorizontalAlignment="Right"
Height="28"
HorizontalAlignment="Right"
Content="Addy" />
<Button
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
<TextBox Width="100" VerticalAlignment="Center" Text="Rrrrr" Watermark="Hello"
UseFloatingWatermark="True" />
<TextBox
Width="100"
VerticalAlignment="Center"
Text="Rrrrr"
UseFloatingWatermark="True"
Watermark="Hello" />
<CheckBox>Test Check</CheckBox>
</StackPanel>
</Grid>
@ -62,13 +66,10 @@
<Style Selector="Image.huge">
<Setter Property="Width" Value="120" />
</Style>
<Style Selector="RadioButton">
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style Selector="#TitleBarHost > Image">
<Style Selector="#TitleBarHost &gt; Image">
<Setter Property="Margin" Value="10" />
</Style>
<Style Selector="#TitleBarHost > Label">
<Style Selector="#TitleBarHost &gt; Label">
<Setter Property="Margin" Value="5" />
<Setter Property="FontSize" Value="14" />
</Style>
@ -225,12 +226,12 @@
<StaticResource x:Key="ListViewItemBackgroundPointerOver" ResourceKey="SystemAccentColorDark2" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPressed" ResourceKey="ThemeAccentColorBrush" />
<StaticResource x:Key="ListViewItemBackgroundSelectedPointerOver" ResourceKey="SystemAccentColorDark2" />
<SolidColorBrush x:Key="DataGridGridLinesBrush"
Color="{DynamicResource SystemBaseMediumLowColor}"
Opacity="0.4" />
<SolidColorBrush
x:Key="DataGridGridLinesBrush"
Opacity="0.4"
Color="{DynamicResource SystemBaseMediumLowColor}" />
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" Color="{DynamicResource DataGridSelectionColor}" />
<SolidColorBrush x:Key="MenuFlyoutPresenterBorderBrush"
Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<SolidColorBrush x:Key="MenuFlyoutPresenterBorderBrush" Color="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<SolidColorBrush x:Key="ThemeAccentColorBrush" Color="{DynamicResource SystemAccentColor}" />
<SolidColorBrush x:Key="ListBoxBackground" Color="{DynamicResource ThemeContentBackgroundColor}" />
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource ThemeForegroundColor}" />
@ -241,7 +242,6 @@
<SolidColorBrush x:Key="SplitButtonBackgroundCheckedDisabled" Color="#00E81123" />
<Thickness x:Key="PageMargin">40 0 40 0</Thickness>
<Thickness x:Key="Margin">0 5 0 5</Thickness>
<Thickness x:Key="TextMargin">0 4 0 0</Thickness>
<Thickness x:Key="MenuItemPadding">5 0 5 0</Thickness>
<Color x:Key="MenuFlyoutPresenterBorderColor">#00000000</Color>
<Color x:Key="SystemAccentColor">#FF00C3E3</Color>

View File

@ -1,17 +1,21 @@
<Window 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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Applet.ErrorAppletWindow"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
CanResize="False"
SizeToContent="Height"
Width="450"
Height="340"
Title="{locale:Locale ErrorWindowTitle}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20">
<Window
x:Class="Ryujinx.Ava.Ui.Applet.ErrorAppletWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="{locale:Locale ErrorWindowTitle}"
Width="450"
Height="340"
CanResize="False"
SizeToContent="Height"
mc:Ignorable="d">
<Grid
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
@ -21,11 +25,28 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="1" Grid.RowSpan="2" Margin="5, 10, 20 , 10" Grid.Column="0"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" Height="80" MinWidth="50" />
<TextBlock Grid.Row="1" Margin="10" Grid.Column="1" VerticalAlignment="Stretch" TextWrapping="Wrap"
Text="{Binding Message}" />
<StackPanel Name="ButtonStack" Margin="10" Spacing="10" Grid.Row="2" Grid.Column="1"
HorizontalAlignment="Right" Orientation="Horizontal" />
<Image
Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="0"
Height="80"
MinWidth="50"
Margin="5,10,20,10"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="10"
VerticalAlignment="Stretch"
Text="{Binding Message}"
TextWrapping="Wrap" />
<StackPanel
Name="ButtonStack"
Grid.Row="2"
Grid.Column="1"
Margin="10"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="10" />
</Grid>
</Window>

View File

@ -1,12 +1,16 @@
<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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Controls.SwkbdAppletDialog"
Width="400">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20">
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.SwkbdAppletDialog"
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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Width="400"
mc:Ignorable="d">
<Grid
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -18,15 +22,43 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="1" VerticalAlignment="Center" Grid.RowSpan="5" Margin="5, 10, 20 , 10"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" Height="80"
MinWidth="50" />
<TextBlock Grid.Row="1" Margin="5" Grid.Column="1" Text="{Binding MainText}" TextWrapping="Wrap" />
<TextBlock Grid.Row="2" Margin="5" Grid.Column="1" Text="{Binding SecondaryText}" TextWrapping="Wrap" />
<TextBox Name="Input" KeyUp="Message_KeyUp" UseFloatingWatermark="True" TextInput="Message_TextInput"
Text="{Binding Message}" Grid.Row="2"
Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap" />
<TextBlock Name="Error" Margin="5" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Stretch"
TextWrapping="Wrap" />
<Image
Grid.Row="1"
Grid.RowSpan="5"
Height="80"
MinWidth="50"
Margin="5,10,20,10"
VerticalAlignment="Center"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="5"
Text="{Binding MainText}"
TextWrapping="Wrap" />
<TextBlock
Grid.Row="2"
Grid.Column="1"
Margin="5"
Text="{Binding SecondaryText}"
TextWrapping="Wrap" />
<TextBox
Name="Input"
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
KeyUp="Message_KeyUp"
Text="{Binding Message}"
TextInput="Message_TextInput"
TextWrapping="Wrap"
UseFloatingWatermark="True" />
<TextBlock
Name="Error"
Grid.Row="4"
Grid.Column="1"
Margin="5"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" />
</Grid>
</UserControl>

View File

@ -1,188 +1,219 @@
<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:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.Ui.Controls.GameGridView">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
<MenuItem
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.GameGridView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
<MenuItem
Command="{Binding ToggleFavorite}"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<Separator />
<MenuItem
<Separator />
<MenuItem
Command="{Binding OpenUserSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserDeviceDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserDeviceDirectoryToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserBcatDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserBcatDirectoryToolTip}" />
<Separator />
<MenuItem
<Separator />
<MenuItem
Command="{Binding OpenTitleUpdateManager}"
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenDlcManager}"
Header="{locale:Locale GameListContextMenuManageDlc}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenCheatManager}"
Header="{locale:Locale GameListContextMenuManageCheat}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenSdModsDirectory}"
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
<Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem
<Separator />
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem
Command="{Binding PurgePtcCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
<MenuItem
<MenuItem
Command="{Binding PurgeShaderCache}"
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenPtcDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
<MenuItem
<MenuItem
Command="{Binding OpenShaderCacheDirectory}"
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
</MenuItem>
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
<MenuItem
Command="{Binding ExtractExeFs}"
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
<MenuItem
<MenuItem
Command="{Binding ExtractRomFs}"
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
<MenuItem
<MenuItem
Command="{Binding ExtractLogo}"
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
</MenuFlyout>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
DoubleTapped="GameList_DoubleTapped"
SelectionChanged="GameList_SelectionChanged"
ContextFlyout="{StaticResource GameContextMenu}"
VerticalAlignment="Stretch"
Items="{Binding AppsObservableList}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<flex:FlexPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" JustifyContent="Center"
AlignContent="FlexStart" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="Background" Value="{DynamicResource SystemAccentColorDark3}" />
<Style.Animations>
<Animation Duration="0:0:0.7">
<KeyFrame Cue="0%">
<Setter Property="MaxWidth" Value="0"/>
<Setter Property="Opacity" Value="0.0"/>
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="MaxWidth" Value="1000"/>
<Setter Property="Opacity" Value="0.3"/>
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="MaxWidth" Value="1000"/>
<Setter Property="Opacity" Value="1.0"/>
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Styles>
<Style Selector="ui|SymbolIcon.small.icon">
<Setter Property="FontSize" Value="15" />
</Style>
<Style Selector="ui|SymbolIcon.normal.icon">
<Setter Property="FontSize" Value="19" />
</Style>
<Style Selector="ui|SymbolIcon.large.icon">
<Setter Property="FontSize" Value="23" />
</Style>
<Style Selector="ui|SymbolIcon.huge.icon">
<Setter Property="FontSize" Value="26" />
</Style>
</Grid.Styles>
<Border
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
</MenuItem>
</MenuFlyout>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox
Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource GameContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<flex:FlexPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AlignContent="FlexStart"
JustifyContent="Center" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="5" />
<Setter Property="CornerRadius" Value="5" />
<Setter Property="Background" Value="{DynamicResource SystemAccentColorDark3}" />
<Style.Animations>
<Animation Duration="0:0:0.7">
<KeyFrame Cue="0%">
<Setter Property="MaxWidth" Value="0" />
<Setter Property="Opacity" Value="0.0" />
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="Opacity" Value="0.3" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="MaxWidth" Value="1000" />
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</ListBox.Styles>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.Styles>
<Style Selector="ui|SymbolIcon.small.icon">
<Setter Property="FontSize" Value="15" />
</Style>
<Style Selector="ui|SymbolIcon.normal.icon">
<Setter Property="FontSize" Value="19" />
</Style>
<Style Selector="ui|SymbolIcon.large.icon">
<Setter Property="FontSize" Value="23" />
</Style>
<Style Selector="ui|SymbolIcon.huge.icon">
<Setter Property="FontSize" Value="26" />
</Style>
</Grid.Styles>
<Border
Margin="0"
Padding="{Binding $parent[UserControl].DataContext.GridItemPadding}"
HorizontalAlignment="Stretch"
Padding="{Binding $parent[UserControl].DataContext.GridItemPadding}" CornerRadius="5"
VerticalAlignment="Stretch" Margin="0" ClipToBounds="True">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="0" Grid.Row="0"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<StackPanel IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}"
Height="50" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="5" Grid.Row="1">
<TextBlock Text="{Binding TitleName}" TextAlignment="Center" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
</StackPanel>
</Grid>
</Border>
<ui:SymbolIcon Classes.icon="true" Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Foreground="Yellow" Symbol="StarFilled"
IsVisible="{Binding Favorite}" Margin="5" VerticalAlignment="Top"
HorizontalAlignment="Left" />
<ui:SymbolIcon Classes.icon="true" Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Foreground="Black" Symbol="Star"
IsVisible="{Binding Favorite}" Margin="5" VerticalAlignment="Top"
HorizontalAlignment="Left" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
VerticalAlignment="Stretch"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
ClipToBounds="True"
CornerRadius="5">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<StackPanel
Grid.Row="1"
Height="50"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].DataContext.ShowNames}">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TitleName}"
TextAlignment="Center"
TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Border>
<ui:SymbolIcon
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.icon="true"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Foreground="Yellow"
IsVisible="{Binding Favorite}"
Symbol="StarFilled" />
<ui:SymbolIcon
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Classes.icon="true"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Foreground="Black"
IsVisible="{Binding Favorite}"
Symbol="Star" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

View File

@ -1,13 +1,16 @@
<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:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.Ui.Controls.GameListView">
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.GameListView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
@ -88,18 +91,23 @@
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
DoubleTapped="GameList_DoubleTapped"
SelectionChanged="GameList_SelectionChanged"
ContextFlyout="{StaticResource GameContextMenu}"
VerticalAlignment="Stretch"
Name="GameListBox"
Items="{Binding AppsObservableList}">
<ListBox
Name="GameListBox"
Grid.Row="0"
Padding="8"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource GameContextMenu}"
DoubleTapped="GameList_DoubleTapped"
Items="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Vertical" Spacing="2" />
<StackPanel
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Orientation="Vertical"
Spacing="2" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.Styles>
@ -112,16 +120,16 @@
<Style.Animations>
<Animation Duration="0:0:0.7">
<KeyFrame Cue="0%">
<Setter Property="MaxHeight" Value="0"/>
<Setter Property="Opacity" Value="0.0"/>
<Setter Property="MaxHeight" Value="0" />
<Setter Property="Opacity" Value="0.0" />
</KeyFrame>
<KeyFrame Cue="50%">
<Setter Property="MaxHeight" Value="1000"/>
<Setter Property="Opacity" Value="0.3"/>
<Setter Property="MaxHeight" Value="1000" />
<Setter Property="Opacity" Value="0.3" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="MaxHeight" Value="1000"/>
<Setter Property="Opacity" Value="1.0"/>
<Setter Property="MaxHeight" Value="1000" />
<Setter Property="Opacity" Value="1.0" />
</KeyFrame>
</Animation>
</Style.Animations>
@ -130,54 +138,96 @@
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border HorizontalAlignment="Stretch"
Padding="10" CornerRadius="5"
VerticalAlignment="Stretch" Margin="0" ClipToBounds="True">
<Grid >
<Border
Margin="0"
Padding="10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ClipToBounds="True"
CornerRadius="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition />
</Grid.RowDefinitions>
<Image
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
<Image
Grid.RowSpan="3"
Grid.Column="0"
Margin="0"
Classes.huge="{Binding $parent[UserControl].DataContext.IsGridHuge}"
Grid.RowSpan="3" Grid.Column="0" Margin="0"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<StackPanel Orientation="Vertical" Spacing="5" VerticalAlignment="Top" HorizontalAlignment="Left"
Grid.Column="2">
<TextBlock Text="{Binding TitleName}" TextAlignment="Left" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
<TextBlock Text="{Binding Developer}" TextAlignment="Left" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
<TextBlock Text="{Binding Version}" TextAlignment="Left" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="5" VerticalAlignment="Top" HorizontalAlignment="Right"
Grid.Column="3">
<TextBlock Text="{Binding TimePlayed}" TextAlignment="Right" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
<TextBlock Text="{Binding LastPlayed}" TextAlignment="Right" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
<TextBlock Text="{Binding FileSize}" TextAlignment="Right" TextWrapping="Wrap"
HorizontalAlignment="Stretch" />
</StackPanel>
<ui:SymbolIcon Grid.Row="0" Grid.Column="0" FontSize="20"
Foreground="Yellow"
Symbol="StarFilled"
IsVisible="{Binding Favorite}" Margin="-5, -5, 0, 0" VerticalAlignment="Top"
HorizontalAlignment="Left" />
<ui:SymbolIcon Grid.Row="0" Grid.Column="0" FontSize="20"
Foreground="Black"
Symbol="Star"
IsVisible="{Binding Favorite}" Margin="-5, -5, 0, 0" VerticalAlignment="Top"
HorizontalAlignment="Left" />
Classes.large="{Binding $parent[UserControl].DataContext.IsGridLarge}"
Classes.normal="{Binding $parent[UserControl].DataContext.IsGridMedium}"
Classes.small="{Binding $parent[UserControl].DataContext.IsGridSmall}"
Source="{Binding Icon, Converter={StaticResource ByteImage}}" />
<StackPanel
Grid.Column="2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Orientation="Vertical"
Spacing="5">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TitleName}"
TextAlignment="Left"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding Developer}"
TextAlignment="Left"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding Version}"
TextAlignment="Left"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
Grid.Column="3"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Orientation="Vertical"
Spacing="5">
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding TimePlayed}"
TextAlignment="Right"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding LastPlayed}"
TextAlignment="Right"
TextWrapping="Wrap" />
<TextBlock
HorizontalAlignment="Stretch"
Text="{Binding FileSize}"
TextAlignment="Right"
TextWrapping="Wrap" />
</StackPanel>
<ui:SymbolIcon
Grid.Row="0"
Grid.Column="0"
Margin="-5,-5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="20"
Foreground="Yellow"
IsVisible="{Binding Favorite}"
Symbol="StarFilled" />
<ui:SymbolIcon
Grid.Row="0"
Grid.Column="0"
Margin="-5,-5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="20"
Foreground="Black"
IsVisible="{Binding Favorite}"
Symbol="Star" />
</Grid>
</Border>
</Grid>

View File

@ -1,18 +1,31 @@
<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"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Controls.InputDialog">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,10,5, 5">
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.InputDialog"
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"
mc:Ignorable="d">
<Grid
Margin="5,10,5,5"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Center" Text="{Binding Message}" />
<TextBox MaxLength="{Binding MaxLength}" Grid.Row="1" Margin="10" Width="300" HorizontalAlignment="Center"
Text="{Binding Input, Mode=TwoWay}" />
<TextBlock Grid.Row="2" Margin="5, 5, 5, 10" HorizontalAlignment="Center" Text="{Binding SubMessage}" />
<TextBox
Grid.Row="1"
Width="300"
Margin="10"
HorizontalAlignment="Center"
MaxLength="{Binding MaxLength}"
Text="{Binding Input, Mode=TwoWay}" />
<TextBlock
Grid.Row="2"
Margin="5,5,5,10"
HorizontalAlignment="Center"
Text="{Binding SubMessage}" />
</Grid>
</UserControl>

View File

@ -1,14 +1,18 @@
<Window 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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.Ui.Controls.UpdateWaitWindow"
WindowStartupLocation="CenterOwner"
SizeToContent="WidthAndHeight"
Title="Ryujinx - Waiting">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="20">
<Window
x:Class="Ryujinx.Ava.Ui.Controls.UpdateWaitWindow"
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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx - Waiting"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<Grid
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -17,12 +21,22 @@
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Row="1" Margin="5, 10, 20 , 10" Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common"
Height="70"
MinWidth="50" />
<StackPanel Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Orientation="Vertical">
<TextBlock Margin="5" Name="PrimaryText" />
<TextBlock VerticalAlignment="Center" Name="SecondaryText" Margin="5" />
<Image
Grid.Row="1"
Height="70"
MinWidth="50"
Margin="5,10,20,10"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
<StackPanel
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Vertical">
<TextBlock Name="PrimaryText" Margin="5" />
<TextBlock
Name="SecondaryText"
Margin="5"
VerticalAlignment="Center" />
</StackPanel>
</Grid>
</Window>

View File

@ -1,28 +1,41 @@
<window:StyleableWindow 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"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
x:Class="Ryujinx.Ava.Ui.Windows.AboutWindow"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
CanResize="False"
WindowStartupLocation="CenterOwner"
Width="850" MinHeight="550" Height="550"
SizeToContent="Width"
MinWidth="500"
Title="Ryujinx - About">
<Grid Margin="15" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.AboutWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx - About"
Width="850"
Height="550"
MinWidth="500"
MinHeight="550"
d:DesignHeight="350"
d:DesignWidth="400"
CanResize="False"
SizeToContent="Width"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<Grid
Margin="15"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="1" Margin="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0">
<Grid
Grid.Row="1"
Grid.Column="0"
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
@ -40,93 +53,168 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Margin="5, 10, 20 , 10"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" Height="110" MinWidth="50" />
<TextBlock FontSize="35" TextAlignment="Center" Grid.Row="0" Grid.Column="1" Text="Ryujinx"
Margin="0,20,0,0" />
<TextBlock FontSize="16" TextAlignment="Center" Grid.Row="1" Grid.Column="1" Text="(REE-YOU-JINX)"
Margin="0,0,0,0" />
<Button Grid.Column="1" Background="Transparent" HorizontalAlignment="Center" Margin="0" Grid.Row="2"
Tag="https://www.ryujinx.org/"
Click="Button_OnClick">
<TextBlock ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}"
TextAlignment="Center" TextDecorations="Underline" Text="www.ryujinx.org" />
<Image
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="0"
Height="110"
MinWidth="50"
Margin="5,10,20,10"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="0,20,0,0"
FontSize="35"
Text="Ryujinx"
TextAlignment="Center" />
<TextBlock
Grid.Row="1"
Grid.Column="1"
Margin="0,0,0,0"
FontSize="16"
Text="(REE-YOU-JINX)"
TextAlignment="Center" />
<Button
Grid.Row="2"
Grid.Column="1"
Margin="0"
HorizontalAlignment="Center"
Background="Transparent"
Click="Button_OnClick"
Tag="https://www.ryujinx.org/">
<TextBlock
Text="www.ryujinx.org"
TextAlignment="Center"
TextDecorations="Underline"
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}" />
</Button>
</Grid>
<TextBlock TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{Binding Version}" Grid.Row="1" />
<TextBlock Grid.Row="2" TextAlignment="Center" HorizontalAlignment="Center" Margin="20"
Text="{locale:Locale AboutDisclaimerMessage}"
MaxLines="2" />
<TextBlock Grid.Row="3" TextAlignment="Center" HorizontalAlignment="Center" Margin="20"
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
Name="AmiiboLabel"
PointerPressed="AmiiboLabel_OnPointerPressed"
MaxLines="2" />
<StackPanel Spacing="10" Orientation="Horizontal" Grid.Row="4" HorizontalAlignment="Center">
<StackPanel Orientation="Vertical"
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
<Button Height="65" Background="Transparent" Tag="https://www.patreon.com/ryujinx"
Click="Button_OnClick">
<TextBlock
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Version}"
TextAlignment="Center" />
<TextBlock
Grid.Row="2"
Margin="20"
HorizontalAlignment="Center"
MaxLines="2"
Text="{locale:Locale AboutDisclaimerMessage}"
TextAlignment="Center" />
<TextBlock
Name="AmiiboLabel"
Grid.Row="3"
Margin="20"
HorizontalAlignment="Center"
MaxLines="2"
PointerPressed="AmiiboLabel_OnPointerPressed"
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
TextAlignment="Center" />
<StackPanel
Grid.Row="4"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="10">
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://www.patreon.com/ryujinx">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Patreon" HorizontalAlignment="Center" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Patreon" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical"
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Button Height="65" Background="Transparent" Tag="https://github.com/Ryujinx/Ryujinx"
Click="Button_OnClick">
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://github.com/Ryujinx/Ryujinx">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="GitHub" HorizontalAlignment="Center" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="GitHub" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical"
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
<Button Height="65" Background="Transparent" Tag="https://discordapp.com/invite/N2FmfVc"
Click="Button_OnClick">
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://discordapp.com/invite/N2FmfVc">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Discord" HorizontalAlignment="Center" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Discord" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical"
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
<Button Height="65" Background="Transparent" Tag="https://twitter.com/RyujinxEmu"
Click="Button_OnClick">
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://twitter.com/RyujinxEmu">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
<TextBlock Grid.Row="1" Margin="0,5,0,0" Text="Twitter" HorizontalAlignment="Center" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Twitter" />
</Grid>
</Button>
</StackPanel>
</StackPanel>
</Grid>
<Border Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch" Margin="5" Width="2" BorderBrush="White"
BorderThickness="1,0,0,0">
<Border
Grid.Row="1"
Grid.Column="1"
Width="2"
Margin="5"
VerticalAlignment="Stretch"
BorderBrush="White"
BorderThickness="1,0,0,0">
<Separator Width="0" />
</Border>
<Grid Grid.Row="1" Margin="20" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="2">
<Grid
Grid.Row="1"
Grid.Column="2"
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
@ -136,27 +224,58 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{locale:Locale AboutRyujinxAboutTitle}" FontWeight="Bold" TextDecorations="Underline" />
<TextBlock LineHeight="20" Grid.Row="1" Margin="20,5,5,5"
Text="{locale:Locale AboutRyujinxAboutContent}" />
<TextBlock Grid.Row="2" Margin="0,10,0,0" Text="{locale:Locale AboutRyujinxMaintainersTitle}"
FontWeight="Bold"
TextDecorations="Underline" />
<TextBlock LineHeight="20" Grid.Row="3" Margin="20,5,5,5"
Text="{Binding Developers}" />
<Button Background="Transparent" HorizontalAlignment="Right" Grid.Row="4"
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a" Click="Button_OnClick">
<TextBlock ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}"
TextAlignment="Right" TextDecorations="Underline"
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" />
<TextBlock
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxAboutTitle}"
TextDecorations="Underline" />
<TextBlock
Grid.Row="1"
Margin="20,5,5,5"
LineHeight="20"
Text="{locale:Locale AboutRyujinxAboutContent}" />
<TextBlock
Grid.Row="2"
Margin="0,10,0,0"
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxMaintainersTitle}"
TextDecorations="Underline" />
<TextBlock
Grid.Row="3"
Margin="20,5,5,5"
LineHeight="20"
Text="{Binding Developers}" />
<Button
Grid.Row="4"
HorizontalAlignment="Right"
Background="Transparent"
Click="Button_OnClick"
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
<TextBlock
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
TextAlignment="Right"
TextDecorations="Underline"
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button>
<TextBlock Grid.Row="5" Margin="0,0,0,0" Text="{locale:Locale AboutRyujinxSupprtersTitle}"
FontWeight="Bold"
TextDecorations="Underline" />
<Border Width="460" Grid.Row="6" VerticalAlignment="Stretch" Height="200" BorderThickness="1" Margin="20,5"
BorderBrush="White" Padding="5">
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top" Name="SupportersTextBlock"
Text="{Binding Supporters}" />
<TextBlock
Grid.Row="5"
Margin="0,0,0,0"
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxSupprtersTitle}"
TextDecorations="Underline" />
<Border
Grid.Row="6"
Width="460"
Height="200"
Margin="20,5"
Padding="5"
VerticalAlignment="Stretch"
BorderBrush="White"
BorderThickness="1">
<TextBlock
Name="SupportersTextBlock"
VerticalAlignment="Top"
Text="{Binding Supporters}"
TextWrapping="Wrap" />
</Border>
</Grid>
</Grid>

View File

@ -1,25 +1,25 @@
<window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.MainWindow"
xmlns="https://github.com/avaloniaui"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
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:models="clr-namespace:Ryujinx.Ava.Ui.Models"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx"
Height="785"
Width="1280"
d:DesignHeight="720"
d:DesignWidth="1280"
Height="785"
MinWidth="1024"
MinHeight="680"
WindowStartupLocation="CenterScreen"
d:DesignHeight="720"
d:DesignWidth="1280"
x:CompileBindings="True"
x:DataType="viewModels:MainWindowViewModel"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Window.Styles>
<Style Selector="TitleBar:fullscreen">
@ -38,23 +38,28 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<controls:OffscreenTextBox Name="HiddenTextBox" Grid.Row="0" />
<ContentControl Grid.Row="1"
Focusable="False"
IsVisible="False"
KeyboardNavigation.IsTabStop="False">
<ui:ContentDialog Name="ContentDialog"
KeyboardNavigation.IsTabStop="False"
IsPrimaryButtonEnabled="True"
IsSecondaryButtonEnabled="True"
IsVisible="True" />
<ContentControl
Grid.Row="1"
Focusable="False"
IsVisible="False"
KeyboardNavigation.IsTabStop="False">
<ui:ContentDialog
Name="ContentDialog"
IsPrimaryButtonEnabled="True"
IsSecondaryButtonEnabled="True"
IsVisible="True"
KeyboardNavigation.IsTabStop="False" />
</ContentControl>
<StackPanel IsVisible="False" Grid.Row="0">
<StackPanel Grid.Row="0" IsVisible="False">
<controls:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" />
<controls:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" />
<controls:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" />
<controls:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" />
</StackPanel>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1">
<Grid
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
@ -73,47 +78,51 @@
<DockPanel HorizontalAlignment="Stretch">
<Menu
Name="Menu"
Margin="0"
Height="35"
Margin="0"
HorizontalAlignment="Left">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel HorizontalAlignment="Stretch" Margin="0" />
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem
VerticalAlignment="Center"
Header="{locale:Locale MenuBarFile}">
<MenuItem IsEnabled="{Binding EnableNonGameRunningControls}"
Command="{ReflectionBinding OpenFile}"
Header="{locale:Locale MenuBarFileOpenFromFile}"
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
<MenuItem IsEnabled="{Binding EnableNonGameRunningControls}"
Command="{ReflectionBinding OpenFolder}"
Header="{locale:Locale MenuBarFileOpenUnpacked}"
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}"
IsEnabled="{Binding IsAppletMenuActive}">
<MenuItem Command="{ReflectionBinding OpenMiiApplet}" Header="Mii Edit Applet"
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
<MenuItem
Command="{ReflectionBinding OpenFile}"
Header="{locale:Locale MenuBarFileOpenFromFile}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenFolder}"
Header="{locale:Locale MenuBarFileOpenUnpacked}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
<MenuItem
Command="{ReflectionBinding OpenMiiApplet}"
Header="Mii Edit Applet"
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
</MenuItem>
<Separator />
<MenuItem Command="{ReflectionBinding OpenRyujinxFolder}"
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
<MenuItem Command="{ReflectionBinding OpenLogsFolder}"
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenRyujinxFolder}"
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenLogsFolder}"
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
<Separator />
<MenuItem Command="{ReflectionBinding CloseWindow}"
Header="{locale:Locale MenuBarFileExit}"
ToolTip.Tip="{locale:Locale ExitTooltip}" />
<MenuItem
Command="{ReflectionBinding CloseWindow}"
Header="{locale:Locale MenuBarFileExit}"
ToolTip.Tip="{locale:Locale ExitTooltip}" />
</MenuItem>
<MenuItem
VerticalAlignment="Center"
Header="{locale:Locale MenuBarOptions}">
<MenuItem Command="{ReflectionBinding ToggleFullscreen}"
Header="{locale:Locale MenuBarOptionsToggleFullscreen}" InputGesture="F11" />
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
<MenuItem
Command="{ReflectionBinding ToggleFullscreen}"
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
InputGesture="F11" />
<MenuItem Header="{locale:Locale MenuBarOptionsStartGamesInFullscreen}">
<MenuItem.Icon>
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}" />
@ -126,60 +135,82 @@
</MenuItem>
<Separator />
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="en_US"
Header="American English" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="pt_BR"
Header="Brazilian Portuguese" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="es_ES"
Header="Castilian Spanish" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="fr_FR"
Header="French" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="de_DE"
Header="German" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="el_GR"
Header="Greek" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="it_IT"
Header="Italian" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="ko_KR"
Header="Korean" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="ru_RU"
Header="Russian" />
<MenuItem Command="{ReflectionBinding ChangeLanguage}" CommandParameter="tr_TR"
Header="Turkish" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="en_US"
Header="American English" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pt_BR"
Header="Brazilian Portuguese" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="es_ES"
Header="Castilian Spanish" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="fr_FR"
Header="French" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="de_DE"
Header="German" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="el_GR"
Header="Greek" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="it_IT"
Header="Italian" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ko_KR"
Header="Korean" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"
Header="Russian" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="tr_TR"
Header="Turkish" />
</MenuItem>
<Separator />
<MenuItem Command="{ReflectionBinding OpenSettings}"
Header="{locale:Locale MenuBarOptionsSettings}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
<MenuItem Command="{ReflectionBinding ManageProfiles}"
IsEnabled="{Binding EnableNonGameRunningControls}"
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenSettings}"
Header="{locale:Locale MenuBarOptionsSettings}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
<MenuItem
Command="{ReflectionBinding ManageProfiles}"
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
</MenuItem>
<MenuItem
Name="ActionsMenuItem"
VerticalAlignment="Center"
Header="{locale:Locale MenuBarActions}"
Name="ActionsMenuItem"
IsEnabled="{Binding IsGameRunning}">
<MenuItem
Click="PauseEmulation_Click"
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding !IsPaused}"
IsVisible="{Binding !IsPaused}"
InputGesture="{Binding PauseKey}" />
IsVisible="{Binding !IsPaused}" />
<MenuItem
Click="ResumeEmulation_Click"
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding IsPaused}"
IsVisible="{Binding IsPaused}"
InputGesture="{Binding PauseKey}" />
IsVisible="{Binding IsPaused}" />
<MenuItem
Click="StopEmulation_Click"
Header="{locale:Locale MenuBarOptionsStopEmulation}"
ToolTip.Tip="{locale:Locale StopEmulationTooltip}"
IsEnabled="{Binding IsGameRunning}" InputGesture="Escape" />
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}"
Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}"
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
<Separator />
<MenuItem
Name="ScanAmiiboMenuItem"
@ -187,41 +218,38 @@
Command="{ReflectionBinding OpenAmiiboWindow}"
Header="{locale:Locale MenuBarActionsScanAmiibo}"
IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem Command="{ReflectionBinding TakeScreenshot}"
IsEnabled="{Binding IsGameRunning}"
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
InputGesture="{Binding ScreenshotKey}" />
<MenuItem Command="{ReflectionBinding HideUi}"
IsEnabled="{Binding IsGameRunning}"
Header="{locale:Locale MenuBarFileToolsHideUi}"
InputGesture="{Binding ShowUiKey}" />
<MenuItem Command="{ReflectionBinding OpenCheatManagerForCurrentApp}"
IsEnabled="{Binding IsGameRunning}"
Header="{locale:Locale GameListContextMenuManageCheat}" />
<MenuItem
Command="{ReflectionBinding TakeScreenshot}"
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
InputGesture="{Binding ScreenshotKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{ReflectionBinding HideUi}"
Header="{locale:Locale MenuBarFileToolsHideUi}"
InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{ReflectionBinding OpenCheatManagerForCurrentApp}"
Header="{locale:Locale GameListContextMenuManageCheat}"
IsEnabled="{Binding IsGameRunning}" />
</MenuItem>
<MenuItem
VerticalAlignment="Center"
Header="{locale:Locale MenuBarTools}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}"
IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}"
Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}"
Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</MenuItem>
</MenuItem>
<MenuItem
VerticalAlignment="Center"
Header="{locale:Locale MenuBarHelp}">
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem
Name="UpdateMenuItem"
Command="{ReflectionBinding CheckForUpdates}"
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
<Separator />
<MenuItem Command="{ReflectionBinding OpenAboutWindow}"
Header="{locale:Locale MenuBarHelpAbout}"
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenAboutWindow}"
Header="{locale:Locale MenuBarHelpAbout}"
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
</MenuItem>
</Menu>
</DockPanel>
@ -230,152 +258,213 @@
Name="Content"
Grid.Row="1"
Padding="0"
IsVisible="{Binding ShowContent}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="0,0,0,0"
DockPanel.Dock="Top">
DockPanel.Dock="Top"
IsVisible="{Binding ShowContent}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" HorizontalAlignment="Stretch" Margin="0,0,0,5">
<DockPanel
Grid.Row="0"
Margin="0,0,0,5"
HorizontalAlignment="Stretch">
<Button
IsEnabled="{Binding IsGrid}" VerticalAlignment="Stretch" MinWidth="40" Width="40"
Margin="5,2,0,2" Command="{ReflectionBinding SetListMode}">
<ui:FontIcon FontFamily="avares://FluentAvalonia/Fonts#Symbols"
VerticalAlignment="Center"
Margin="0"
Glyph="{controls:GlyphValueConverter List}"
HorizontalAlignment="Stretch" />
Width="40"
MinWidth="40"
Margin="5,2,0,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetListMode}"
IsEnabled="{Binding IsGrid}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{controls:GlyphValueConverter List}" />
</Button>
<Button
IsEnabled="{Binding IsList}" VerticalAlignment="Stretch" MinWidth="40" Width="40"
Margin="5,2,5,2" Command="{ReflectionBinding SetGridMode}">
<ui:FontIcon FontFamily="avares://FluentAvalonia/Fonts#Symbols"
VerticalAlignment="Center"
Margin="0"
Glyph="{controls:GlyphValueConverter Grid}"
HorizontalAlignment="Stretch" />
Width="40"
MinWidth="40"
Margin="5,2,5,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetGridMode}"
IsEnabled="{Binding IsList}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{controls:GlyphValueConverter Grid}" />
</Button>
<TextBlock Text="{locale:Locale IconSize}"
VerticalAlignment="Center" Margin="10,0"
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
<Slider Width="150" Margin="5,-10,5 ,0" Height="35"
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
VerticalAlignment="Center" Minimum="1" Maximum="4" IsSnapToTickEnabled="True"
TickFrequency="1" Value="{Binding GridSizeScale}" />
<CheckBox Margin="0" IsChecked="{Binding ShowNames, Mode=TwoWay}" VerticalAlignment="Center"
IsVisible="{Binding IsGrid}">
<TextBlock Text="{locale:Locale CommonShowNames}" Margin="5,3,0,0" />
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
Text="{locale:Locale IconSize}"
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
<Slider
Width="150"
Height="35"
Margin="5,-10,5,0"
VerticalAlignment="Center"
IsSnapToTickEnabled="True"
Maximum="4"
Minimum="1"
TickFrequency="1"
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
Value="{Binding GridSizeScale}" />
<CheckBox
Margin="0"
VerticalAlignment="Center"
IsChecked="{Binding ShowNames, Mode=TwoWay}"
IsVisible="{Binding IsGrid}">
<TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
</CheckBox>
<TextBox
Name="SearchBox"
DockPanel.Dock="Right"
VerticalAlignment="Center"
MinWidth="200"
Margin="5,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
KeyUp="SearchBox_OnKeyUp"
Text="{Binding SearchText}"
Watermark="{locale:Locale MenuSearch}" />
<ui:DropDownButton DockPanel.Dock="Right"
HorizontalAlignment="Right" Width="150" VerticalAlignment="Center"
Content="{Binding SortName}">
<ui:DropDownButton
Width="150"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{Binding SortName}"
DockPanel.Dock="Right">
<ui:DropDownButton.Flyout>
<Flyout Placement="Bottom">
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Margin="0">
<StackPanel
Margin="0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel>
<RadioButton Tag="Favorite"
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
GroupName="Sort"
Content="{locale:Locale CommonFavorite}"
Checked="Sort_Checked" />
<RadioButton Tag="Title" GroupName="Sort"
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
Content="{locale:Locale GameListHeaderApplication}"
Checked="Sort_Checked" />
<RadioButton Tag="Developer" GroupName="Sort"
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
Content="{locale:Locale GameListHeaderDeveloper}"
Checked="Sort_Checked" />
<RadioButton Tag="TotalTimePlayed" GroupName="Sort"
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
Content="{locale:Locale GameListHeaderTimePlayed}"
Checked="Sort_Checked" />
<RadioButton Tag="LastPlayed" GroupName="Sort"
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
Content="{locale:Locale GameListHeaderLastPlayed}"
Checked="Sort_Checked" />
<RadioButton Tag="FileType" GroupName="Sort"
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
Content="{locale:Locale GameListHeaderFileExtension}"
Checked="Sort_Checked" />
<RadioButton Tag="FileSize" GroupName="Sort"
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
Content="{locale:Locale GameListHeaderFileSize}"
Checked="Sort_Checked" />
<RadioButton Tag="Path" GroupName="Sort"
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
Content="{locale:Locale GameListHeaderPath}"
Checked="Sort_Checked" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale CommonFavorite}"
GroupName="Sort"
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
Tag="Favorite" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderApplication}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
Tag="Title" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderDeveloper}"
GroupName="Sort"
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
Tag="Developer" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderTimePlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
Tag="TotalTimePlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderLastPlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
Tag="LastPlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileExtension}"
GroupName="Sort"
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
Tag="FileType" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileSize}"
GroupName="Sort"
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
Tag="FileSize" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderPath}"
GroupName="Sort"
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
Tag="Path" />
</StackPanel>
<Border HorizontalAlignment="Stretch" Margin="5" Height="2" BorderBrush="White"
Width="60" BorderThickness="0,1,0,0">
<Separator HorizontalAlignment="Stretch" Height="0" />
<Border
Width="60"
Height="2"
Margin="5"
HorizontalAlignment="Stretch"
BorderBrush="White"
BorderThickness="0,1,0,0">
<Separator Height="0" HorizontalAlignment="Stretch" />
</Border>
<RadioButton Tag="Ascending" IsChecked="{Binding IsAscending, Mode=OneTime}"
GroupName="Order"
Content="{locale:Locale OrderAscending}" Checked="Order_Checked" />
<RadioButton Tag="Descending" GroupName="Order"
IsChecked="{Binding !IsAscending, Mode=OneTime}"
Content="{locale:Locale OrderDescending}" Checked="Order_Checked" />
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderAscending}"
GroupName="Order"
IsChecked="{Binding IsAscending, Mode=OneTime}"
Tag="Ascending" />
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderDescending}"
GroupName="Order"
IsChecked="{Binding !IsAscending, Mode=OneTime}"
Tag="Descending" />
</StackPanel>
</Flyout>
</ui:DropDownButton.Flyout>
</ui:DropDownButton>
<TextBlock DockPanel.Dock="Right" HorizontalAlignment="Right"
Text="{locale:Locale CommonSort}" VerticalAlignment="Center" Margin="10,0" />
<TextBlock
Margin="10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Text="{locale:Locale CommonSort}" />
</DockPanel>
<controls:GameListView
x:Name="GameList"
Grid.Row="1"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsVisible="{Binding IsList}" />
<controls:GameGridView
x:Name="GameGrid"
Grid.Row="1"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsVisible="{Binding IsGrid}" />
</Grid>
</ContentControl>
<Grid Grid.Row="1"
VerticalAlignment="Stretch"
Background="{DynamicResource ThemeContentBackgroundColor}"
IsVisible="{Binding ShowLoadProgress}"
ZIndex="1000"
HorizontalAlignment="Stretch">
<Grid
Grid.Row="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{DynamicResource ThemeContentBackgroundColor}"
IsVisible="{Binding ShowLoadProgress}"
ZIndex="1000">
<Grid
HorizontalAlignment="Center"
IsVisible="{Binding ShowLoadProgress}"
Margin="40"
VerticalAlignment="Center">
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsVisible="{Binding ShowLoadProgress}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.Column="0"
Grid.RowSpan="2"
IsVisible="{Binding ShowLoadProgress}"
Grid.Column="0"
Width="256"
Height="256"
Margin="10"
@ -383,62 +472,64 @@
BorderBrush="Black"
BorderThickness="2"
BoxShadow="4 4 32 8 #40000000"
CornerRadius="3">
CornerRadius="3"
IsVisible="{Binding ShowLoadProgress}">
<Image
IsVisible="{Binding ShowLoadProgress}"
Width="256"
Height="256"
IsVisible="{Binding ShowLoadProgress}"
Source="{Binding SelectedIcon, Converter={StaticResource ByteImage}}" />
</Border>
<Grid Grid.Column="1"
IsVisible="{Binding ShowLoadProgress}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center">
<Grid
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsVisible="{Binding ShowLoadProgress}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
IsVisible="{Binding ShowLoadProgress}"
Grid.Row="0"
Margin="10"
FontSize="30"
FontWeight="Bold"
TextWrapping="Wrap"
Text="{Binding LoadHeading}"
TextAlignment="Left" />
<Border
IsVisible="{Binding ShowLoadProgress}"
Text="{Binding LoadHeading}"
TextAlignment="Left"
TextWrapping="Wrap" />
<Border
Grid.Row="1"
CornerRadius="5"
ClipToBounds="True"
BorderBrush="{Binding ProgressBarBackgroundColor}"
Margin="10"
Padding="0"
HorizontalAlignment="Stretch"
Margin="10"
BorderThickness="1">
BorderBrush="{Binding ProgressBarBackgroundColor}"
BorderThickness="1"
ClipToBounds="True"
CornerRadius="5"
IsVisible="{Binding ShowLoadProgress}">
<ProgressBar
IsVisible="{Binding ShowLoadProgress}"
Height="10"
MinWidth="500"
Margin="0"
Padding="0"
CornerRadius="5"
ClipToBounds="True"
MinWidth="500"
HorizontalAlignment="Stretch"
Background="{Binding ProgressBarBackgroundColor}"
ClipToBounds="True"
CornerRadius="5"
Foreground="{Binding ProgressBarForegroundColor}"
IsIndeterminate="{Binding IsLoadingIndeterminate}"
IsVisible="{Binding ShowLoadProgress}"
Maximum="{Binding ProgressMaximum}"
Minimum="0"
IsIndeterminate="{Binding IsLoadingIndeterminate}"
Value="{Binding ProgressValue}" />
</Border>
<TextBlock
IsVisible="{Binding ShowLoadProgress}"
Grid.Row="2"
Margin="10"
FontSize="18"
IsVisible="{Binding ShowLoadProgress}"
Text="{Binding CacheLoadStatus}"
TextAlignment="Left" />
</Grid>
@ -450,8 +541,8 @@
Height="30"
Margin="0,0"
HorizontalAlignment="Stretch"
Background="{DynamicResource ThemeContentBackgroundColor}"
VerticalAlignment="Bottom"
Background="{DynamicResource ThemeContentBackgroundColor}"
DockPanel.Dock="Bottom"
IsVisible="{Binding ShowMenuAndStatusBar}">
<Grid.ColumnDefinitions>
@ -460,8 +551,11 @@
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" IsVisible="{Binding EnableNonGameRunningControls}"
VerticalAlignment="Center" Margin="10,0">
<StackPanel
Grid.Column="0"
Margin="10,0"
VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}">
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
@ -476,7 +570,10 @@
VerticalAlignment="Center"
Background="Transparent"
Command="{ReflectionBinding LoadApplications}">
<ui:SymbolIcon Symbol="Refresh" Height="100" Width="50" />
<ui:SymbolIcon
Width="50"
Height="100"
Symbol="Refresh" />
</Button>
<TextBlock
Name="LoadStatus"
@ -489,11 +586,11 @@
Name="LoadProgressBar"
Grid.Column="2"
Height="6"
Maximum="{Binding StatusBarProgressMaximum}"
Value="{Binding StatusBarProgressValue}"
VerticalAlignment="Center"
Foreground="{DynamicResource HighlightColor}"
IsVisible="{Binding EnableNonGameRunningControls}" />
IsVisible="{Binding EnableNonGameRunningControls}"
Maximum="{Binding StatusBarProgressMaximum}"
Value="{Binding StatusBarProgressValue}" />
</Grid>
</StackPanel>
<StackPanel
@ -505,132 +602,137 @@
Orientation="Horizontal">
<TextBlock
Name="VsyncStatus"
Margin="0,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{Binding VsyncColor}"
PointerReleased="VsyncStatus_PointerReleased"
IsVisible="{Binding !ShowLoadProgress}"
Margin="0,0,5,0"
PointerReleased="VsyncStatus_PointerReleased"
Text="VSync"
TextAlignment="Left" />
<Border
Width="2"
Margin="2,0"
IsVisible="{Binding !ShowLoadProgress}"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
Name="DockedStatus"
IsVisible="{Binding !ShowLoadProgress}"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="DockedStatus_PointerReleased"
Text="{Binding DockedStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Margin="2,0"
IsVisible="{Binding !ShowLoadProgress}"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
Name="AspectRatioStatus"
IsVisible="{Binding !ShowLoadProgress}"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="AspectRatioStatus_PointerReleased"
Text="{Binding AspectRatioStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Margin="2,0"
IsVisible="{Binding !ShowLoadProgress}"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<ui:ToggleSplitButton
Name="VolumeStatus"
Margin="-2,0,-3,0"
Padding="5,0,0,5"
Name="VolumeStatus"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
BorderBrush="{DynamicResource ThemeContentBackgroundColor}"
Background="{DynamicResource ThemeContentBackgroundColor}"
BorderBrush="{DynamicResource ThemeContentBackgroundColor}"
Content="{Binding VolumeStatusText}"
IsChecked="{Binding VolumeMuted}"
Content="{Binding VolumeStatusText}">
IsVisible="{Binding !ShowLoadProgress}">
<ui:ToggleSplitButton.Flyout>
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
<Grid Margin="0">
<Slider Value="{Binding Volume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Minimum="0"
Maximum="1"
TickFrequency="0.05"
IsSnapToTickEnabled="True"
Padding="0"
Margin="0"
SmallChange="0.01"
LargeChange="0.05"
Width="150" />
<Slider
Width="150"
Margin="0"
Padding="0"
IsSnapToTickEnabled="True"
LargeChange="0.05"
Maximum="1"
Minimum="0"
SmallChange="0.01"
TickFrequency="0.05"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Value="{Binding Volume}" />
</Grid>
</Flyout>
</ui:ToggleSplitButton.Flyout>
</ui:ToggleSplitButton>
<Border
Width="2"
Margin="2,0"
IsVisible="{Binding !ShowLoadProgress}"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
IsVisible="{Binding !ShowLoadProgress}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GameStatusText}"
TextAlignment="Left" />
<Border
Width="2"
IsVisible="{Binding !ShowLoadProgress}"
Margin="2,0"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
IsVisible="{Binding !ShowLoadProgress}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding FifoStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Margin="2,0"
IsVisible="{Binding !ShowLoadProgress}"
BorderThickness="1"
Height="12"
BorderBrush="Gray" />
Margin="2,0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
IsVisible="{Binding !ShowLoadProgress}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GpuStatusText}"
TextAlignment="Left" />
</StackPanel>
<StackPanel VerticalAlignment="Center" IsVisible="{Binding ShowFirmwareStatus}" Grid.Column="3"
Orientation="Horizontal" Margin="10, 0">
<StackPanel
Grid.Column="3"
Margin="10,0"
VerticalAlignment="Center"
IsVisible="{Binding ShowFirmwareStatus}"
Orientation="Horizontal">
<TextBlock
Name="FirmwareStatus"
Margin="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0"
Text="{locale:Locale StatusBarSystemVersion}" />
</StackPanel>
</Grid>

View File

@ -1,18 +1,26 @@
<window:StyleableWindow 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"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="350"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
x:Class="Ryujinx.Ava.Ui.Windows.UpdaterWindow"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
CanResize="False"
SizeToContent="Height"
Width="500" MinHeight="500" Height="500"
WindowStartupLocation="CenterOwner"
MinWidth="500"
Title="Ryujinx Updater">
<Grid Margin="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<window:StyleableWindow
x:Class="Ryujinx.Ava.Ui.Windows.UpdaterWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx Updater"
Width="500"
Height="500"
MinWidth="500"
MinHeight="500"
d:DesignHeight="350"
d:DesignWidth="400"
CanResize="False"
SizeToContent="Height"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<Grid
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
@ -20,17 +28,38 @@
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" HorizontalAlignment="Stretch" TextAlignment="Center" Height="20" Name="MainText" />
<TextBlock Height="20" HorizontalAlignment="Stretch" TextAlignment="Center" Name="SecondaryText" Grid.Row="2" />
<ProgressBar IsVisible="False" HorizontalAlignment="Stretch" Name="ProgressBar" Maximum="100" Minimum="0"
Margin="20" Grid.Row="3" />
<StackPanel IsVisible="False" Name="ButtonBox" Orientation="Horizontal" Spacing="20" Grid.Row="4"
HorizontalAlignment="Right">
<Button Command="{Binding YesPressed}" MinWidth="50">
<TextBlock TextAlignment="Center" Text="{locale:Locale InputDialogYes}" />
<TextBlock
Name="MainText"
Grid.Row="1"
Height="20"
HorizontalAlignment="Stretch"
TextAlignment="Center" />
<TextBlock
Name="SecondaryText"
Grid.Row="2"
Height="20"
HorizontalAlignment="Stretch"
TextAlignment="Center" />
<ProgressBar
Name="ProgressBar"
Grid.Row="3"
Margin="20"
HorizontalAlignment="Stretch"
IsVisible="False"
Maximum="100"
Minimum="0" />
<StackPanel
Name="ButtonBox"
Grid.Row="4"
HorizontalAlignment="Right"
IsVisible="False"
Orientation="Horizontal"
Spacing="20">
<Button MinWidth="50" Command="{Binding YesPressed}">
<TextBlock Text="{locale:Locale InputDialogYes}" TextAlignment="Center" />
</Button>
<Button Command="{Binding NoPressed}" MinWidth="50">
<TextBlock TextAlignment="Center" Text="{locale:Locale InputDialogNo}" />
<Button MinWidth="50" Command="{Binding NoPressed}">
<TextBlock Text="{locale:Locale InputDialogNo}" TextAlignment="Center" />
</Button>
</StackPanel>
</Grid>

View File

@ -0,0 +1,12 @@
namespace Ryujinx.Common.Memory
{
public class Box<T> where T : unmanaged
{
public T Data;
public Box()
{
Data = new T();
}
}
}

View File

@ -101,6 +101,16 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The GPU resource with the given ID</returns>
public abstract T1 Get(int id);
/// <summary>
/// Checks if a given ID is valid and inside the range of the pool.
/// </summary>
/// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
/// <returns>True if the specified ID is valid, false otherwise</returns>
public bool IsValidId(int id)
{
return (uint)id <= MaximumId;
}
/// <summary>
/// Synchronizes host memory with guest memory.
/// This causes invalidation of pool entries,

View File

@ -32,6 +32,9 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly GpuChannel _channel;
private readonly TexturePoolCache _texturePoolCache;
private TexturePool _cachedTexturePool;
private SamplerPool _cachedSamplerPool;
private readonly TextureBindingInfo[][] _textureBindings;
private readonly TextureBindingInfo[][] _imageBindings;
@ -343,9 +346,14 @@ namespace Ryujinx.Graphics.Gpu.Image
? _texturePoolCache.FindOrCreate(_channel, texturePoolAddress, _texturePoolMaximumId)
: null;
SamplerPool samplerPool = _samplerPool;
// Check if the texture pool has been modified since bindings were last committed.
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
bool poolModified = false;
bool poolModified = _cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool;
_cachedTexturePool = texturePool;
_cachedSamplerPool = samplerPool;
if (texturePool != null)
{
@ -358,9 +366,9 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
if (_samplerPool != null)
if (samplerPool != null)
{
int samplerPoolSequence = _samplerPool.CheckModified();
int samplerPoolSequence = samplerPool.CheckModified();
if (_samplerPoolSequence != samplerPoolSequence)
{
@ -738,7 +746,22 @@ namespace Ryujinx.Graphics.Gpu.Image
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId);
return texturePool.GetDescriptor(textureId);
TextureDescriptor descriptor;
if (texturePool.IsValidId(textureId))
{
descriptor = texturePool.GetDescriptor(textureId);
}
else
{
// If the ID is not valid, we just return a default descriptor with the most common state.
// Since this is used for shader specialization, doing so might avoid the need for recompilations.
descriptor = new TextureDescriptor();
descriptor.Word4 |= (uint)TextureTarget.Texture2D << 23;
descriptor.Word5 |= 1u << 31; // Coords normalized.
}
return descriptor;
}
/// <summary>

View File

@ -241,25 +241,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
}
/// <summary>
/// Create the equivalent of this TextureDescriptor for the shader cache.
/// </summary>
/// <returns>The equivalent of this TextureDescriptor for the shader cache.</returns>
public GuestTextureDescriptor ToCache()
{
GuestTextureDescriptor result = new GuestTextureDescriptor
{
Handle = uint.MaxValue,
Format = UnpackFormat(),
Target = UnpackTextureTarget(),
IsSrgb = UnpackSrgb(),
IsTextureCoordNormalized = UnpackTextureCoordNormalized(),
};
return result;
}
/// <summary>
/// Check if two descriptors are equal.
/// </summary>

View File

@ -579,14 +579,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
textureKey.StageIndex);
int packedId = TextureHandle.ReadPackedId(textureKey.Handle, cachedTextureBuffer, cachedSamplerBuffer);
int textureId = TextureHandle.UnpackTextureId(packedId);
ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
if (!MatchesTexture(kv.Value, descriptor))
if (pool.IsValidId(textureId))
{
return false;
ref readonly Image.TextureDescriptor descriptor = ref pool.GetDescriptorRef(textureId);
if (!MatchesTexture(kv.Value, descriptor))
{
return false;
}
}
}
}

View File

@ -966,6 +966,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
SignalExitToDebugExited();
SignalExit();
}
KernelStatic.GetCurrentThread().Exit();
}
private void UnpauseAndTerminateAllThreadsExcept(KThread currentThread)
@ -981,7 +983,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
foreach (KThread thread in _threads)
{
if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
if (thread != currentThread && (thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
{
thread.PrepareForTermination();
}

View File

@ -2,7 +2,10 @@ using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities;
using Ryujinx.Memory;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
@ -100,15 +103,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
string locationName = StringUtils.ReadInlinedAsciiString(context.RequestData, 0x24);
ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName);
// Write TimeZoneRule if success
if (resultCode == ResultCode.Success)
using (WritableRegion region = context.Memory.GetWritableRegion(bufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
{
MemoryHelper.Write(context.Memory, bufferPosition, rules);
}
ref TimeZoneRule rules = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
return resultCode;
return _timeZoneContentManager.LoadTimeZoneRule(ref rules, locationName);
}
}
[CommandHipc(100)]

View File

@ -4,9 +4,12 @@ using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.HLE.Utilities;
using Ryujinx.Memory;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.StaticService
{
@ -165,11 +168,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
using (MemoryStream timeZoneBinaryStream = new MemoryStream(temp))
{
result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
if (result == ResultCode.Success)
using (WritableRegion region = context.Memory.GetWritableRegion(timeZoneRuleBufferPosition, Unsafe.SizeOf<TimeZoneRule>()))
{
MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
ref TimeZoneRule rule = ref MemoryMarshal.Cast<byte, TimeZoneRule>(region.Memory.Span)[0];
result = _timeZoneManager.ParseTimeZoneRuleBinary(ref rule, timeZoneBinaryStream);
}
}
@ -199,9 +202,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
throw new InvalidOperationException();
}
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(bufferPosition, (int)bufferSize));
ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
ResultCode resultCode = _timeZoneManager.ToCalendarTime(in rules[0], posixTime, out CalendarInfo calendar);
if (resultCode == 0)
{
@ -244,9 +247,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
throw new InvalidOperationException();
}
TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
ReadOnlySpan<TimeZoneRule> rules = MemoryMarshal.Cast<byte, TimeZoneRule>(context.Memory.GetSpan(inBufferPosition, (int)inBufferSize));
ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
ResultCode resultCode = _timeZoneManager.ToPosixTime(in rules[0], calendarTime, out long posixTime);
if (resultCode == ResultCode.Success)
{

View File

@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.Utilities;
using System;
@ -38,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
new int[] { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
private const string TimeZoneDefaultRule = ",M4.1.0,M10.5.0";
private static readonly byte[] TimeZoneDefaultRule = Encoding.ASCII.GetBytes(",M4.1.0,M10.5.0");
[StructLayout(LayoutKind.Sequential, Pack = 0x4, Size = 0x10)]
private struct CalendarTimeInternal
@ -133,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return (t1 - t0) == SecondsPerRepeat;
}
private static bool TimeTypeEquals(TimeZoneRule outRules, byte aIndex, byte bIndex)
private static bool TimeTypeEquals(in TimeZoneRule outRules, byte aIndex, byte bIndex)
{
if (aIndex < 0 || aIndex >= outRules.TypeCount || bIndex < 0 || bIndex >= outRules.TypeCount)
{
@ -150,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
StringUtils.CompareCStr(outRules.Chars[a.AbbreviationListIndex..], outRules.Chars[b.AbbreviationListIndex..]) == 0;
}
private static int GetQZName(ReadOnlySpan<char> name, int namePosition, char delimiter)
private static int GetQZName(ReadOnlySpan<byte> name, int namePosition, char delimiter)
{
int i = namePosition;
@ -162,13 +163,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return i;
}
private static int GetTZName(char[] name, int namePosition)
private static int GetTZName(ReadOnlySpan<byte> name, int namePosition)
{
int i = namePosition;
char c;
while ((c = name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
while ((c = (char)name[i]) != '\0' && !char.IsDigit(c) && c != ',' && c != '-' && c != '+')
{
i++;
}
@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return i;
}
private static bool GetNum(char[] name, ref int namePosition, out int num, int min, int max)
private static bool GetNum(ReadOnlySpan<byte> name, ref int namePosition, out int num, int min, int max)
{
num = 0;
@ -185,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false;
}
char c = name[namePosition];
char c = (char)name[namePosition];
if (!char.IsDigit(c))
{
@ -205,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false;
}
c = name[namePosition];
c = (char)name[namePosition];
}
while (char.IsDigit(c));
@ -217,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true;
}
private static bool GetSeconds(char[] name, ref int namePosition, out int seconds)
private static bool GetSeconds(ReadOnlySpan<byte> name, ref int namePosition, out int seconds)
{
seconds = 0;
@ -266,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true;
}
private static bool GetOffset(char[] name, ref int namePosition, ref int offset)
private static bool GetOffset(ReadOnlySpan<byte> name, ref int namePosition, ref int offset)
{
bool isNegative = false;
@ -304,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return true;
}
private static bool GetRule(char[] name, ref int namePosition, out Rule rule)
private static bool GetRule(ReadOnlySpan<byte> name, ref int namePosition, out Rule rule)
{
rule = new Rule();
@ -347,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerWekk - 1);
}
else if (char.IsDigit(name[namePosition]))
else if (char.IsDigit((char)name[namePosition]))
{
rule.Type = RuleType.DayOfYear;
isValid = GetNum(name, ref namePosition, out rule.Day, 0, DaysPerLYear - 1);
@ -385,19 +386,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return 0;
}
private static bool ParsePosixName(ReadOnlySpan<char> name, out TimeZoneRule outRules, bool lastDitch)
private static bool ParsePosixName(ReadOnlySpan<byte> name, ref TimeZoneRule outRules, bool lastDitch)
{
outRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
outRules = new TimeZoneRule();
int stdLen;
ReadOnlySpan<char> stdName = name;
ReadOnlySpan<byte> stdName = name;
int namePosition = 0;
int stdOffset = 0;
@ -428,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
}
else
{
namePosition = GetTZName(name.ToArray(), namePosition);
namePosition = GetTZName(name, namePosition);
stdLen = namePosition;
}
@ -449,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
int destLen = 0;
int dstOffset = 0;
ReadOnlySpan<char> destName = name.Slice(namePosition);
ReadOnlySpan<byte> destName = name.Slice(namePosition);
if (TzCharsArraySize < charCount)
{
@ -476,7 +471,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
else
{
destName = name.Slice(namePosition);
namePosition = GetTZName(name.ToArray(), namePosition);
namePosition = GetTZName(name, namePosition);
destLen = namePosition;
}
@ -507,7 +502,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
if (name[namePosition] == '\0')
{
name = TimeZoneDefaultRule.ToCharArray();
name = TimeZoneDefaultRule;
namePosition = 0;
}
@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
namePosition++;
bool IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule start);
bool IsRuleValid = GetRule(name, ref namePosition, out Rule start);
if (!IsRuleValid)
{
return false;
@ -526,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return false;
}
IsRuleValid = GetRule(name.ToArray(), ref namePosition, out Rule end);
IsRuleValid = GetRule(name, ref namePosition, out Rule end);
if (!IsRuleValid)
{
return false;
@ -738,7 +733,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
}
charsPosition += stdLen;
outRules.Chars[charsPosition++] = '\0';
outRules.Chars[charsPosition++] = 0;
if (destLen != 0)
{
@ -746,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
outRules.Chars[charsPosition + i] = destName[i];
}
outRules.Chars[charsPosition + destLen] = '\0';
outRules.Chars[charsPosition + destLen] = 0;
}
return true;
@ -882,20 +877,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
}
}
internal static bool ParsePosixName(string name, out TimeZoneRule outRules)
internal static bool ParsePosixName(string name, ref TimeZoneRule outRules)
{
return ParsePosixName(name.ToCharArray(), out outRules, false);
return ParsePosixName(Encoding.ASCII.GetBytes(name), ref outRules, false);
}
internal static bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData)
internal static bool ParseTimeZoneBinary(ref TimeZoneRule outRules, Stream inputData)
{
outRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
outRules = new TimeZoneRule();
BinaryReader reader = new BinaryReader(inputData);
@ -1020,10 +1009,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
outRules.Ttis[i] = ttis;
}
Encoding.ASCII.GetChars(p[..outRules.CharCount].ToArray()).CopyTo(outRules.Chars.AsSpan());
p[..outRules.CharCount].CopyTo(outRules.Chars);
p = p[outRules.CharCount..];
outRules.Chars[outRules.CharCount] = '\0';
outRules.Chars[outRules.CharCount] = 0;
for (int i = 0; i < outRules.TypeCount; i++)
{
@ -1077,27 +1066,30 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
throw new InvalidOperationException();
}
char[] tempName = new char[TzNameMax + 1];
byte[] tempName = new byte[TzNameMax + 1];
Array.Copy(workBuffer, position, tempName, 0, nRead);
if (nRead > 2 && tempName[0] == '\n' && tempName[nRead - 1] == '\n' && outRules.TypeCount + 2 <= TzMaxTypes)
{
tempName[nRead - 1] = '\0';
tempName[nRead - 1] = 0;
char[] name = new char[TzNameMax];
byte[] name = new byte[TzNameMax];
Array.Copy(tempName, 1, name, 0, nRead - 1);
if (ParsePosixName(name, out TimeZoneRule tempRules, false))
Box<TimeZoneRule> tempRulesBox = new Box<TimeZoneRule>();
ref TimeZoneRule tempRules = ref tempRulesBox.Data;
if (ParsePosixName(name, ref tempRulesBox.Data, false))
{
int abbreviationCount = 0;
charCount = outRules.CharCount;
Span<char> chars = outRules.Chars;
Span<byte> chars = outRules.Chars;
for (int i = 0; i < tempRules.TypeCount; i++)
{
ReadOnlySpan<char> tempChars = tempRules.Chars;
ReadOnlySpan<char> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
ReadOnlySpan<byte> tempChars = tempRules.Chars;
ReadOnlySpan<byte> tempAbbreviation = tempChars[tempRules.Ttis[i].AbbreviationListIndex..];
int j;
@ -1175,7 +1167,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
for (int i = 1; i < outRules.TimeCount; i++)
{
if (TimeTypeEquals(outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
if (TimeTypeEquals(in outRules, outRules.Types[i], outRules.Types[0]) && DifferByRepeat(outRules.Ats[i], outRules.Ats[0]))
{
outRules.GoBack = true;
break;
@ -1184,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
for (int i = outRules.TimeCount - 2; i >= 0; i--)
{
if (TimeTypeEquals(outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
if (TimeTypeEquals(in outRules, outRules.Types[outRules.TimeCount - 1], outRules.Types[i]) && DifferByRepeat(outRules.Ats[outRules.TimeCount - 1], outRules.Ats[i]))
{
outRules.GoAhead = true;
break;
@ -1259,10 +1251,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
long remainingSeconds = time % SecondsPerDay;
calendarTime = new CalendarTimeInternal();
calendarAdditionalInfo = new CalendarAdditionalInfo()
{
TimezoneName = new char[8]
};
calendarAdditionalInfo = new CalendarAdditionalInfo();
while (timeDays < 0 || timeDays >= YearLengths[IsLeap((int)year)])
{
@ -1353,13 +1342,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return 0;
}
private static ResultCode ToCalendarTimeInternal(TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
private static ResultCode ToCalendarTimeInternal(in TimeZoneRule rules, long time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo)
{
calendarTime = new CalendarTimeInternal();
calendarAdditionalInfo = new CalendarAdditionalInfo()
{
TimezoneName = new char[8]
};
calendarAdditionalInfo = new CalendarAdditionalInfo();
ResultCode result;
@ -1398,7 +1384,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return ResultCode.TimeNotFound;
}
result = ToCalendarTimeInternal(rules, newTime, out calendarTime, out calendarAdditionalInfo);
result = ToCalendarTimeInternal(in rules, newTime, out calendarTime, out calendarAdditionalInfo);
if (result != 0)
{
return result;
@ -1450,17 +1436,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
calendarAdditionalInfo.IsDaySavingTime = rules.Ttis[ttiIndex].IsDaySavingTime;
ReadOnlySpan<char> timeZoneAbbreviation = rules.Chars.AsSpan()[rules.Ttis[ttiIndex].AbbreviationListIndex..];
ReadOnlySpan<byte> timeZoneAbbreviation = rules.Chars[rules.Ttis[ttiIndex].AbbreviationListIndex..];
int timeZoneSize = Math.Min(StringUtils.LengthCstr(timeZoneAbbreviation), 8);
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.AsSpan());
timeZoneAbbreviation[..timeZoneSize].CopyTo(calendarAdditionalInfo.TimezoneName.ToSpan());
}
return result;
}
private static ResultCode ToPosixTimeInternal(TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
private static ResultCode ToPosixTimeInternal(in TimeZoneRule rules, CalendarTimeInternal calendarTime, out long posixTime)
{
posixTime = 0;
@ -1604,7 +1590,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
int direction;
ResultCode result = ToCalendarTimeInternal(rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
ResultCode result = ToCalendarTimeInternal(in rules, pivot, out CalendarTimeInternal candidateCalendarTime, out _);
if (result != 0)
{
if (pivot > 0)
@ -1675,9 +1661,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return ResultCode.Success;
}
internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
internal static ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
{
ResultCode result = ToCalendarTimeInternal(rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
ResultCode result = ToCalendarTimeInternal(in rules, time, out CalendarTimeInternal calendarTime, out CalendarAdditionalInfo calendarAdditionalInfo);
calendar = new CalendarInfo()
{
@ -1697,7 +1683,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result;
}
internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
internal static ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
{
CalendarTimeInternal calendarTimeInternal = new CalendarTimeInternal()
{
@ -1710,7 +1696,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
Second = calendarTime.Second
};
return ToPosixTimeInternal(rules, calendarTimeInternal, out posixTime);
return ToPosixTimeInternal(in rules, calendarTimeInternal, out posixTime);
}
}
}

View File

@ -16,7 +16,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
using TimeZoneRuleBox = Ryujinx.Common.Memory.Box<Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule>;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
@ -149,7 +149,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
continue;
}
TimeZone.ParseTimeZoneBinary(out TimeZoneRule tzRule, tzif.Get.AsStream());
TimeZoneRuleBox tzRuleBox = new TimeZoneRuleBox();
ref TimeZoneRule tzRule = ref tzRuleBox.Data;
TimeZone.ParseTimeZoneBinary(ref tzRule, tzif.Get.AsStream());
TimeTypeInfo ttInfo;
if (tzRule.TimeCount > 0) // Find the current transition period
@ -174,10 +178,10 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
continue;
}
var abbrStart = tzRule.Chars.AsSpan(ttInfo.AbbreviationListIndex);
int abbrEnd = abbrStart.IndexOf('\0');
var abbrStart = tzRule.Chars[ttInfo.AbbreviationListIndex..];
int abbrEnd = abbrStart.IndexOf((byte)0);
outList.Add((ttInfo.GmtOffset, locName, abbrStart.Slice(0, abbrEnd).ToString()));
outList.Add((ttInfo.GmtOffset, locName, abbrStart[..abbrEnd].ToString()));
}
}
@ -276,15 +280,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return (ResultCode)result.Value;
}
internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName)
internal ResultCode LoadTimeZoneRule(ref TimeZoneRule rules, string locationName)
{
outRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
rules = default;
if (!HasTimeZoneBinaryTitle())
{
@ -295,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
if (result == ResultCode.Success)
{
result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream);
result = Manager.ParseTimeZoneRuleBinary(ref rules, timeZoneBinaryStream);
ncaFile.Dispose();
}

View File

@ -1,14 +1,14 @@
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.Utilities;
using System.IO;
using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
class TimeZoneManager
{
private bool _isInitialized;
private TimeZoneRule _myRules;
private Box<TimeZoneRule> _myRules;
private string _deviceLocationName;
private UInt128 _timeZoneRuleVersion;
private uint _totalLocationNameCount;
@ -21,15 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
_deviceLocationName = "UTC";
_timeZoneRuleVersion = new UInt128();
_lock = new object();
// Empty rules
_myRules = new TimeZoneRule
{
Ats = new long[TzMaxTimes],
Types = new byte[TzMaxTimes],
Ttis = new TimeTypeInfo[TzMaxTypes],
Chars = new char[TzCharsArraySize]
};
_myRules = new Box<TimeZoneRule>();
_timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
}
@ -78,7 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
lock (_lock)
{
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream);
Box<TimeZoneRule> rules = new Box<TimeZoneRule>();
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref rules.Data, timeZoneBinaryStream);
if (timeZoneConversionSuccess)
{
@ -154,13 +148,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result;
}
public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream)
public ResultCode ParseTimeZoneRuleBinary(ref TimeZoneRule outRules, Stream timeZoneBinaryStream)
{
ResultCode result = ResultCode.Success;
lock (_lock)
{
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream);
bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(ref outRules, timeZoneBinaryStream);
if (!timeZoneConversionSuccess)
{
@ -208,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
if (_isInitialized)
{
result = ToCalendarTime(_myRules, time, out calendar);
result = ToCalendarTime(in _myRules.Data, time, out calendar);
}
else
{
@ -220,13 +214,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result;
}
public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
public ResultCode ToCalendarTime(in TimeZoneRule rules, long time, out CalendarInfo calendar)
{
ResultCode result;
lock (_lock)
{
result = TimeZone.ToCalendarTime(rules, time, out calendar);
result = TimeZone.ToCalendarTime(in rules, time, out calendar);
}
return result;
@ -240,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
if (_isInitialized)
{
result = ToPosixTime(_myRules, calendarTime, out posixTime);
result = ToPosixTime(in _myRules.Data, calendarTime, out posixTime);
}
else
{
@ -252,13 +246,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
return result;
}
public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
public ResultCode ToPosixTime(in TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
{
ResultCode result;
lock (_lock)
{
result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
result = TimeZone.ToPosixTime(in rules, calendarTime, out posixTime);
}
return result;

View File

@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
@ -8,14 +9,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
public uint DayOfWeek;
public uint DayOfYear;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] TimezoneName;
public Array8<byte> TimezoneName;
[MarshalAs(UnmanagedType.I1)]
public bool IsDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] Padding;
public Array3<byte> Padding;
public int GmtOffset;
}

View File

@ -1,17 +1,19 @@
using System.Runtime.InteropServices;
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 4)]
struct TimeTypeInfo
[StructLayout(LayoutKind.Sequential, Size = Size, Pack = 4)]
public struct TimeTypeInfo
{
public const int Size = 0x10;
public int GmtOffset;
[MarshalAs(UnmanagedType.I1)]
public bool IsDaySavingTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public char[] Padding1;
public Array3<byte> Padding1;
public int AbbreviationListIndex;
@ -21,7 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
[MarshalAs(UnmanagedType.I1)]
public bool IsGMT;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public char[] Padding2;
public ushort Padding2;
}
}

View File

@ -1,16 +1,18 @@
using System.Runtime.InteropServices;
using Ryujinx.Common.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
{
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 0x4000, CharSet = CharSet.Ansi)]
struct TimeZoneRule
public struct TimeZoneRule
{
public const int TzMaxTypes = 128;
public const int TzMaxChars = 50;
public const int TzMaxLeaps = 50;
public const int TzMaxTimes = 1000;
public const int TzNameMax = 255;
public const int TzCharsArraySize = 2 * (TzNameMax + 1);
public const int TzMaxTypes = 128;
public const int TzMaxChars = 50;
public const int TzMaxLeaps = 50;
public const int TzMaxTimes = 1000;
public const int TzNameMax = 255;
public const int TzCharsArraySize = 2 * (TzNameMax + 1);
public int TimeCount;
public int TypeCount;
@ -22,17 +24,32 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
[MarshalAs(UnmanagedType.I1)]
public bool GoAhead;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
public long[] Ats;
[StructLayout(LayoutKind.Sequential, Size = sizeof(long) * TzMaxTimes)]
private struct AtsStorageStruct { }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTimes)]
public byte[] Types;
private AtsStorageStruct _ats;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzMaxTypes)]
public TimeTypeInfo[] Ttis;
public Span<long> Ats => SpanHelpers.AsSpan<AtsStorageStruct, long>(ref _ats);
[MarshalAs(UnmanagedType.ByValArray, SizeConst = TzCharsArraySize)]
public char[] Chars;
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzMaxTimes)]
private struct TypesStorageStruct { }
private TypesStorageStruct _types;
public Span<byte> Types => SpanHelpers.AsByteSpan(ref _types);
[StructLayout(LayoutKind.Sequential, Size = TimeTypeInfo.Size * TzMaxTypes)]
private struct TimeTypeInfoStorageStruct { }
private TimeTypeInfoStorageStruct _ttis;
public Span<TimeTypeInfo> Ttis => SpanHelpers.AsSpan<TimeTypeInfoStorageStruct, TimeTypeInfo>(ref _ttis);
[StructLayout(LayoutKind.Sequential, Size = sizeof(byte) * TzCharsArraySize)]
private struct CharsStorageStruct { }
private CharsStorageStruct _chars;
public Span<byte> Chars => SpanHelpers.AsByteSpan(ref _chars);
public int DefaultType;
}

View File

@ -128,7 +128,7 @@ namespace Ryujinx.HLE.Utilities
}
}
public static int CompareCStr(ReadOnlySpan<char> s1, ReadOnlySpan<char> s2)
public static int CompareCStr(ReadOnlySpan<byte> s1, ReadOnlySpan<byte> s2)
{
int s1Index = 0;
int s2Index = 0;
@ -142,11 +142,11 @@ namespace Ryujinx.HLE.Utilities
return s2[s2Index] - s1[s1Index];
}
public static int LengthCstr(ReadOnlySpan<char> s)
public static int LengthCstr(ReadOnlySpan<byte> s)
{
int i = 0;
while (s[i] != '\0')
while (s[i] != 0)
{
i++;
}

View File

@ -227,6 +227,8 @@ namespace Ryujinx.Memory.Tracking
// Look up the virtual region using the region list.
// Signal up the chain to relevant handles.
bool shouldThrow = false;
lock (TrackingLock)
{
ref var overlaps = ref ThreadStaticArray<VirtualRegion>.Get();
@ -235,32 +237,41 @@ namespace Ryujinx.Memory.Tracking
if (count == 0 && !precise)
{
if (!_memoryManager.IsMapped(address))
if (_memoryManager.IsMapped(address))
{
_invalidAccessHandler?.Invoke(address);
// We can't continue - it's impossible to remove protection from the page.
// Even if the access handler wants us to continue, we wouldn't be able to.
throw new InvalidMemoryRegionException();
}
_memoryManager.TrackingReprotect(address & ~(ulong)(_pageSize - 1), (ulong)_pageSize, MemoryPermission.ReadAndWrite);
return false; // We can't handle this - it's probably a real invalid access.
}
for (int i = 0; i < count; i++)
{
VirtualRegion region = overlaps[i];
if (precise)
{
region.SignalPrecise(address, size, write);
_memoryManager.TrackingReprotect(address & ~(ulong)(_pageSize - 1), (ulong)_pageSize, MemoryPermission.ReadAndWrite);
return false; // We can't handle this - it's probably a real invalid access.
}
else
{
region.Signal(address, size, write);
shouldThrow = true;
}
}
else
{
for (int i = 0; i < count; i++)
{
VirtualRegion region = overlaps[i];
if (precise)
{
region.SignalPrecise(address, size, write);
}
else
{
region.Signal(address, size, write);
}
}
}
}
if (shouldThrow)
{
_invalidAccessHandler?.Invoke(address);
// We can't continue - it's impossible to remove protection from the page.
// Even if the access handler wants us to continue, we wouldn't be able to.
throw new InvalidMemoryRegionException();
}
return true;

View File

@ -24,6 +24,7 @@
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Cpu\Ryujinx.Cpu.csproj" />
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
<ProjectReference Include="..\Ryujinx.Memory\Ryujinx.Memory.csproj" />
<ProjectReference Include="..\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj" />
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />

View File

@ -0,0 +1,18 @@
using NUnit.Framework;
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System.Runtime.CompilerServices;
namespace Ryujinx.Tests.Time
{
internal class TimeZoneRuleTests
{
class EffectInfoParameterTests
{
[Test]
public void EnsureTypeSize()
{
Assert.AreEqual(0x4000, Unsafe.SizeOf<TimeZoneRule>());
}
}
}
}