Compare commits

...

51 Commits

Author SHA1 Message Date
Evan Husted
139e86915b Merge branch 'master' into master 2025-02-13 19:42:56 -06:00
Daniel Nylander
91f73a4891 Updated Swedish translation (#594) 2025-02-13 19:28:39 -06:00
Evan Husted
883d4d863a i18n: chore: [ci skip] missing closing single quote in translation (not JSON breaking) 2025-02-13 19:16:05 -06:00
Dennis
ca5de909a1 Updated German translation (#626) 2025-02-13 19:14:02 -06:00
Vladimir Sokolov
ca8329d3da Merge branch 'master' into master 2025-02-13 19:38:29 +10:00
Vladimir Sokolov
4f02b6a6de Merge branch 'master' into master 2025-02-13 18:11:07 +10:00
Vova
1dedf4c8d5 fixed: when loading a game with a custom configuration via a shortcut or rebooting, when entering the settings menu, a menu for global configuration was displayed. 2025-02-13 16:34:03 +10:00
Vladimir Sokolov
3f185f580c Merge branch 'master' into master 2025-02-13 16:12:19 +10:00
Vova
2dd787fb7b Changed: Hid.DisableInputWhenOutOfFocus only for global config 2025-02-12 16:00:37 +10:00
Vladimir Sokolov
42bd99193f Merge branch 'master' into master 2025-02-12 15:18:56 +10:00
Vladimir Sokolov
22299e69c6 Merge branch 'master' into master 2025-02-12 13:27:41 +10:00
Vladimir Sokolov
acc06a8430 Merge branch 'master' into master 2025-02-12 12:45:34 +10:00
Vova
5d061a1871 Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-12 12:44:46 +10:00
Vova
c21e63eb02 Removed irrelevant functions.
Renamed variables to correct names
2025-02-12 12:44:38 +10:00
Vladimir Sokolov
32f9b3357a Merge branch 'master' into master 2025-02-12 11:27:19 +10:00
Vladimir Sokolov
7f7055a037 Merge branch 'master' into master 2025-02-11 17:27:56 +10:00
Vova
af13f4bb3b fix latest changes 2025-02-11 15:32:17 +10:00
Vladimir Sokolov
03692e6072 Merge branch 'master' into master 2025-02-11 15:24:48 +10:00
Vova
6f4930d547 Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-11 14:07:03 +10:00
Vova
1ca5407c22 Added autorestart of the emulator if it is necessary to change the graphic multi-thread.
Code cleaning
2025-02-11 14:06:31 +10:00
Vladimir Sokolov
8efceb3c28 Merge branch 'master' into master 2025-02-11 08:56:57 +10:00
Vova
0399af0ff9 Replace the "delete" button with "apply" during the game in the custom configuration. 2025-02-10 21:02:26 +10:00
Vladimir Sokolov
3e40532dee Merge branch 'master' into master 2025-02-10 15:36:15 +10:00
Vova
1d68546aa0 Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-10 15:26:21 +10:00
Vova
966860a464 Change: initialization of user configuration is now a separate function 2025-02-10 15:25:19 +10:00
Vladimir Sokolov
b9982c02e3 Merge branch 'master' into master 2025-02-09 16:51:11 +10:00
Vladimir Sokolov
a301a14074 Merge branch 'master' into master 2025-02-09 16:50:08 +10:00
Vova
7568dd4d7a Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-09 15:30:57 +10:00
Vova
4bbcec2076 Fixed a bug where control would not return after changing settings.
Added a function to update the gamelist status after the game is released
2025-02-09 15:30:33 +10:00
Vladimir Sokolov
57ac90a55b Merge branch 'master' into master 2025-02-09 15:20:03 +10:00
Vladimir Sokolov
cdc4557d82 Merge branch 'master' into master 2025-02-09 09:15:26 +10:00
Vova
ae16360685 Code cleaning 2025-02-08 22:27:38 +10:00
Vova
45b1794a45 Returned an erroneously modified Convert time string 2025-02-08 20:41:16 +10:00
Vova
39fbbb39e0 Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-08 20:19:23 +10:00
Vova
11a68a204f Fixed bug crash due to incorrect System.SystemTimeOffset.Value,
hotkeys should now also be read from the global configuration file
2025-02-08 20:16:38 +10:00
Vladimir Sokolov
e4e38c4bdd Merge branch 'master' into master 2025-02-08 18:30:35 +10:00
Vova
5f5c76107c Fixed a ban where a custom setting was mistakenly created when starting the game if it did not exist.
Now when starting the game, if a custom setting was created, the current game will be displayed in the settings window.
Code cleanup.
2025-02-08 18:29:31 +10:00
Vova
a92475b8fd Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-08 10:51:57 +10:00
Vova
4d5ae23b0b Added game name to user settings window 2025-02-08 10:51:39 +10:00
Vladimir Sokolov
4cb2170ad1 Merge branch 'master' into master 2025-02-08 10:49:55 +10:00
Vladimir Sokolov
fc01f7b58f Merge branch 'master' into master 2025-02-08 10:17:13 +10:00
Vova
fa463c51f8 code cleaning 2025-02-07 23:37:35 +10:00
Vova
3a8e6e3117 Added gamepad configuration for custom configuration 2025-02-07 21:48:51 +10:00
Vladimir Sokolov
7f4a161ca5 Merge branch 'master' into master 2025-02-07 20:48:10 +10:00
Vova
df1c7613e8 returned the deleted field 2025-02-06 23:24:52 +10:00
Vova
6d37d79cc7 Ops 2025-02-06 22:58:43 +10:00
Vova
afdfcc1720 fix 2025-02-06 22:54:45 +10:00
Vova
fe94224d01 keys returned to their place, minor fixes, local ones added 2025-02-06 22:51:08 +10:00
Vova
fbe1a7d966 Added custom setting function for each game 2025-02-06 04:54:00 -06:00
Vova
5dc7fb4495 Merge branch 'master' of https://github.com/Goodfeat/Ryujinx_alt 2025-02-06 20:34:31 +10:00
Vova
2640f083b6 Added custom setting function for each game 2025-02-06 20:34:15 +10:00
22 changed files with 1093 additions and 188 deletions

View File

@@ -582,6 +582,13 @@ namespace Ryujinx.Ava
Rainbow.Disable();
Rainbow.Reset();
// Reload settings when the game is turned off
// (resets custom settings if there were any)
Program.ReloadConfig();
//Updates the gameList (changes the status of the user setting if it was added or removed during the game)
RyujinxApp.MainWindow.GameListUpdate();
_isStopped = true;
Stop();
}

View File

@@ -1,7 +1,8 @@
<Styles
<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:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia">
<Design.PreviewWith>
<Border Height="2000"
@@ -30,7 +31,8 @@
<Button
Name="btnRem"
HorizontalAlignment="Right"
Content="Add" />
Content="Add"
Classes="red"/>
<TextBox
Width="100"
VerticalAlignment="Center"
@@ -41,7 +43,11 @@
</StackPanel>
</Grid>
<ui:NumberBox Value="1" />
<MenuItem
Header="123 0000"
ToolTip.Tip="What this"/>
</StackPanel>
</Border>
</Design.PreviewWith>
<Style Selector="DropDownButton">
@@ -373,6 +379,16 @@
<Setter Property="Background"
Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style>
<Style Selector="Button.red /template/ ContentPresenter">
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style Selector="Button.red:pointerover /template/ ContentPresenter">
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Background" Value="{DynamicResource WarningBackgroundColor}" />
<Setter Property="Foreground" Value="White"/>
</Style>
<Styles.Resources>
<SolidColorBrush x:Key="ThemeAccentColorBrush"
Color="{DynamicResource SystemAccentColor}" />

View File

@@ -1,4 +1,4 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
@@ -12,11 +12,13 @@
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
<Color x:Key="Switch">#FF2EEAC9</Color>
<Color x:Key="Unbounded">#FFFF4554</Color>
<Color x:Key="Custom">#6483F5</Color>
<Color x:Key="Warning">#800080</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
@@ -29,11 +31,13 @@
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
<Color x:Key="Switch">#13c3a4</Color>
<Color x:Key="Unbounded">#FFFF4554</Color>
<Color x:Key="Custom">#6483F5</Color>
<Color x:Key="Warning">#800080</Color>
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
@@ -46,11 +50,13 @@
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="WarningBackgroundColor">#FF6347</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
<Color x:Key="Switch">#FF2EEAC9</Color>
<Color x:Key="Unbounded">#FFFF4554</Color>
<Color x:Key="Custom">#6483F5</Color>
<Color x:Key="Warning">#FFA500</Color>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>

View File

@@ -76,7 +76,7 @@
"ID": "MenuBarFileOpenAppletOpenMiiApplet",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Mii-Bearbeitungsapplet",
"el_GR": "",
"en_US": "Mii Edit Applet",
"es_ES": "Applet Editor Mii",
@@ -176,7 +176,7 @@
"ID": "SettingsTabSystemMemoryManagerModeSoftware",
"Translations": {
"ar_SA": "البرنامج",
"de_DE": "",
"de_DE": "Programme",
"el_GR": "Λογισμικό",
"en_US": "Software",
"es_ES": "",
@@ -326,7 +326,7 @@
"ID": "MenuBarFileOpenFromFileError",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Keine Anwendungen im ausgewählten Datei gefunden.",
"el_GR": "",
"en_US": "No applications found in selected file.",
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
@@ -376,7 +376,7 @@
"ID": "MenuBarFileLoadDlcFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "DLC aus Ordner laden",
"el_GR": "",
"en_US": "Load DLC From Folder",
"es_ES": "Cargar DLC Desde Carpeta",
@@ -401,7 +401,7 @@
"ID": "MenuBarFileLoadTitleUpdatesFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Titel-Updates aus Ordner laden",
"el_GR": "",
"en_US": "Load Title Updates From Folder",
"es_ES": "Cargar Actualizaciones de Títulos Desde Carpeta",
@@ -576,7 +576,7 @@
"ID": "MenuBarOptionsStartGamesWithoutUI",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Spiele ohne Benutzeroberfläche starten",
"el_GR": "",
"en_US": "Start Games with UI Hidden",
"es_ES": "",
@@ -589,7 +589,7 @@
"pl_PL": "",
"pt_BR": "Iniciar jogos ocultando a interface",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Starta spel med dolt användargränssnitt",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -751,7 +751,7 @@
"ID": "MenuBarActionsScanAmiiboBin",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Amiibo scannen (aus Bin-Datei)",
"el_GR": "",
"en_US": "Scan An Amiibo (From Bin)",
"es_ES": "",
@@ -851,7 +851,7 @@
"ID": "MenuBarActionsInstallKeys",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel installieren",
"el_GR": "",
"en_US": "Install Keys",
"es_ES": "",
@@ -876,7 +876,7 @@
"ID": "MenuBarFileActionsInstallKeysFromFile",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel aus KEYS oder ZIP installieren",
"el_GR": "",
"en_US": "Install keys from KEYS or ZIP",
"es_ES": "Instalar keys de KEYS o ZIP",
@@ -901,7 +901,7 @@
"ID": "MenuBarFileActionsInstallKeysFromFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel aus einem Verzeichnis installieren",
"el_GR": "",
"en_US": "Install keys from a directory",
"es_ES": "Instalar keys de un directorio",
@@ -1001,7 +1001,7 @@
"ID": "MenuBarActionsXCITrimmer",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "XCI-Dateien trimmen",
"el_GR": "",
"en_US": "Trim XCI Files",
"es_ES": "Recortar archivos XCI",
@@ -1226,7 +1226,7 @@
"ID": "MenuBarHelpFaqAndGuides",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Anleitungen",
"el_GR": "",
"en_US": "FAQ & Guides",
"es_ES": "",
@@ -1251,7 +1251,7 @@
"ID": "MenuBarHelpFaq",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Fehlerbehebung Seite",
"el_GR": "",
"en_US": "FAQ & Troubleshooting Page",
"es_ES": "",
@@ -1276,7 +1276,7 @@
"ID": "MenuBarHelpFaqTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die FAQ- und Fehlerbehebungsseite im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the FAQ and Troubleshooting page on the official Ryujinx wiki",
"es_ES": "",
@@ -1301,7 +1301,7 @@
"ID": "MenuBarHelpSetup",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Setup- und Konfigurationsanleitung",
"el_GR": "",
"en_US": "Setup & Configuration Guide",
"es_ES": "",
@@ -1326,7 +1326,7 @@
"ID": "MenuBarHelpSetupTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die Setup- und Konfigurationsanleitung im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the Setup & Configuration guide on the official Ryujinx wiki",
"es_ES": "",
@@ -1351,7 +1351,7 @@
"ID": "MenuBarHelpMultiplayer",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Multiplayer (LDN/LAN) Anleitung",
"el_GR": "",
"en_US": "Multiplayer (LDN/LAN) Guide",
"es_ES": "",
@@ -1376,7 +1376,7 @@
"ID": "MenuBarHelpMultiplayerTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Öffnet die Multiplayer-Anleitung im offiziellen Ryujinx-Wiki",
"el_GR": "",
"en_US": "Opens the Multiplayer guide on the official Ryujinx wiki",
"es_ES": "",
@@ -1526,7 +1526,7 @@
"ID": "GameListHeaderDeveloper",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Entwickelt von {0}",
"el_GR": "",
"en_US": "Developed by {0}",
"es_ES": "",
@@ -1826,7 +1826,7 @@
"ID": "GameListHeaderCompatibilityStatus",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Kompatibilität:",
"el_GR": "",
"en_US": "Compatibility:",
"es_ES": "",
@@ -2614,7 +2614,7 @@
"pl_PL": "",
"pt_BR": "Extraia o RomFS de um arquivo DLC selecionado",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Extrahera RomFS från en vald DLC-fil",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -2722,6 +2722,31 @@
"zh_TW": "建立桌面捷徑,啟動選取的應用程式"
}
},
{
"ID": "GameListContextMenuEditGameConfiguration",
"Translations": {
"ar_SA": "تعديل تكوين اللعبة",
"de_DE": "Spielkonfiguration bearbeiten",
"el_GR": "Επεξεργασία ρυθμίσεων παιχνιδιού",
"en_US": "Edit Game Configuration",
"es_ES": "Editar configuración del juego",
"fr_FR": "Modifier la configuration du jeu",
"he_IL": "ערוך את הגדרת המשחק",
"it_IT": "Modifica configurazione gioco",
"ja_JP": "ゲーム設定を編集",
"ko_KR": "게임 설정 편집",
"no_NO": "Rediger spillkonfig.",
"pl_PL": "Edytuj konfigurację gry",
"pt_BR": "Editar configuração do jogo",
"ru_RU": "Редактировать конфигурацию игры",
"sv_SE": "Redigera spelkonfiguration",
"th_TH": "แก้ไขการตั้งค่าเกม",
"tr_TR": "Oyunun yapılandırmasını düzenle",
"uk_UA": "Редагувати конфігурацію гри",
"zh_CN": "编辑游戏配置",
"zh_TW": "編輯遊戲設定"
}
},
{
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
"Translations": {
@@ -2747,6 +2772,31 @@
"zh_TW": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式"
}
},
{
"ID": "EditGameConfigurationToolTip",
"Translations": {
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
"de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel",
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
"en_US": "Creates an independent configuration for the current game",
"es_ES": "Crea una configuración independiente para el juego actual",
"fr_FR": "Crée une configuration indépendante pour le jeu en cours",
"he_IL": "יוצר תצורה עצמאית למשחק הנוכחי",
"it_IT": "Crea una configurazione indipendente per il gioco attuale",
"ja_JP": "現在のゲーム用の独立した設定を作成します",
"ko_KR": "현재 게임에 대한 독립적인 설정을 생성합니다",
"no_NO": "Oppretter en uavhengig konfigurasjon for det gjeldende spillet",
"pl_PL": "Tworzy niezależną konfigurację dla bieżącej gry",
"pt_BR": "Cria uma configuração independente para o jogo atual",
"ru_RU": "Создает независимую конфигурацию для текущей игры",
"sv_SE": "Skapar en oberoende konfiguration för det aktuella spelet",
"th_TH": "สร้างการกำหนดค่าที่เป็นอิสระสำหรับเกมปัจจุบัน",
"tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur",
"uk_UA": "Створює незалежну конфігурацію для поточної гри",
"zh_CN": "为当前游戏创建独立的配置",
"zh_TW": "為當前遊戲創建獨立的配置"
}
},
{
"ID": "GameListContextMenuShowCompatEntry",
"Translations": {
@@ -3062,7 +3112,7 @@
"ko_KR": "XCI 파일 '{0}' 트리밍",
"no_NO": "Trimming av XCI-filen '{0}'",
"pl_PL": "",
"pt_BR": "Reduzindo arquivo XCI '{0}",
"pt_BR": "Reduzindo arquivo XCI '{0}'",
"ru_RU": "Обрезается XCI файл '{0}'",
"sv_SE": "Optimerar XCI-filen '{0}'",
"th_TH": "",
@@ -3272,6 +3322,31 @@
"zh_TW": "設定"
}
},
{
"ID": "SettingsWithInfo",
"Translations": {
"ar_SA": "{0} - إعدادات",
"de_DE": "Einstellungen - {0}",
"el_GR": "Ρυθμίσεις - {0}",
"en_US": "Settings - {0}",
"es_ES": "Configuración - {0}",
"fr_FR": "Paramètres - {0}",
"he_IL": "{0} - הגדרות",
"it_IT": "Impostazioni - {0}",
"ja_JP": "設定 - {0}",
"ko_KR": "설정 - {0}",
"no_NO": "Innstillinger - {0}",
"pl_PL": "Ustawienia - {0}",
"pt_BR": "Configurações - {0}",
"ru_RU": "Параметры - {0}",
"sv_SE": "Inställningar - {0}",
"th_TH": "ตั้งค่า - {0}",
"tr_TR": "Ayarlar - {0}",
"uk_UA": "Налаштування - {0}",
"zh_CN": "设置 - {0}",
"zh_TW": "設定 - {0}"
}
},
{
"ID": "SettingsTabGeneral",
"Translations": {
@@ -4621,7 +4696,7 @@
"zh_CN": "繁体中文(推荐)",
"zh_TW": "正體中文 (建議)"
}
},
},
{
"ID": "SettingsTabSystemSystemLanguageSwedish",
"Translations": {
@@ -4646,7 +4721,7 @@
"zh_CN": "瑞典语",
"zh_TW": ""
}
},
},
{
"ID": "SettingsTabSystemSystemLanguageNorwegian",
"Translations": {
@@ -6464,7 +6539,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Ok",
"th_TH": "ตกลง",
"tr_TR": "Tamam",
"uk_UA": "",
@@ -8364,7 +8439,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Inaktivera",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8389,7 +8464,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Regnbåge",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8439,7 +8514,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Färg",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -12672,6 +12747,31 @@
"zh_TW": "正在下載更新..."
}
},
{
"ID": "DialogRebooterMessage",
"Translations": {
"ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل",
"de_DE": "Bitte warten Sie, der Emulator wird neu gestartet",
"el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται",
"en_US": "Please wait, the emulator is restarting",
"es_ES": "Por favor, espere, el emulador se está reiniciando",
"fr_FR": "Veuillez patienter, l'émulateur est en train de redémarrer",
"he_IL": "אנא המתן, המחקה מתארגן מחדש",
"it_IT": "Attendere prego, l'emulatore si sta riavviando",
"ja_JP": "お待ちください、エミュレーターが再起動しています",
"ko_KR": "잠시만 기다려 주세요, 에뮬레이터가 재시작 중입니다",
"no_NO": "Vennligst vent, emulatoren starter på nytt",
"pl_PL": "Proszę czekać, emulator jest w trakcie ponownego uruchamiania",
"pt_BR": "Por favor, aguarde, o emulador está reiniciando",
"ru_RU": "Пожалуйста, подождите, эмулятор перезапускается",
"sv_SE": "Vänligen vänta, emulatorn startar om",
"th_TH": "กรุณารอสักครู่, ตัวจำลองกำลังเริ่มใหม่",
"tr_TR": "Lütfen bekleyin, emülatör yeniden başlatılıyor",
"uk_UA": "Будь ласка, зачекайте, емулятор перезавантажується",
"zh_CN": "请稍等,模拟器正在重新启动",
"zh_TW": "請稍候,模擬器正在重新啟動"
}
},
{
"ID": "DialogUpdaterExtractionMessage",
"Translations": {
@@ -15239,7 +15339,7 @@
"pl_PL": "Seria Amiibo",
"pt_BR": "Franquia Amiibo",
"ru_RU": "Серия Amiibo",
"sv_SE": "",
"sv_SE": "Amiibo-serie",
"th_TH": "",
"tr_TR": "Amiibo Serisi",
"uk_UA": "Серія Amiibo",
@@ -18664,7 +18764,7 @@
"pl_PL": "",
"pt_BR": "{0} - Informação",
"ru_RU": "{0} - Информация",
"sv_SE": "",
"sv_SE": "{0} - Information",
"th_TH": "{0} ข้อมูล",
"tr_TR": "{0} - Bilgi",
"uk_UA": "{0} - Інформація",
@@ -19522,6 +19622,31 @@
"zh_TW": "{0} 更新程式"
}
},
{
"ID": "RyujinxRebooter",
"Translations": {
"ar_SA": "إعادة تشغيل {0}",
"de_DE": "Neustart von {0}",
"el_GR": "Επανεκκίνηση {0}",
"en_US": "{0} Reboot",
"es_ES": "Reinicio de {0}",
"fr_FR": "Redémarrage de {0}",
"he_IL": "אתחול {0}",
"it_IT": "Riavvio di {0}",
"ja_JP": "{0} 再起動",
"ko_KR": "{0} 재부팅",
"no_NO": "Omstart av {0}",
"pl_PL": "Ponowne uruchomienie {0}",
"pt_BR": "Reinício de {0}",
"ru_RU": "{0} Перезагрузка",
"sv_SE": "Ominläsning av {0}",
"th_TH": "เริ่มต้นใหม่ {0}",
"tr_TR": "{0} Yeniden Başlatma",
"uk_UA": "Перезавантаження {0}",
"zh_CN": "{0} 重启",
"zh_TW": "{0} 重新啟動"
}
},
{
"ID": "SettingsTabHotkeys",
"Translations": {
@@ -19739,7 +19864,7 @@
"pl_PL": "",
"pt_BR": "Configurações de LED",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "LED-inställningar",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -21939,7 +22064,7 @@
"pl_PL": "Głoś",
"pt_BR": "",
"ru_RU": "Громкость",
"sv_SE": "",
"sv_SE": "Volym",
"th_TH": "ระดับเสียง",
"tr_TR": "Ses",
"uk_UA": "Гуч.",
@@ -23351,7 +23476,7 @@
"ID": "SettingsTabSystemVSyncModeTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. \"Unbounded\" ist eine unbegrenzte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
"es_ES": "",
@@ -23376,7 +23501,7 @@
"ID": "SettingsTabSystemVSyncModeTooltipCustom",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Emulierte vertikale Synchronisation. \"Switch\" emuliert die 60Hz-Bildwiederholfrequenz der Switch. „Unbounded“ ist eine unbegrenzte Bildwiederholfrequenz. „Benutzerdefinierte Bildwiederholfrequenz“ emuliert die angegebene benutzerdefinierte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom Refresh Rate' emulates the specified custom refresh rate.",
"es_ES": "",
@@ -23401,7 +23526,7 @@
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLassen Sie diese Option deaktiviert, wenn Sie sich nicht sicher sind.",
"el_GR": "",
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
"es_ES": "",
@@ -23426,7 +23551,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalValueTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Der Zielwert für die benutzerdefinierte Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "The custom refresh rate target value.",
"es_ES": "",
@@ -23451,7 +23576,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalSliderTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Die benutzerdefinierte Bildwiederholfrequenz als Prozentsatz der normalen Switch-Bildwiederholfrequenz.",
"el_GR": "",
"en_US": "The custom refresh rate, as a percentage of the normal Switch refresh rate.",
"es_ES": "",
@@ -23476,7 +23601,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalPercentage",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz %:",
"el_GR": "",
"en_US": "Custom Refresh Rate %:",
"es_ES": "",
@@ -23501,7 +23626,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalValue",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Wert für benutzerdefinierte Bildwiederholfrequenz:",
"el_GR": "",
"en_US": "Custom Refresh Rate Value:",
"es_ES": "",
@@ -23551,7 +23676,7 @@
"ID": "SettingsTabHotkeysToggleVSyncModeHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "VSync-Modus umschalten:",
"el_GR": "",
"en_US": "Toggle VSync mode:",
"es_ES": "",
@@ -23576,7 +23701,7 @@
"ID": "SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz erhöhen:",
"el_GR": "",
"en_US": "Raise custom refresh rate",
"es_ES": "",
@@ -23601,7 +23726,7 @@
"ID": "SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz senken:",
"el_GR": "",
"en_US": "Lower custom refresh rate:",
"es_ES": "",
@@ -23626,7 +23751,7 @@
"ID": "CompatibilityListLastUpdated",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Zuletzt aktualisiert: {0}",
"el_GR": "",
"en_US": "Last updated: {0}",
"es_ES": "",
@@ -23651,7 +23776,7 @@
"ID": "CompatibilityListWarning",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Diese Kompatibilitätsliste könnte veraltete Einträge enthalten. Teste dennoch Spiele im \"Ingame\"-Status.",
"el_GR": "",
"en_US": "This compatibility list might contain out of date entries.\nDo not be opposed to testing games in the \"Ingame\" status.",
"es_ES": "",
@@ -23676,7 +23801,7 @@
"ID": "CompatibilityListSearchBoxWatermark",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Kompatibilitätseinträge durchsuchen...",
"el_GR": "",
"en_US": "Search compatibility entries...",
"es_ES": "",
@@ -23726,7 +23851,7 @@
"ID": "CompatibilityListOnlyShowOwnedGames",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Nur eigene Spiele anzeigen",
"el_GR": "",
"en_US": "Only show owned games",
"es_ES": "",
@@ -23751,7 +23876,7 @@
"ID": "CompatibilityListPlayable",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Spielbar",
"el_GR": "",
"en_US": "Playable",
"es_ES": "",
@@ -23776,7 +23901,7 @@
"ID": "CompatibilityListIngame",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Im Spiel",
"el_GR": "",
"en_US": "Ingame",
"es_ES": "",
@@ -23951,7 +24076,7 @@
"ID": "CompatibilityListBootsTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Startet, kommt aber nicht über den Titelbildschirm hinaus.",
"el_GR": "",
"en_US": "Boots but does not make it past the title screen.",
"es_ES": "",
@@ -23976,7 +24101,7 @@
"ID": "CompatibilityListNothingTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Startet nicht oder zeigt keine Anzeichen von Aktivität.",
"el_GR": "",
"en_US": "Does not boot or shows no signs of activity.",
"es_ES": "",
@@ -23998,11 +24123,36 @@
}
},
{
"ID": "ExtractAocListHeader",
"ID": "UserConfigurationHeader",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "User Config",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "ExtractAocListHeader",
"Translations": {
"ar_SA": "",
"de_DE": "Wähle ein DLC zum Extrahieren aus",
"el_GR": "",
"en_US": "Select a DLC to Extract",
"es_ES": "",
"fr_FR": "Choisissez un DLC à extraire",
@@ -24014,7 +24164,7 @@
"pl_PL": "",
"pt_BR": "Selecione um DLC para extrair",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Välj en DLC att extrahera",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",

View File

@@ -54,6 +54,7 @@ namespace Ryujinx.Ava.Common.Locale
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
}
public string this[LocaleKeys key]

View File

@@ -32,8 +32,10 @@ namespace Ryujinx.Ava
public static double DesktopScaleFactor { get; set; } = 1.0;
public static string Version { get; private set; }
public static string ConfigurationPath { get; private set; }
public static string GlobalConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; }
public static bool UseHardwareAcceleration { get; private set; }
public static string BackendThreadingArg { get; private set; }
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
@@ -155,11 +157,43 @@ namespace Ryujinx.Ava
}
}
public static bool FindGameConfig(string gameDir)
{
if (File.Exists(gameDir))
{
return true;
}
return false;
}
public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false)
{
string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName);
// Should load with the game if there is a custom setting for the game
if (rememberGlobalDir)
{
GlobalConfigurationPath = ConfigurationPath;
}
if (changeFolderForGame)
{
ConfigurationPath = gameDir;
}
return gameDir;
}
public static void ReloadConfig()
{
//It is necessary that when a user setting appears, the global setting remains available
GlobalConfigurationPath = null;
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
// Now load the configuration as the other subsystems are now registered
if (File.Exists(localConfigurationPath))
{
@@ -217,6 +251,11 @@ namespace Ryujinx.Ava
_ => ConfigurationState.Instance.Graphics.BackendThreading
};
if (CommandLineState.OverrideBackendThreadingAfterReboot is not null)
{
BackendThreadingArg = CommandLineState.OverrideBackendThreadingAfterReboot;
}
// Check if docked mode was overriden.
if (CommandLineState.OverrideDockedMode.HasValue)
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
@@ -232,6 +271,33 @@ namespace Ryujinx.Ava
_ => ConfigurationState.Instance.HideCursor,
};
// Check if memoryManagerMode was overridden.
if (CommandLineState.OverrideMemoryManagerMode is not null)
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
{
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
}
// Check if PPTC was overridden.
if (CommandLineState.OverridePPTC is not null)
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
{
ConfigurationState.Instance.System.EnablePtc.Value = result;
}
// Check if region was overridden.
if (CommandLineState.OverrideSystemRegion is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
{
ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
}
//Check if language was overridden.
if (CommandLineState.OverrideSystemLanguage is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
{
ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
}
// Check if hardware-acceleration was overridden.
if (CommandLineState.OverrideHardwareAcceleration != null)

95
src/Ryujinx/Rebooter.cs Normal file
View File

@@ -0,0 +1,95 @@
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.Utilities;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava
{
internal static class Rebooter
{
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
public static void RebootAppWithGame(string gamePath, List<string> args)
{
_ = Reboot(gamePath, args);
}
private static async Task Reboot(string gamePath, List<string> args)
{
bool shouldRestart = true;
TaskDialog taskDialog = new()
{
Header = LocaleManager.Instance[LocaleKeys.RyujinxRebooter],
SubHeader = LocaleManager.Instance[LocaleKeys.DialogRebooterMessage],
IconSource = new SymbolIconSource { Symbol = Symbol.Games },
XamlRoot = RyujinxApp.MainWindow,
};
if (shouldRestart)
{
List<string> arguments = CommandLineState.Arguments.ToList();
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
// On macOS we perform the update at relaunch.
if (OperatingSystem.IsMacOS())
{
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
string currentPid = Environment.ProcessId.ToString();
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
Process.Start("/bin/bash", arguments);
}
else
{
var dialogTask = taskDialog.ShowAsync(true);
await Task.Delay(500);
// Find the process name.
string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty;
// Some operating systems can see the renamed executable, so strip off the .ryuold if found.
if (ryuName.EndsWith(".ryuold"))
{
ryuName = ryuName[..^7];
}
// Fallback if the executable could not be found.
if (ryuName.Length == 0 || !Path.Exists(Path.Combine(executableDirectory, ryuName)))
{
ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx";
}
ProcessStartInfo processStart = new(ryuName)
{
UseShellExecute = true,
WorkingDirectory = executableDirectory,
};
foreach (var arg in args)
{
processStart.ArgumentList.Add(arg);
}
processStart.ArgumentList.Add(gamePath);
Process.Start(processStart);
}
Environment.Exit(0);
}
}
}
}

View File

@@ -19,6 +19,11 @@
Header="{ext:Locale GameListContextMenuCreateShortcut}"
Icon="{ext:Icon fa-solid fa-bookmark}"
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
<MenuItem
Click="EditGameConfiguration_Click"
Header="{ext:Locale GameListContextMenuEditGameConfiguration}"
Icon="{ext:Icon fa-solid fa-gear}"
ToolTip.Tip="{ext:Locale EditGameConfigurationToolTip}" />
<MenuItem
IsVisible="{Binding HasCompatibilityEntry}"
Click="OpenApplicationCompatibility_Click"

View File

@@ -386,13 +386,26 @@ namespace Ryujinx.Ava.UI.Controls
viewModel.SelectedApplication.Icon
);
}
public async void EditGameConfiguration_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
{
await new GameSpecificSettingsWindow(viewModel).ShowDialog((Window)viewModel.TopLevel);
//just checking for file presence
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString,false,false));
viewModel.RefreshView();
}
}
public async void OpenApplicationCompatibility_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
await CompatibilityList.Show(viewModel.SelectedApplication.IdString);
}
public async void OpenApplicationData_Click(object sender, RoutedEventArgs args)
{
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })

View File

@@ -7,6 +7,7 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
d:DesignHeight="450"
d:DesignWidth="800"
Focusable="True"
@@ -73,12 +74,18 @@
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsVisible="{Binding $parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Name}"
TextAlignment="Center"
TextWrapping="Wrap" />
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock
Text="{Binding Name}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
IsVisible="{Binding HasIndependentConfiguration}"
Text="{ext:Locale UserConfigurationHeader}"
TextAlignment="Center"
TextWrapping="Wrap"
Foreground="{DynamicResource Warning}" />
</StackPanel>
</Panel>
</Grid>
</Border>
@@ -86,10 +93,28 @@
Margin="5,5,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontSize="16"
FontSize="18"
Foreground="{DynamicResource FavoriteApplicationIconColor}"
IsVisible="{Binding Favorite}"
Symbol="StarFilled" />
<Grid IsVisible="{Binding !$parent[UserControl].((viewModels:MainWindowViewModel)DataContext).ShowNames}">
<Border
Margin="15,35,5,15"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Width="90"
Height="20"
CornerRadius="4"
IsVisible="{Binding HasIndependentConfiguration}"
Background="{DynamicResource ThemeContentBackgroundColor}">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ext:Locale UserConfigurationHeader}"
TextAlignment="Center"
TextWrapping="Wrap" />
</Border>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>

View File

@@ -6,6 +6,7 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
d:DesignHeight="450"
d:DesignWidth="800"
Focusable="True"
@@ -156,6 +157,13 @@
Text="{Binding Converter={x:Static helpers:MultiplayerInfoConverter.Instance}}"
TextAlignment="Start"
TextWrapping="Wrap"/>
<TextBlock
HorizontalAlignment="Stretch"
IsVisible="{Binding HasIndependentConfiguration}"
Text="{ext:Locale UserConfigurationHeader}"
TextAlignment="Start"
TextWrapping="Wrap"
Foreground="{DynamicResource Warning}" />
</StackPanel>
<StackPanel
Grid.Column="4"

View File

@@ -355,6 +355,11 @@ namespace Ryujinx.Ava.UI.ViewModels
_ => null,
};
}
set
{
ListSelectedApplication = value;
GridSelectedApplication = value;
}
}
public bool HasCompatibilityEntry => SelectedApplication.HasPlayabilityInfo;
@@ -1084,7 +1089,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_rendererWaitEvent.WaitOne();
AppHost?.Start();
AppHost?.DisposeContext();
}
@@ -1531,8 +1536,50 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public bool InitializeUserConfig(ApplicationData application)
{
// Code where conditions will be met before loading the user configuration (Global Config)
BackendThreading backendThreadingValue = ConfigurationState.Instance.Graphics.BackendThreading.Value;
string BackendThreadingInit = Program.BackendThreadingArg;
if (BackendThreadingInit is null)
{
BackendThreadingInit = ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
}
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
string idGame = application.IdBaseString;
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
{
// Loads the user configuration, having previously changed the global configuration to the user configuration
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame);
}
// Code where conditions will be executed after loading user configuration
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit)
{
List<string> Arguments = new List<string>
{
"--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading
};
Rebooter.RebootAppWithGame(application.Path, Arguments);
return true;
}
return false;
}
public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct<ApplicationControlProperty>? customNacpData = null)
{
if (InitializeUserConfig(application))
{
return;
}
if (AppHost != null)
{
await ContentDialogHelper.CreateInfoDialog(
@@ -1548,7 +1595,7 @@ namespace Ryujinx.Ava.UI.ViewModels
#if RELEASE
await PerformanceCheck();
#endif
Logger.RestartTime();
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path, ConfigurationState.Instance.System.Language, application.Id);
@@ -1593,6 +1640,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Thread gameThread = new(InitializeGame) { Name = "GUI.WindowThread" };
gameThread.Start();
}
public void SwitchToRenderer(bool startFullscreen) =>

View File

@@ -1,8 +1,10 @@
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Humanizer;
using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
@@ -27,6 +29,7 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
@@ -69,6 +72,17 @@ namespace Ryujinx.Ava.UI.ViewModels
public SettingsHacksViewModel DirtyHacks { get; }
private readonly bool _isGameRunning;
private Bitmap _gameIcon;
private string _gameTitle;
private string _gamePath;
private string _gameId;
public bool IsGameRunning => _isGameRunning;
public Bitmap GameIcon => _gameIcon;
public string GamePath => _gamePath;
public string GameTitle => _gameTitle;
public string GameId => _gameId;
public int ResolutionScale
{
get => _resolutionScale;
@@ -336,7 +350,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsInvalidLdnPassphraseVisible { get; set; }
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
{
_virtualFileSystem = virtualFileSystem;
_contentManager = contentManager;
@@ -349,7 +363,51 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public SettingsViewModel()
public SettingsViewModel(
VirtualFileSystem virtualFileSystem,
ContentManager contentManager,
bool gameRunning,
string gamePath,
string gameName,
string gameId,
byte[] gameIconData,
bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig)
{
_virtualFileSystem = virtualFileSystem;
_contentManager = contentManager;
if (gameIconData != null && gameIconData.Length > 0)
{
using (var ms = new MemoryStream(gameIconData))
{
_gameIcon = new Bitmap(ms);
}
}
_isGameRunning = gameRunning;
_gamePath = gamePath;
_gameTitle = gameName;
_gameId = gameId;
if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window
{
string gameDir = Program.GetDirGameUserConfig(gameId, false, true);
if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat))
{
ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId);
}
LoadCurrentConfiguration(); // Needed to load custom configuration
}
if (Program.PreviewerDetached)
{
Task.Run(LoadTimeZones);
}
}
public SettingsViewModel(bool noLoadGlobalConfig = false)
{
GameDirectories = [];
AutoloadDirectories = [];
@@ -364,7 +422,11 @@ namespace Ryujinx.Ava.UI.ViewModels
if (Program.PreviewerDetached)
{
Task.Run(LoadAvailableGpus);
LoadCurrentConfiguration();
if (!noLoadGlobalConfig)// Default is false, but loading custom config avoids double call
{
LoadCurrentConfiguration();
}
DirtyHacks = new SettingsHacksViewModel(this);
}
@@ -475,29 +537,35 @@ namespace Ryujinx.Ava.UI.ViewModels
{
ConfigurationState config = ConfigurationState.Instance;
// User Interface
EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit;
RememberWindowState = config.RememberWindowState;
ShowTitleBar = config.ShowTitleBar;
HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value);
AutoloadDirectories.Clear();
AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
BaseStyleIndex = config.UI.BaseStyle.Value switch
//It is necessary that the data is used from the global configuration file
if (string.IsNullOrEmpty(GameId))
{
"Auto" => 0,
"Light" => 1,
"Dark" => 2,
_ => 0
};
// User Interface
EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit;
RememberWindowState = config.RememberWindowState;
ShowTitleBar = config.ShowTitleBar;
HideCursor = (int)config.HideCursor.Value;
UpdateCheckerType = (int)config.UpdateCheckerType.Value;
FocusLostActionType = (int)config.FocusLostActionType.Value;
GameDirectories.Clear();
GameDirectories.AddRange(config.UI.GameDirs.Value);
AutoloadDirectories.Clear();
AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
BaseStyleIndex = config.UI.BaseStyle.Value switch
{
"Auto" => 0,
"Light" => 1,
"Dark" => 2,
_ => 0
};
}
// Input
EnableDockedMode = config.System.EnableDockedMode;
@@ -518,7 +586,6 @@ namespace Ryujinx.Ava.UI.ViewModels
DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
CurrentDate = currentDateTime.Date;
CurrentTime = currentDateTime.TimeOfDay;
MatchSystemTime = config.System.MatchSystemTime;
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
@@ -579,39 +646,44 @@ namespace Ryujinx.Ava.UI.ViewModels
LdnPassphrase = config.Multiplayer.LdnPassphrase;
LdnServer = config.Multiplayer.LdnServer;
}
public void SaveSettings()
{
ConfigurationState config = ConfigurationState.Instance;
bool userConfigFile = string.IsNullOrEmpty(GameId);
// User Interface
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.RememberWindowState.Value = RememberWindowState;
config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor;
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
if (GameDirectoryChanged)
if (userConfigFile)
{
config.UI.GameDirs.Value = [..GameDirectories];
// User Interface
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.RememberWindowState.Value = RememberWindowState;
config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor;
config.UpdateCheckerType.Value = (UpdaterType)UpdateCheckerType;
config.FocusLostActionType.Value = (FocusLostType)FocusLostActionType;
if (GameDirectoryChanged)
{
config.UI.GameDirs.Value = [.. GameDirectories];
}
if (AutoloadDirectoryChanged)
{
config.UI.AutoloadDirs.Value = [.. AutoloadDirectories];
}
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
0 => "Auto",
1 => "Light",
2 => "Dark",
_ => "Auto"
};
}
if (AutoloadDirectoryChanged)
{
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
}
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
0 => "Auto",
1 => "Light",
2 => "Dark",
_ => "Auto"
};
// Input
config.System.EnableDockedMode.Value = EnableDockedMode;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
@@ -708,7 +780,9 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
MainWindow.UpdateGraphicsConfig();
RyujinxApp.MainWindow.ViewModel.VSyncModeSettingChanged();
@@ -721,7 +795,11 @@ namespace Ryujinx.Ava.UI.ViewModels
private static void RevertIfNotSaved()
{
Program.ReloadConfig();
// maybe this is an unnecessary check(all options need to be tested)
if (string.IsNullOrEmpty(Program.GlobalConfigurationPath))
{
Program.ReloadConfig();
}
}
public void ApplyButton()
@@ -729,6 +807,26 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettings();
}
public void DeleteConfigGame()
{
string gameDir = Program.GetDirGameUserConfig(GameId,false,false);
if (File.Exists(gameDir))
{
File.Delete(gameDir);
}
RevertIfNotSaved();
CloseWindow?.Invoke();
}
public void SaveUserConfig()
{
SaveSettings();
RevertIfNotSaved(); // Revert global configuration after saving user configuration
CloseWindow?.Invoke();
}
public void OkButton()
{
SaveSettings();

View File

@@ -130,9 +130,26 @@ namespace Ryujinx.Ava.UI.Views.Main
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
Rainbow.Enable();
await Window.SettingsWindow.ShowDialog(Window);
if (ViewModel.SelectedApplication is null) // Checks if game data exists
{
await Window.SettingsWindow.ShowDialog(Window);
}
else
{
bool userConfigExist = Program.FindGameConfig(Program.GetDirGameUserConfig(ViewModel.SelectedApplication.IdString, false, false));
if (!ViewModel.IsGameRunning || !userConfigExist)
{
await Window.SettingsWindow.ShowDialog(Window); // The game is not running, or if the user configuration does not exist
}
else
{
// If there is a custom configuration in the folder
await new GameSpecificSettingsWindow(ViewModel, userConfigExist).ShowDialog((Window)ViewModel.TopLevel);
}
}
Rainbow.Disable();
Rainbow.Reset();

View File

@@ -0,0 +1,149 @@
<window:StyleableAppWindow
x:Class="Ryujinx.Ava.UI.Windows.GameSpecificSettingsWindow"
xmlns="https://github.com/avaloniaui"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
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"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
Width="1100"
Height="768"
MinWidth="800"
MinHeight="480"
WindowStartupLocation="CenterOwner"
x:DataType="viewModels:SettingsViewModel"
mc:Ignorable="d"
Focusable="True">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600" RowDefinitions="Auto,*,Auto">
<ContentPresenter
x:Name="ContentPresenter"
Grid.Row="1"
IsVisible="False"
KeyboardNavigation.IsTabStop="False"/>
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
<settings:SettingsInputView Name="InputPage" />
<settings:SettingsSystemView Name="SystemPage" />
<settings:SettingsCPUView Name="CpuPage" />
<settings:SettingsGraphicsView Name="GraphicsPage" />
<settings:SettingsAudioView Name="AudioPage" />
<settings:SettingsNetworkView Name="NetworkPage" />
<settings:SettingsLoggingView Name="LoggingPage" />
<settings:SettingsHacksView Name="HacksPage" />
</Grid>
<ui:NavigationView
Grid.Row="1"
IsSettingsVisible="False"
Name="NavPanel"
IsBackEnabled="False"
PaneDisplayMode="Left"
Margin="2,10,10,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
OpenPaneLength="200"
IsPaneToggleButtonVisible="False">
<!-- For image -->
<ui:NavigationView.PaneHeader>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding GameId}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,0,10"
TextAlignment="Center" Grid.Row="0" />
<Image Source="{Binding GameIcon}"
Width="160"
Height="160"
Grid.Row="1"
Margin="0,0,0,10"
HorizontalAlignment="Center" />
<TextBlock Text="{Binding GameTitle}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,0,10"
TextAlignment="Center" Grid.Row="2" />
<Separator Height="1" Grid.Row="3" Margin="0,0,0,10" HorizontalAlignment="Stretch"/>
</Grid>
</ui:NavigationView.PaneHeader>
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabInput}"
Tag="InputPage"
IconSource="Games" />
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabSystem}"
Tag="SystemPage"
IconSource="Settings" />
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabCpu}"
Tag="CpuPage">
<ui:NavigationViewItem.IconSource>
<ui:FontIconSource
FontFamily="avares://Ryujinx/Assets/Fonts#Segoe Fluent Icons"
Glyph="{helpers:GlyphValueConverter Chip}" />
</ui:NavigationViewItem.IconSource>
</ui:NavigationViewItem>
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabGraphics}"
Tag="GraphicsPage"
IconSource="Image" />
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabAudio}"
IconSource="Audio"
Tag="AudioPage" />
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabNetwork}"
Tag="NetworkPage"
IconSource="Globe" />
<ui:NavigationViewItem
Content="{ext:Locale SettingsTabLogging}"
Tag="LoggingPage"
IconSource="Document" />
<ui:NavigationViewItem
IsVisible="{Binding ShowDirtyHacks}"
Content="Dirty Hacks"
Tag="HacksPage"
IconSource="Code" />
</ui:NavigationView.MenuItems>
</ui:NavigationView>
<ReversibleStackPanel
Grid.Row="2"
Margin="10"
Spacing="10"
Orientation="Horizontal"
HorizontalAlignment="Right"
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
<Button
Content="{ext:Locale SettingsButtonSave}"
Command="{Binding SaveUserConfig}" />
<Button
HotKey="Escape"
Content="{ext:Locale SettingsButtonClose}"
Command="{Binding CancelButton}" />
<Button
IsVisible="{Binding IsGameRunning}"
Content="{ext:Locale SettingsButtonApply}"
Command="{Binding ApplyButton}" />
<Button
IsVisible="{Binding !IsGameRunning}"
Content="{ext:Locale UserProfilesDelete}"
Command="{Binding DeleteConfigGame}"
Classes="red"/>
</ReversibleStackPanel>
</Grid>
</window:StyleableAppWindow>

View File

@@ -0,0 +1,117 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Media.Imaging;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Projektanker.Icons.Avalonia;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.Configuration;
using Ryujinx.Common.Configuration;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input;
using System;
using System.IO;
using System.Linq;
using Key = Avalonia.Input.Key;
namespace Ryujinx.Ava.UI.Windows
{
public partial class GameSpecificSettingsWindow : StyleableAppWindow
{
internal readonly SettingsViewModel ViewModel;
public GameSpecificSettingsWindow(MainWindowViewModel viewModel, bool findUserConfigDir = true)
{
Title = string.Format(LocaleManager.Instance[LocaleKeys.SettingsWithInfo], viewModel.SelectedApplication.Name, viewModel.SelectedApplication.IdString);
DataContext = ViewModel = new SettingsViewModel(
viewModel.VirtualFileSystem,
viewModel.ContentManager,
viewModel.IsGameRunning,
viewModel.SelectedApplication.Path,
viewModel.SelectedApplication.Name,
viewModel.SelectedApplication.IdString,
viewModel.SelectedApplication.Icon,
findUserConfigDir);
ViewModel.CloseWindow += Close;
ViewModel.SaveSettingsEvent += SaveSettings;
InitializeComponent();
Load();
#if DEBUG
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
#endif
}
public void SaveSettings()
{
InputPage.InputView?.SaveCurrentProfile();
}
private void Load()
{
Pages.Children.Clear();
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
}
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
{
if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
{
switch (navItem.Tag.ToString())
{
case nameof(InputPage):
NavPanel.Content = InputPage;
break;
case nameof(SystemPage):
SystemPage.ViewModel = ViewModel;
NavPanel.Content = SystemPage;
break;
case nameof(CpuPage):
NavPanel.Content = CpuPage;
break;
case nameof(GraphicsPage):
NavPanel.Content = GraphicsPage;
break;
case nameof(AudioPage):
NavPanel.Content = AudioPage;
break;
case nameof(NetworkPage):
NetworkPage.ViewModel = ViewModel;
NavPanel.Content = NetworkPage;
break;
case nameof(LoggingPage):
NavPanel.Content = LoggingPage;
break;
case nameof(HacksPage):
HacksPage.DataContext = ViewModel;
NavPanel.Content = HacksPage;
break;
default:
throw new NotImplementedException();
}
}
}
protected override void OnClosing(WindowClosingEventArgs e)
{
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
base.OnClosing(e);
}
}
}

View File

@@ -223,7 +223,7 @@ namespace Ryujinx.Ava.UI.Windows
_deferLoad = true;
_launchPath = launchPathArg;
_launchApplicationId = launchApplicationId;
_startFullscreen = startFullscreenArg;
_startFullscreen = startFullscreenArg;
}
public void SwitchToGameControl(bool startFullscreen = false)
@@ -374,6 +374,7 @@ namespace Ryujinx.Ava.UI.Windows
if (applicationData != null)
{
ViewModel.SelectedApplication = applicationData;
await ViewModel.LoadApplication(applicationData, _startFullscreen);
}
else
@@ -385,6 +386,7 @@ namespace Ryujinx.Ava.UI.Windows
else
{
applicationData = applications[0];
ViewModel.SelectedApplication = applicationData;
await ViewModel.LoadApplication(applicationData, _startFullscreen);
}
}
@@ -658,6 +660,11 @@ namespace Ryujinx.Ava.UI.Windows
ReloadGameList();
}
public void GameListUpdate()
{
ReloadGameList();
}
public void ToggleFileType(string fileType)
{
switch (fileType)

View File

@@ -23,6 +23,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public class ApplicationData
{
public bool Favorite { get; set; }
public bool HasIndependentConfiguration { get; set; }
public byte[] Icon { get; set; }
public string Name { get; set; } = "Unknown";

View File

@@ -505,7 +505,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
if (data.Id != 0)
{
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
{
{
appMetadata.Title = data.Name;
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
@@ -529,10 +529,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
}
});
data.Favorite = appMetadata.Favorite;
data.TimePlayed = appMetadata.TimePlayed;
data.LastPlayed = appMetadata.LastPlayed;
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config
}
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();

View File

@@ -6,11 +6,16 @@ namespace Ryujinx.Ava.Utilities
public static class CommandLineState
{
public static string[] Arguments { get; private set; }
public static int CountArguments { get; private set; }
public static bool? OverrideDockedMode { get; private set; }
public static bool? OverrideHardwareAcceleration { get; private set; }
public static string OverrideGraphicsBackend { get; private set; }
public static string OverrideBackendThreading { get; private set; }
public static string OverrideBackendThreadingAfterReboot { get; private set; }
public static string OverridePPTC { get; private set; }
public static string OverrideMemoryManagerMode { get; private set; }
public static string OverrideSystemRegion { get; private set; }
public static string OverrideSystemLanguage { get; private set; }
public static string OverrideHideCursor { get; private set; }
public static string BaseDirPathArg { get; private set; }
public static string Profile { get; private set; }
@@ -19,6 +24,7 @@ namespace Ryujinx.Ava.Utilities
public static bool StartFullscreenArg { get; private set; }
public static bool HideAvailableUpdates { get; private set; }
public static void ParseArguments(string[] args)
{
List<string> arguments = [];
@@ -28,6 +34,11 @@ namespace Ryujinx.Ava.Utilities
{
string arg = args[i];
if (arg.Contains("-") || arg.Contains("--"))
{
CountArguments++;
}
switch (arg)
{
case "-r":
@@ -85,6 +96,57 @@ namespace Ryujinx.Ava.Utilities
OverrideBackendThreading = args[++i];
break;
case "--bt":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideBackendThreadingAfterReboot = args[++i];
break;
case "--pptc":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverridePPTC = args[++i];
break;
case "-m":
case "--memory-manager-mode":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideMemoryManagerMode = args[++i];
break;
case "--system-region":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideSystemRegion = args[++i];
break;
case "--system-language":
if (i + 1 >= args.Length)
{
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
continue;
}
OverrideSystemLanguage = args[++i];
break;
case "-i":
case "--application-id":
LaunchApplicationId = args[++i];

View File

@@ -18,9 +18,15 @@ namespace Ryujinx.Ava.Utilities.Configuration
{
public partial class ConfigurationState
{
public void Load(ConfigurationFileFormat cff, string configurationFilePath)
public void Load(ConfigurationFileFormat cff, string configurationFilePath, string titleId = "")
{
bool configurationFileUpdated = false;
bool shouldLoadFromFile = true;
if (!string.IsNullOrEmpty(titleId))
{
shouldLoadFromFile = false;
}
if (cff.Version is < 0 or > ConfigurationFileFormat.CurrentVersion)
{
@@ -43,16 +49,17 @@ namespace Ryujinx.Ava.Utilities.Configuration
configurationFileUpdated = true;
}
EnableDiscordIntegration.Value = cff.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = cff.CheckUpdatesOnStart;
UpdateCheckerType.Value = cff.UpdateCheckerType;
FocusLostActionType.Value = cff.FocusLostActionType;
ShowConfirmExit.Value = cff.ShowConfirmExit;
RememberWindowState.Value = cff.RememberWindowState;
ShowTitleBar.Value = cff.ShowTitleBar;
EnableHardwareAcceleration.Value = cff.EnableHardwareAcceleration;
HideCursor.Value = cff.HideCursor;
EnableDiscordIntegration.Value = shouldLoadFromFile ? cff.EnableDiscordIntegration : EnableDiscordIntegration.Value; // Get from global config only
CheckUpdatesOnStart.Value = shouldLoadFromFile ? cff.CheckUpdatesOnStart : CheckUpdatesOnStart.Value; // Get from global config only
UpdateCheckerType.Value = shouldLoadFromFile ? cff.UpdateCheckerType : UpdateCheckerType.Value; // Get from global config only
FocusLostActionType.Value = shouldLoadFromFile ? cff.FocusLostActionType : FocusLostActionType.Value; // Get from global config only
ShowConfirmExit.Value = shouldLoadFromFile ? cff.ShowConfirmExit : ShowConfirmExit.Value; // Get from global config only
RememberWindowState.Value = shouldLoadFromFile ? cff.RememberWindowState : RememberWindowState.Value; // Get from global config only
ShowTitleBar.Value = shouldLoadFromFile ? cff.ShowTitleBar : ShowTitleBar.Value; // Get from global config only
EnableHardwareAcceleration.Value = shouldLoadFromFile ? cff.EnableHardwareAcceleration : EnableHardwareAcceleration.Value; // Get from global config only
HideCursor.Value = shouldLoadFromFile ? cff.HideCursor : HideCursor.Value; // Get from global config only
Logger.EnableFileLog.Value = cff.EnableFileLog;
Logger.EnableDebug.Value = cff.LoggingEnableDebug;
Logger.EnableStub.Value = cff.LoggingEnableStub;
@@ -87,7 +94,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.Language.Value = cff.SystemLanguage;
System.Region.Value = cff.SystemRegion;
System.TimeZone.Value = cff.SystemTimeZone;
System.SystemTimeOffset.Value = cff.SystemTimeOffset;
System.SystemTimeOffset.Value = shouldLoadFromFile ? cff.SystemTimeOffset : System.SystemTimeOffset.Value; // Get from global config only
System.EnableDockedMode.Value = cff.DockedMode;
System.EnablePtc.Value = cff.EnablePtc;
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
@@ -101,47 +108,48 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
System.IgnoreApplet.Value = cff.IgnoreApplet;
System.UseHypervisor.Value = cff.UseHypervisor;
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;
UI.GuiColumns.IconColumn.Value = cff.GuiColumns.IconColumn;
UI.GuiColumns.AppColumn.Value = cff.GuiColumns.AppColumn;
UI.GuiColumns.DevColumn.Value = cff.GuiColumns.DevColumn;
UI.GuiColumns.VersionColumn.Value = cff.GuiColumns.VersionColumn;
UI.GuiColumns.TimePlayedColumn.Value = cff.GuiColumns.TimePlayedColumn;
UI.GuiColumns.LastPlayedColumn.Value = cff.GuiColumns.LastPlayedColumn;
UI.GuiColumns.FileExtColumn.Value = cff.GuiColumns.FileExtColumn;
UI.GuiColumns.FileSizeColumn.Value = cff.GuiColumns.FileSizeColumn;
UI.GuiColumns.PathColumn.Value = cff.GuiColumns.PathColumn;
UI.ColumnSort.SortColumnId.Value = cff.ColumnSort.SortColumnId;
UI.ColumnSort.SortAscending.Value = cff.ColumnSort.SortAscending;
UI.GameDirs.Value = cff.GameDirs;
UI.AutoloadDirs.Value = cff.AutoloadDirs ?? [];
UI.ShownFileTypes.NSP.Value = cff.ShownFileTypes.NSP;
UI.ShownFileTypes.PFS0.Value = cff.ShownFileTypes.PFS0;
UI.ShownFileTypes.XCI.Value = cff.ShownFileTypes.XCI;
UI.ShownFileTypes.NCA.Value = cff.ShownFileTypes.NCA;
UI.ShownFileTypes.NRO.Value = cff.ShownFileTypes.NRO;
UI.ShownFileTypes.NSO.Value = cff.ShownFileTypes.NSO;
UI.LanguageCode.Value = cff.LanguageCode;
UI.BaseStyle.Value = cff.BaseStyle;
UI.GameListViewMode.Value = cff.GameListViewMode;
UI.ShowNames.Value = cff.ShowNames;
UI.IsAscendingOrder.Value = cff.IsAscendingOrder;
UI.GridSize.Value = cff.GridSize;
UI.ApplicationSort.Value = cff.ApplicationSort;
UI.StartFullscreen.Value = cff.StartFullscreen;
UI.StartNoUI.Value = cff.StartNoUI;
UI.ShowConsole.Value = cff.ShowConsole;
UI.WindowStartup.WindowSizeWidth.Value = cff.WindowStartup.WindowSizeWidth;
UI.WindowStartup.WindowSizeHeight.Value = cff.WindowStartup.WindowSizeHeight;
UI.WindowStartup.WindowPositionX.Value = cff.WindowStartup.WindowPositionX;
UI.WindowStartup.WindowPositionY.Value = cff.WindowStartup.WindowPositionY;
UI.WindowStartup.WindowMaximized.Value = cff.WindowStartup.WindowMaximized;
UI.GuiColumns.FavColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FavColumn : UI.GuiColumns.FavColumn.Value;
UI.GuiColumns.IconColumn.Value = shouldLoadFromFile ? cff.GuiColumns.IconColumn : UI.GuiColumns.IconColumn.Value;
UI.GuiColumns.AppColumn.Value = shouldLoadFromFile ? cff.GuiColumns.AppColumn : UI.GuiColumns.AppColumn.Value;
UI.GuiColumns.DevColumn.Value = shouldLoadFromFile ? cff.GuiColumns.DevColumn : UI.GuiColumns.DevColumn.Value;
UI.GuiColumns.VersionColumn.Value = shouldLoadFromFile ? cff.GuiColumns.VersionColumn : UI.GuiColumns.VersionColumn.Value;
UI.GuiColumns.TimePlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.TimePlayedColumn : UI.GuiColumns.TimePlayedColumn.Value;
UI.GuiColumns.LastPlayedColumn.Value = shouldLoadFromFile ? cff.GuiColumns.LastPlayedColumn : UI.GuiColumns.LastPlayedColumn.Value;
UI.GuiColumns.FileExtColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileExtColumn : UI.GuiColumns.FileExtColumn.Value;
UI.GuiColumns.FileSizeColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FileSizeColumn : UI.GuiColumns.FileSizeColumn.Value;
UI.GuiColumns.PathColumn.Value = shouldLoadFromFile ? cff.GuiColumns.PathColumn : UI.GuiColumns.PathColumn.Value;
UI.ColumnSort.SortColumnId.Value = shouldLoadFromFile ? cff.ColumnSort.SortColumnId : UI.ColumnSort.SortColumnId.Value;
UI.ColumnSort.SortAscending.Value = shouldLoadFromFile ? cff.ColumnSort.SortAscending : UI.ColumnSort.SortAscending.Value;
UI.GameDirs.Value = shouldLoadFromFile ? cff.GameDirs : UI.GameDirs.Value;
UI.AutoloadDirs.Value = shouldLoadFromFile ? (cff.AutoloadDirs ?? []) : UI.AutoloadDirs.Value;
UI.ShownFileTypes.NSP.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSP : UI.ShownFileTypes.NSP.Value;
UI.ShownFileTypes.PFS0.Value = shouldLoadFromFile ? cff.ShownFileTypes.PFS0 : UI.ShownFileTypes.PFS0.Value;
UI.ShownFileTypes.XCI.Value = shouldLoadFromFile ? cff.ShownFileTypes.XCI : UI.ShownFileTypes.XCI.Value;
UI.ShownFileTypes.NCA.Value = shouldLoadFromFile ? cff.ShownFileTypes.NCA : UI.ShownFileTypes.NCA.Value;
UI.ShownFileTypes.NRO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NRO : UI.ShownFileTypes.NRO.Value;
UI.ShownFileTypes.NSO.Value = shouldLoadFromFile ? cff.ShownFileTypes.NSO : UI.ShownFileTypes.NSO.Value;
UI.LanguageCode.Value = shouldLoadFromFile ? cff.LanguageCode : UI.LanguageCode.Value;
UI.BaseStyle.Value = shouldLoadFromFile ? cff.BaseStyle : UI.BaseStyle.Value;
UI.GameListViewMode.Value = shouldLoadFromFile ? cff.GameListViewMode : UI.GameListViewMode.Value;
UI.ShowNames.Value = shouldLoadFromFile ? cff.ShowNames : UI.ShowNames.Value;
UI.IsAscendingOrder.Value = shouldLoadFromFile ? cff.IsAscendingOrder : UI.IsAscendingOrder.Value;
UI.GridSize.Value = shouldLoadFromFile ? cff.GridSize : UI.GridSize.Value;
UI.ApplicationSort.Value = shouldLoadFromFile ? cff.ApplicationSort : UI.ApplicationSort.Value;
UI.StartFullscreen.Value = shouldLoadFromFile ? cff.StartFullscreen : UI.StartFullscreen.Value;
UI.StartNoUI.Value = shouldLoadFromFile ? cff.StartNoUI : UI.StartNoUI.Value;
UI.ShowConsole.Value = shouldLoadFromFile ? cff.ShowConsole : UI.ShowConsole.Value;
UI.WindowStartup.WindowSizeWidth.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeWidth : UI.WindowStartup.WindowSizeWidth.Value;
UI.WindowStartup.WindowSizeHeight.Value = shouldLoadFromFile ? cff.WindowStartup.WindowSizeHeight : UI.WindowStartup.WindowSizeHeight.Value;
UI.WindowStartup.WindowPositionX.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionX : UI.WindowStartup.WindowPositionX.Value;
UI.WindowStartup.WindowPositionY.Value = shouldLoadFromFile ? cff.WindowStartup.WindowPositionY : UI.WindowStartup.WindowPositionY.Value;
UI.WindowStartup.WindowMaximized.Value = shouldLoadFromFile ? cff.WindowStartup.WindowMaximized : UI.WindowStartup.WindowMaximized.Value;
Hid.EnableKeyboard.Value = cff.EnableKeyboard;
Hid.EnableMouse.Value = cff.EnableMouse;
Hid.DisableInputWhenOutOfFocus.Value = cff.DisableInputWhenOutOfFocus;
Hid.Hotkeys.Value = cff.Hotkeys;
Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus: Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only
Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only
Hid.InputConfig.Value = cff.InputConfig ?? [];
Hid.RainbowSpeed.Value = cff.RainbowSpeed;

View File

@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.Utilities
public static class ShortcutHelper
{
[SupportedOSPlatform("windows")]
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath)
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath, string args = "")
{
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
iconPath += ".ico";
@@ -22,13 +22,13 @@ namespace Ryujinx.Ava.Utilities
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
SaveBitmapAsIcon(image, iconPath);
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId, args), iconPath, 0);
shortcut.StringData.NameString = cleanedAppName;
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
}
[SupportedOSPlatform("linux")]
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName, string args = "")
{
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
string desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop");
@@ -40,11 +40,11 @@ namespace Ryujinx.Ava.Utilities
data.SaveTo(file);
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId, args)}");
}
[SupportedOSPlatform("macos")]
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName)
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName, string args = "")
{
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
string plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist");
@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Utilities
string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
using StreamWriter scriptFile = new(scriptPath);
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId));
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId, args));
// Set execute permission
FileInfo fileInfo = new(scriptPath);
@@ -87,7 +87,7 @@ namespace Ryujinx.Ava.Utilities
outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
}
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData)
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData, string args = "")
{
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars()));
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.Utilities
{
string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app");
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath);
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath, args);
return;
}
@@ -106,14 +106,14 @@ namespace Ryujinx.Ava.Utilities
string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
Directory.CreateDirectory(iconPath);
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName);
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName, args);
return;
}
if (OperatingSystem.IsMacOS())
{
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName);
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName, args);
return;
}
@@ -121,7 +121,7 @@ namespace Ryujinx.Ava.Utilities
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
}
private static string GetArgsString(string appFilePath, string applicationId)
private static string GetArgsString(string appFilePath, string applicationId, string config = "")
{
// args are first defined as a list, for easier adjustments in the future
List<string> argsList = [];
@@ -132,6 +132,11 @@ namespace Ryujinx.Ava.Utilities
argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
}
if (!string.IsNullOrEmpty(config))
{
argsList.Add(config);
}
if (appFilePath.ToLower().EndsWith(".xci"))
{
argsList.Add("--application-id");