From 8e666ab19ffec1d6ad08379b57a73d77ec6c462e Mon Sep 17 00:00:00 2001 From: Daenorth Date: Thu, 20 Mar 2025 01:56:27 +0000 Subject: [PATCH 01/14] Update file compatibility.csv --- docs/compatibility.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/compatibility.csv b/docs/compatibility.csv index ccaafa283..b2eeeefca 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -3375,6 +3375,7 @@ 0100E95004038000,"Xenoblade Chronicles™ 2",deadlock;amd-vendor-bug,ingame,2024-03-28 14:31:41 010074F013262000,"Xenoblade Chronicles™ 3",gpu;crash;nvdec;vulkan-backend-bug;amd-vendor-bug,ingame,2024-08-06 19:56:44 0100FF500E34A000,"Xenoblade Chronicles™ Definitive Edition",nvdec,playable,2024-05-04 20:12:41 +0100453019aa8000, Xenoblade Chronicles: X Definitive Edition,,boots,2025-03-20 02:51:00 010028600BA16000,"Xenon Racer",nvdec;UE4,playable,2022-08-31 22:05:30 010064200C324000,"Xenon Valkyrie+",,playable,2021-06-07 20:25:53 0100928005BD2000,"Xenoraid",,playable,2022-09-03 13:01:10 From 8ca06b69acb675a5da7af0e536203694248af7f9 Mon Sep 17 00:00:00 2001 From: Daenorth Date: Thu, 20 Mar 2025 01:57:45 +0000 Subject: [PATCH 02/14] Update file TitleIDs.cs --- src/Ryujinx.Common/TitleIDs.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs index 76a8a7126..3201a5c6b 100644 --- a/src/Ryujinx.Common/TitleIDs.cs +++ b/src/Ryujinx.Common/TitleIDs.cs @@ -163,6 +163,7 @@ namespace Ryujinx.Common "0100ff500e34a000", // Xenoblade Chronicles - Definitive Edition "0100e95004038000", // Xenoblade Chronicles 2 "010074f013262000", // Xenoblade Chronicles 3 + "0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition //Misc Games "010056e00853a000", // A Hat in Time From 1a705418c5a80ba95af9b621b9f8f42411bddc47 Mon Sep 17 00:00:00 2001 From: Daenorth Date: Thu, 20 Mar 2025 05:57:17 +0000 Subject: [PATCH 03/14] Edit compatibility.csv --- docs/compatibility.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility.csv b/docs/compatibility.csv index b2eeeefca..4ef7dd445 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -3375,7 +3375,7 @@ 0100E95004038000,"Xenoblade Chronicles™ 2",deadlock;amd-vendor-bug,ingame,2024-03-28 14:31:41 010074F013262000,"Xenoblade Chronicles™ 3",gpu;crash;nvdec;vulkan-backend-bug;amd-vendor-bug,ingame,2024-08-06 19:56:44 0100FF500E34A000,"Xenoblade Chronicles™ Definitive Edition",nvdec,playable,2024-05-04 20:12:41 -0100453019aa8000, Xenoblade Chronicles: X Definitive Edition,,boots,2025-03-20 02:51:00 +0100453019aa8000, Xenoblade Chronicles: X Definitive Edition,,ingame,2025-03-20 06:51:00 010028600BA16000,"Xenon Racer",nvdec;UE4,playable,2022-08-31 22:05:30 010064200C324000,"Xenon Valkyrie+",,playable,2021-06-07 20:25:53 0100928005BD2000,"Xenoraid",,playable,2022-09-03 13:01:10 From c067f85fa3541682a58315cf723f1000b25889ff Mon Sep 17 00:00:00 2001 From: Daenorth Date: Thu, 20 Mar 2025 06:13:27 +0000 Subject: [PATCH 04/14] Edit compatibility.csv --- docs/compatibility.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compatibility.csv b/docs/compatibility.csv index 4ef7dd445..c46a0d1e9 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -3375,7 +3375,7 @@ 0100E95004038000,"Xenoblade Chronicles™ 2",deadlock;amd-vendor-bug,ingame,2024-03-28 14:31:41 010074F013262000,"Xenoblade Chronicles™ 3",gpu;crash;nvdec;vulkan-backend-bug;amd-vendor-bug,ingame,2024-08-06 19:56:44 0100FF500E34A000,"Xenoblade Chronicles™ Definitive Edition",nvdec,playable,2024-05-04 20:12:41 -0100453019aa8000, Xenoblade Chronicles: X Definitive Edition,,ingame,2025-03-20 06:51:00 +0100453019aa8000,"Xenoblade Chronicles: X Definitive Edition",,ingame,2025-03-20 06:51:00 010028600BA16000,"Xenon Racer",nvdec;UE4,playable,2022-08-31 22:05:30 010064200C324000,"Xenon Valkyrie+",,playable,2021-06-07 20:25:53 0100928005BD2000,"Xenoraid",,playable,2022-09-03 13:01:10 From 02d459a9bd47e5b6b41a7e1540759581e9d2a9e9 Mon Sep 17 00:00:00 2001 From: Yeager Date: Fri, 21 Mar 2025 06:26:42 +0000 Subject: [PATCH 05/14] Swedish translations for new strings --- src/Ryujinx/Assets/locales.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 2605490b1..dd6b7ee88 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -2764,7 +2764,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Задать индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Skapa anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Створити користувацьку конфігурацію", @@ -2789,7 +2789,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Изменить индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Redigera anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Редагувати користувацьку конфігурацію", @@ -2864,7 +2864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Отредактировать существующие независимые параметры для выбранной игры.", - "sv_SE": "", + "sv_SE": "Redigera din befintliga oberoende konfiguration för det valda spelet", "th_TH": "", "tr_TR": "", "uk_UA": "Відредагувати наявну індивідуальну конфігурацію для цієї гри.", @@ -3839,7 +3839,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Включить оригинальный интерфейса (требуется перезагрузка)", - "sv_SE": "", + "sv_SE": "Visa ursprunglig gränssnittsstil (kräver omstart)", "th_TH": "", "tr_TR": "", "uk_UA": "Показати оригінальний UI (Потрібен перезапуск)", @@ -3864,7 +3864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Показать старый пользовательский интерфейс Avalonia Ryujinx, напоминающий Ryujinx 1.1.1403. Включено по умолчанию на платформах, отличных от Windows.\nСтрока заголовка в классическом стиле вернётся на место, а основные изменения в оформлении окна будут отменены; например, расположение навигации по настройкам над этой всплывающей подсказкой.", - "sv_SE": "", + "sv_SE": "Visa det gamla Ryuijinx-gränssnittet baserat på Avalonia som påminner om version 1.1.1403. Detta är aktiverat som standard på plattformat som inte är Windows.\nDen klassiska titelfältet är tillbaka och de stora omarbetningarna av fönsterlayouten är omvända, till exempel placeringen av inställningsnavigeringen ovanför detta verktygstips.", "th_TH": "", "tr_TR": "", "uk_UA": "Показати старий інтерфейс Avalonia Ryujinx, який був у Ryujinx 1.1.1403. Ця опція активна за замовчуванням на всіх інших, окрім Windows платформах.\nПовернеться класична панель заголовка, а всі суттєві зміни інтерфейсу будуть скасовані, зокрема горизонтальне розміщення навігації в налаштуваннях.", @@ -23864,7 +23864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Список совместимости — записей: {0}", - "sv_SE": "", + "sv_SE": "Kompatibilitetslista - {0} poster", "th_TH": "", "tr_TR": "", "uk_UA": "Список сумісності — {0} ігор", @@ -23939,7 +23939,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Поиск среди {0} записей о совместимости...", - "sv_SE": "", + "sv_SE": "Sök i {0} kompatibilitetsposter...", "th_TH": "", "tr_TR": "", "uk_UA": "Шукати серед {0} перевірених ігор...", @@ -24264,7 +24264,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Власна конфігурація", From 86c2f261afd1bc098e89c0285cd2c5bbdf24b41f Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sat, 22 Mar 2025 22:39:03 -0500 Subject: [PATCH 06/14] Graphics: Save shaders to a lowercase title ID folder. I think this got reverted in the metal revert commit. --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 4fc66c4c0..2ab413848 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader private static string GetDiskCachePath() { return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null - ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader") + ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader") : null; } From 2f064064ca4040e299462325714281844a4b33af Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sun, 23 Mar 2025 04:14:31 -0500 Subject: [PATCH 07/14] HLE: Slightly speed up IpcService creation This is not a crazy speedup, we're talking fractions of a millisecond here (I had to measure in ticks; 10000000 ticks per second) - TIPC commands are opted-into for registration since they are only used for IUserInterface, but were still attempted to be initialized for every service which spent needless time. - Directly use GetType() instead of accessing all exported types, and Where()ing for equivalency to GetType(). This causes the logic for registration to be a lot faster. --- src/Ryujinx.HLE/HOS/Services/IpcService.cs | 43 ++++++++++++++----- .../HOS/Services/Sm/IUserInterface.cs | 2 +- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 1b95b6712..7b1739e8e 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -1,8 +1,10 @@ +using Gommon; using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -21,22 +23,43 @@ namespace Ryujinx.HLE.HOS.Services private int _selfId; private bool _isDomain; - public IpcService(ServerBase server = null) + public IpcService(ServerBase server = null, bool registerTipc = false) { - CmifCommands = GetType().Assembly.GetTypes() - .Where(type => type == GetType()) - .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) + Stopwatch sw = Stopwatch.StartNew(); + + CmifCommands = GetType() + .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) .SelectMany(methodInfo => methodInfo.GetCustomAttributes() .Select(command => (command.Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); + + sw.Stop(); + + Logger.Notice.Print( + LogClass.Emulation, + $"{CmifCommands.Count} Cmif commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).", + GetType().AsPrettyString() + ); - TipcCommands = GetType().Assembly.GetTypes() - .Where(type => type == GetType()) - .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes() - .Select(command => (command.Id, methodInfo))) - .ToDictionary(command => command.Id, command => command.methodInfo); + if (registerTipc) + { + sw.Start(); + TipcCommands = GetType() + .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes() + .Select(command => (command.Id, methodInfo))) + .ToDictionary(command => command.Id, command => command.methodInfo); + + sw.Stop(); + + Logger.Notice.Print( + LogClass.Emulation, + $"{TipcCommands.Count} Tipc commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).", + GetType().AsPrettyString() + ); + } + Server = server; _parent = this; diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 6d03d8d05..af511af29 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm private bool _isInitialized; - public IUserInterface(KernelContext context, SmRegistry registry) + public IUserInterface(KernelContext context, SmRegistry registry) : base(registerTipc: true) { _commonServer = new ServerBase(context, "CommonServer"); _registry = registry; From e7d3f39c1b21ca54b67ac620253ba245f399cebf Mon Sep 17 00:00:00 2001 From: GreemDev Date: Sun, 23 Mar 2025 04:16:23 -0500 Subject: [PATCH 08/14] HLE: Damnit Greem, those were supposed to be debug. OOPSIE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --- src/Ryujinx.HLE/HOS/Services/IpcService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 7b1739e8e..1fa8d7842 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services sw.Stop(); - Logger.Notice.Print( + Logger.Debug?.Print( LogClass.Emulation, $"{CmifCommands.Count} Cmif commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).", GetType().AsPrettyString() @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Services sw.Stop(); - Logger.Notice.Print( + Logger.Debug?.Print( LogClass.Emulation, $"{TipcCommands.Count} Tipc commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).", GetType().AsPrettyString() From 4ef2836df14ef050d7c0eed93c277da2ee63f729 Mon Sep 17 00:00:00 2001 From: Yeager Date: Sun, 23 Mar 2025 17:41:58 +0000 Subject: [PATCH 09/14] Very small fix for Swedish in locales.json to render the Settings tab line better --- src/Ryujinx/Assets/locales.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 2605490b1..00e31dd33 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -2764,7 +2764,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Задать индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Skapa anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Створити користувацьку конфігурацію", @@ -2789,7 +2789,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Изменить индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Redigera anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Редагувати користувацьку конфігурацію", @@ -2864,7 +2864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Отредактировать существующие независимые параметры для выбранной игры.", - "sv_SE": "", + "sv_SE": "Redigera din befintliga oberoende konfiguration för det valda spelet", "th_TH": "", "tr_TR": "", "uk_UA": "Відредагувати наявну індивідуальну конфігурацію для цієї гри.", @@ -3839,7 +3839,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Включить оригинальный интерфейса (требуется перезагрузка)", - "sv_SE": "", + "sv_SE": "Visa ursprunglig gränssnittsstil (kräver omstart)", "th_TH": "", "tr_TR": "", "uk_UA": "Показати оригінальний UI (Потрібен перезапуск)", @@ -3864,7 +3864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Показать старый пользовательский интерфейс Avalonia Ryujinx, напоминающий Ryujinx 1.1.1403. Включено по умолчанию на платформах, отличных от Windows.\nСтрока заголовка в классическом стиле вернётся на место, а основные изменения в оформлении окна будут отменены; например, расположение навигации по настройкам над этой всплывающей подсказкой.", - "sv_SE": "", + "sv_SE": "Visa det gamla Ryuijinx-gränssnittet baserat på Avalonia som påminner om version 1.1.1403. Detta är aktiverat som standard på plattformat som inte är Windows.\nDen klassiska titelfältet är tillbaka och de stora omarbetningarna av fönsterlayouten är omvända, till exempel placeringen av inställningsnavigeringen ovanför detta verktygstips.", "th_TH": "", "tr_TR": "", "uk_UA": "Показати старий інтерфейс Avalonia Ryujinx, який був у Ryujinx 1.1.1403. Ця опція активна за замовчуванням на всіх інших, окрім Windows платформах.\nПовернеться класична панель заголовка, а всі суттєві зміни інтерфейсу будуть скасовані, зокрема горизонтальне розміщення навігації в налаштуваннях.", @@ -19764,7 +19764,7 @@ "pl_PL": "Skróty Klawiszowe Klawiatury", "pt_BR": "Atalhos do Teclado", "ru_RU": "Горячие клавиши", - "sv_SE": "Snabbtangenter för tangentbord", + "sv_SE": "Snabbtangenter", "th_TH": "ปุ่มลัดของคีย์บอร์ด", "tr_TR": "Klavye Kısayolları", "uk_UA": "Гарячі клавіші клавіатури", @@ -23864,7 +23864,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Список совместимости — записей: {0}", - "sv_SE": "", + "sv_SE": "Kompatibilitetslista - {0} poster", "th_TH": "", "tr_TR": "", "uk_UA": "Список сумісності — {0} ігор", @@ -23939,7 +23939,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Поиск среди {0} записей о совместимости...", - "sv_SE": "", + "sv_SE": "Sök i {0} kompatibilitetsposter...", "th_TH": "", "tr_TR": "", "uk_UA": "Шукати серед {0} перевірених ігор...", @@ -24264,7 +24264,7 @@ "pl_PL": "", "pt_BR": "", "ru_RU": "Индивидуальные параметры", - "sv_SE": "", + "sv_SE": "Anpassad konfiguration", "th_TH": "", "tr_TR": "", "uk_UA": "Власна конфігурація", From 8ad5c73f4c24fedf48d828c1b7cc99b25fd90ee7 Mon Sep 17 00:00:00 2001 From: GreemDev <1-greem@users.noreply.git.ryujinx.app> Date: Tue, 25 Mar 2025 02:37:22 +0000 Subject: [PATCH 10/14] feature: Turbo Mode Adds an elapsed tick multiplier feature which speeds up games which are built upon delta time. More information: https://web.archive.org/web/20240713135029/https://github.com/Ryujinx/Ryujinx/pull/6456 --- .../Configuration/Hid/KeyboardHotkeys.cs | 2 + src/Ryujinx.Cpu/ITickSource.cs | 7 + src/Ryujinx.Cpu/TickSource.cs | 29 ++- .../Engine/Threed/StateUpdater.cs | 9 +- src/Ryujinx.HLE/HOS/Services/IpcService.cs | 5 +- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 3 +- src/Ryujinx.HLE/HleConfiguration.cs | 7 + src/Ryujinx.HLE/PerformanceStatistics.cs | 14 +- src/Ryujinx.HLE/Switch.cs | 22 +- src/Ryujinx/Assets/locales.json | 202 +++++++++++++++++- src/Ryujinx/Common/KeyboardHotkeyState.cs | 1 + src/Ryujinx/Common/LocaleManager.cs | 7 + src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 4 +- src/Ryujinx/Systems/AppHost.cs | 31 ++- .../Configuration/ConfigurationFileFormat.cs | 5 + .../ConfigurationState.Migration.cs | 25 ++- .../Configuration/ConfigurationState.Model.cs | 21 +- .../Configuration/ConfigurationState.cs | 5 + src/Ryujinx/UI/Models/Input/HotkeyConfig.cs | 8 + .../UI/ViewModels/SettingsViewModel.cs | 22 ++ .../UI/Views/Settings/SettingsCPUView.axaml | 52 +++++ .../Views/Settings/SettingsHotkeysView.axaml | 32 +-- .../Settings/SettingsHotkeysView.axaml.cs | 3 + 23 files changed, 475 insertions(+), 41 deletions(-) diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 6b8152b9d..efdb422e7 100644 --- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid public Key VolumeDown { get; set; } public Key CustomVSyncIntervalIncrement { get; set; } public Key CustomVSyncIntervalDecrement { get; set; } + public Key TurboMode { get; set; } + public bool TurboModeWhileHeld { get; set; } } } diff --git a/src/Ryujinx.Cpu/ITickSource.cs b/src/Ryujinx.Cpu/ITickSource.cs index e65e99e26..4aff612f0 100644 --- a/src/Ryujinx.Cpu/ITickSource.cs +++ b/src/Ryujinx.Cpu/ITickSource.cs @@ -8,10 +8,17 @@ namespace Ryujinx.Cpu /// public interface ITickSource : ICounter { + public const long RealityTickScalar = 100; + /// /// Time elapsed since the counter was created. /// TimeSpan ElapsedTime { get; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + long TickScalar { get; set; } /// /// Time elapsed since the counter was created, in seconds. diff --git a/src/Ryujinx.Cpu/TickSource.cs b/src/Ryujinx.Cpu/TickSource.cs index eee83fc62..3bc01d6b9 100644 --- a/src/Ryujinx.Cpu/TickSource.cs +++ b/src/Ryujinx.Cpu/TickSource.cs @@ -14,12 +14,37 @@ namespace Ryujinx.Cpu /// public ulong Counter => (ulong)(ElapsedSeconds * Frequency); + + + public long TickScalar { get; set; } + + + private static long _acumElapsedTicks; + + + private static long _lastElapsedTicks; + + + private long ElapsedTicks + { + get + { + long elapsedTicks = _tickCounter.ElapsedTicks; + + _acumElapsedTicks += (elapsedTicks - _lastElapsedTicks) * TickScalar / 100; + + _lastElapsedTicks = elapsedTicks; + + return _acumElapsedTicks; + } + } /// - public TimeSpan ElapsedTime => _tickCounter.Elapsed; + + public TimeSpan ElapsedTime => Stopwatch.GetElapsedTime(0, ElapsedTicks); /// - public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq; + public double ElapsedSeconds => ElapsedTicks * _hostTickFreq; public TickSource(ulong frequency) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index f50ec852e..4620821cb 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -1065,7 +1065,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void UpdateIndexBufferState() { - IndexBufferState indexBuffer = _state.State.IndexBufferState; + IndexBufferState? indexBufferNullable = _state?.State.IndexBufferState; + + if (!indexBufferNullable.HasValue) + { + return; + } + + IndexBufferState indexBuffer = indexBufferNullable.Value; if (_drawState.IndexCount == 0) { diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 1fa8d7842..de58bb178 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -150,10 +150,7 @@ namespace Ryujinx.HLE.HOS.Services } else { - string serviceName; - - - serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName; + string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName; Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored"); } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 935e9895e..294192363 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.PreciseSleep; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -89,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } else { - _ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval; + _ticksPerFrame = ((Stopwatch.Frequency / _device.TargetVSyncInterval) * 100) / _device.TickScalar; _targetVSyncInterval = _device.TargetVSyncInterval; } } diff --git a/src/Ryujinx.HLE/HleConfiguration.cs b/src/Ryujinx.HLE/HleConfiguration.cs index 97835033e..39745ff53 100644 --- a/src/Ryujinx.HLE/HleConfiguration.cs +++ b/src/Ryujinx.HLE/HleConfiguration.cs @@ -102,6 +102,11 @@ namespace Ryujinx.HLE /// Control if the Profiled Translation Cache (PTC) should be used. /// internal readonly bool EnablePtc; + + /// + /// Control the arbitrary scalar applied to emulated CPU tick timing. + /// + public long TickScalar { get; set; } /// /// Control if the guest application should be told that there is a Internet connection available. @@ -201,6 +206,7 @@ namespace Ryujinx.HLE VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, + long tickScalar, bool enableInternetAccess, IntegrityCheckLevel fsIntegrityCheckLevel, int fsGlobalAccessLogMode, @@ -226,6 +232,7 @@ namespace Ryujinx.HLE CustomVSyncInterval = customVSyncInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; + TickScalar = tickScalar; EnableInternetAccess = enableInternetAccess; FsIntegrityCheckLevel = fsIntegrityCheckLevel; FsGlobalAccessLogMode = fsGlobalAccessLogMode; diff --git a/src/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs index e80faa7d2..9363ff2d3 100644 --- a/src/Ryujinx.HLE/PerformanceStatistics.cs +++ b/src/Ryujinx.HLE/PerformanceStatistics.cs @@ -6,6 +6,8 @@ namespace Ryujinx.HLE { public class PerformanceStatistics { + private readonly Switch _device; + private const int FrameTypeGame = 0; private const int PercentTypeFifo = 0; @@ -28,8 +30,10 @@ namespace Ryujinx.HLE private readonly System.Timers.Timer _resetTimer; - public PerformanceStatistics() + public PerformanceStatistics(Switch device) { + _device = device; + _frameRate = new double[1]; _accumulatedFrameTime = new double[1]; _previousFrameTime = new double[1]; @@ -162,14 +166,6 @@ namespace Ryujinx.HLE return 1000 / _frameRate[FrameTypeGame]; } - public string FormatGameFrameRate() - { - double frameRate = GetGameFrameRate(); - double frameTime = GetGameFrameTime(); - - return $"{frameRate:00.00} FPS ({frameTime:00.00}ms)"; - } - public string FormatFifoPercent() { double fifoPercent = GetFifoPercent(); diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index df5b48103..e52b3df15 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -4,6 +4,7 @@ using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Cpu; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; @@ -26,18 +27,26 @@ namespace Ryujinx.HLE public GpuContext Gpu { get; } public VirtualFileSystem FileSystem { get; } public HOS.Horizon System { get; } + + public bool TurboMode = false; + + public long TickScalar + { + get => System?.TickSource?.TickScalar ?? ITickSource.RealityTickScalar; + set => System.TickSource.TickScalar = value; + } + public ProcessLoader Processes { get; } public PerformanceStatistics Statistics { get; } public Hid Hid { get; } public TamperMachine TamperMachine { get; } public IHostUIHandler UIHandler { get; } - public int CpuCoresCount = 4; //Switch 1 has 4 cores + public int CpuCoresCount = 4; // Switch has a quad-core Tegra X1 SoC public VSyncMode VSyncMode { get; set; } public bool CustomVSyncIntervalEnabled { get; set; } public int CustomVSyncInterval { get; set; } - public long TargetVSyncInterval { get; set; } = 60; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; @@ -64,7 +73,7 @@ namespace Ryujinx.HLE Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); System = new HOS.Horizon(this); - Statistics = new PerformanceStatistics(); + Statistics = new PerformanceStatistics(this); Hid = new Hid(this, System.HidStorage); Processes = new ProcessLoader(this); TamperMachine = new TamperMachine(); @@ -75,6 +84,7 @@ namespace Ryujinx.HLE VSyncMode = Configuration.VSyncMode; CustomVSyncInterval = Configuration.CustomVSyncInterval; + TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar; System.State.DockedMode = Configuration.EnableDockedMode; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.EnablePtc = Configuration.EnablePtc; @@ -126,6 +136,12 @@ namespace Ryujinx.HLE } } + public void ToggleTurbo() + { + TurboMode = !TurboMode; + TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar; + } + public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile); public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId); public bool LoadNca(string ncaFile, BlitStruct? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData); diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 00e31dd33..7d9550849 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -4922,6 +4922,81 @@ "zh_TW": "低功耗 PPTC" } }, + { + "ID": "SettingsTabSystemTurboMultiplier", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Turbo Mode multiplier:", + "es_ES": "", + "fr_FR": "Multiplicateur du Mode Turbo :", + "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": "SettingsTabSystemTurboMultiplierValueToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The Turbo mode multiplier target value.\n\nLeave at 200 if unsure.", + "es_ES": "", + "fr_FR": "La valeur souhaitée du multiplicateur du Mode Turbo.\n\nGarder à 200 si incertain.", + "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": "SettingsTabSystemTurboMultiplierToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Turbo mode is an emulator feature which effectively causes speed up or slow down when a game is not frame-rate sensitive.\nYou can toggle this feature in-game with a hotkey, configurable in Ryujinx Keyboard Hotkeys settings.\n\nLeave at 200 if unsure.", + "es_ES": "", + "fr_FR": "Le Mode Turbo est une fonctionnalité de l'émulateur qui accélère ou ralentit le jeu lorsque ce dernier n'est pas sensible au framerate.\nVous pouvez changer cette option en jeu avec un raccourci clavier, configurable dans les paramètres de Raccourcis clavier de Ryujinx.\n\nGarder à 200 si incertain.", + "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": "SettingsTabSystemEnableFsIntegrityChecks", "Translations": { @@ -10505,7 +10580,7 @@ "el_GR": "", "en_US": "Unbound", "es_ES": "", - "fr_FR": "Pas Attribuée", + "fr_FR": "Non Attribuée", "he_IL": "", "it_IT": "Non assegnato", "ja_JP": "", @@ -18097,6 +18172,56 @@ "zh_TW": "更新已停用!" } }, + { + "ID": "FpsStatusBarText", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "{0} FPS ({1}ms)", + "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": "FpsTurboStatusBarText", + "Translations": { + "ar_SA": "{0} FPS ({1}ms), التوربو %{2}", + "de_DE": "", + "el_GR": "", + "en_US": "{0} FPS ({1}ms), Turbo ({2}%)", + "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": "UpdaterBackgroundStatusBarButtonText", "Translations": { @@ -23822,6 +23947,81 @@ "zh_TW": "降低自訂的重新整理頻率" } }, + { + "ID": "SettingsTabHotkeysTurboMode", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Turbo mode:", + "es_ES": "", + "fr_FR": "Mode Turbo :", + "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": "SettingsTabHotkeysTurboModeToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The Turbo mode hotkey.\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\n\nLeave Unbound if unsure.", + "es_ES": "", + "fr_FR": "Le raccourci clavier Mode Turbo.\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\n\nLaisser Non Attribuée si incertain.", + "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": "SettingsTabHotkeysOnlyWhilePressed", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Only while pressed", + "es_ES": "", + "fr_FR": "Seulement quand le raccourci est maintenu", + "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": "CompatibilityListLastUpdated", "Translations": { diff --git a/src/Ryujinx/Common/KeyboardHotkeyState.cs b/src/Ryujinx/Common/KeyboardHotkeyState.cs index 060c678d2..b6fb02f04 100644 --- a/src/Ryujinx/Common/KeyboardHotkeyState.cs +++ b/src/Ryujinx/Common/KeyboardHotkeyState.cs @@ -14,5 +14,6 @@ namespace Ryujinx.Ava.Common VolumeDown, CustomVSyncIntervalIncrement, CustomVSyncIntervalDecrement, + TurboMode, } } diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index d116fe709..9307532e7 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -61,6 +61,13 @@ namespace Ryujinx.Ava.Common.Locale } } + public static string GetUnformatted(LocaleKeys key) => Instance.Get(key); + + public string Get(LocaleKeys key) => + _localeStrings.TryGetValue(key, out string value) + ? value + : key.ToString(); + public string this[LocaleKeys key] { get diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index 751a86571..a2f5af24c 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -11,6 +11,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; @@ -311,7 +312,7 @@ namespace Ryujinx.Headless return new OpenGLRenderer(); } - + private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) => new( new HleConfiguration( @@ -321,6 +322,7 @@ namespace Ryujinx.Headless options.VSyncMode, !options.DisableDockedMode, !options.DisablePTC, + ITickSource.RealityTickScalar, options.EnableInternetAccess, !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, options.FsGlobalAccessLogMode, diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs index 455afaf45..7b07000b4 100644 --- a/src/Ryujinx/Systems/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Threading; using DiscordRPC; +using Gommon; using LibHac.Common; using LibHac.Ns; using Ryujinx.Audio.Backends.Dummy; @@ -1115,11 +1116,23 @@ namespace Ryujinx.Ava.Systems LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - Device.Statistics.FormatGameFrameRate(), + FormatGameFrameRate(), Device.Statistics.FormatFifoPercent(), _displayCount)); } + private string FormatGameFrameRate() + { + string frameRate = Device.Statistics.GetGameFrameRate().ToString("00.00"); + string frameTime = Device.Statistics.GetGameFrameTime().ToString("00.00"); + + return Device.TurboMode + ? LocaleManager.GetUnformatted(LocaleKeys.FpsTurboStatusBarText) + .Format(frameRate, frameTime, Device.TickScalar) + : LocaleManager.GetUnformatted(LocaleKeys.FpsStatusBarText) + .Format(frameRate, frameTime); + } + public async Task ShowExitPrompt() { bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; @@ -1215,6 +1228,12 @@ namespace Ryujinx.Ava.Systems if (currentHotkeyState != _prevHotkeyState) { + if (ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld && + _keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode) != Device.TurboMode) + { + Device.ToggleTurbo(); + } + switch (currentHotkeyState) { case KeyboardHotkeyState.ToggleVSyncMode: @@ -1226,6 +1245,12 @@ namespace Ryujinx.Ava.Systems case KeyboardHotkeyState.CustomVSyncIntervalIncrement: _viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval(); break; + case KeyboardHotkeyState.TurboMode: + if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld) + { + Device.ToggleTurbo(); + } + break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; break; @@ -1355,6 +1380,10 @@ namespace Ryujinx.Ava.Systems { state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode)) + { + state = KeyboardHotkeyState.TurboMode; + } return state; } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs index c5315ab12..470749674 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs @@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// Enables or disables low-power profiled translation cache persistency loading /// public bool EnableLowPowerPtc { get; set; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + public long TickScalar { get; set; } /// /// Enables or disables guest Internet access diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs index b10cc3926..20c4c6414 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs @@ -93,6 +93,7 @@ namespace Ryujinx.Ava.Systems.Configuration System.EnableDockedMode.Value = cff.DockedMode; System.EnablePtc.Value = cff.EnablePtc; System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc; + System.TickScalar.Value = cff.TickScalar; System.EnableInternetAccess.Value = cff.EnableInternetAccess; System.EnableFsIntegrityChecks.Value = cff.EnableFsIntegrityChecks; System.FsGlobalAccessLogMode.Value = cff.FsGlobalAccessLogMode; @@ -438,9 +439,27 @@ namespace Ryujinx.Ava.Systems.Configuration (64, static cff => cff.LoggingEnableAvalonia = false), (65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off), (66, static cff => cff.DisableInputWhenOutOfFocus = false), - (67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing) - // 68 was the version that added per-game configs; the file structure did not change - // the version was increased so external tools could know that your Ryujinx version has per-game config capabilities. + (67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing), + (68, static cff => + { + cff.TickScalar = 200; + cff.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = cff.Hotkeys.ToggleVSyncMode, + Screenshot = cff.Hotkeys.Screenshot, + ShowUI = cff.Hotkeys.ShowUI, + Pause = cff.Hotkeys.Pause, + ToggleMute = cff.Hotkeys.ToggleMute, + ResScaleUp = cff.Hotkeys.ResScaleUp, + ResScaleDown = cff.Hotkeys.ResScaleDown, + VolumeUp = cff.Hotkeys.VolumeUp, + VolumeDown = cff.Hotkeys.VolumeDown, + CustomVSyncIntervalIncrement = cff.Hotkeys.CustomVSyncIntervalIncrement, + CustomVSyncIntervalDecrement = cff.Hotkeys.CustomVSyncIntervalDecrement, + TurboMode = Key.Unbound, + TurboModeWhileHeld = false + }; + }) ); } } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs index b52c624e3..205054474 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs @@ -335,6 +335,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// Enables or disables persistent profiled translation cache /// public ReactiveObject EnablePtc { get; private set; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + public ReactiveObject TickScalar { get; set; } /// /// Enables or disables low-power persistent profiled translation cache loading @@ -415,6 +420,15 @@ namespace Ryujinx.Ava.Systems.Configuration EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc)); EnableLowPowerPtc.Event += (_, evnt) => Optimizations.LowPower = evnt.NewValue; + TickScalar = new ReactiveObject(); + TickScalar.LogChangesToValue(nameof(TickScalar)); + TickScalar.Event += (_, evnt) => + { + if (Switch.Shared is null) + return; + + Switch.Shared.Configuration.TickScalar = evnt.NewValue; + }; EnableInternetAccess = new ReactiveObject(); EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess)); EnableFsIntegrityChecks = new ReactiveObject(); @@ -842,6 +856,7 @@ namespace Ryujinx.Ava.Systems.Configuration Graphics.VSyncMode, System.EnableDockedMode, System.EnablePtc, + System.TickScalar, System.EnableInternetAccess, System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid @@ -860,8 +875,8 @@ namespace Ryujinx.Ava.Systems.Configuration Multiplayer.Mode, Multiplayer.DisableP2p, Multiplayer.LdnPassphrase, - Instance.Multiplayer.GetLdnServer(), - Instance.Graphics.CustomVSyncInterval, - Instance.Hacks.ShowDirtyHacks ? Instance.Hacks.EnabledHacks : null); + Multiplayer.GetLdnServer(), + Graphics.CustomVSyncInterval, + Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null); } } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs index 6fe35c744..65e8e02ce 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs @@ -72,6 +72,7 @@ namespace Ryujinx.Ava.Systems.Configuration EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnablePtc = System.EnablePtc, EnableLowPowerPtc = System.EnableLowPowerPtc, + TickScalar = System.TickScalar, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, @@ -260,6 +261,10 @@ namespace Ryujinx.Ava.Systems.Configuration ResScaleDown = Key.Unbound, VolumeUp = Key.Unbound, VolumeDown = Key.Unbound, + CustomVSyncIntervalIncrement = Key.Unbound, + CustomVSyncIntervalDecrement = Key.Unbound, + TurboMode = Key.Unbound, + TurboModeWhileHeld = false }; Hid.RainbowSpeed.Value = 1f; Hid.InputConfig.Value = diff --git a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs index 40f53c673..9e557d7b1 100644 --- a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs +++ b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs @@ -28,6 +28,10 @@ namespace Ryujinx.Ava.UI.Models.Input [ObservableProperty] private Key _customVSyncIntervalDecrement; + [ObservableProperty] private Key _turboMode; + + [ObservableProperty] private bool _turboModeWhileHeld; + public HotkeyConfig(KeyboardHotkeys config) { if (config == null) @@ -44,6 +48,8 @@ namespace Ryujinx.Ava.UI.Models.Input VolumeDown = config.VolumeDown; CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement; CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement; + TurboMode = config.TurboMode; + TurboModeWhileHeld = config.TurboModeWhileHeld; } public KeyboardHotkeys GetConfig() => @@ -60,6 +66,8 @@ namespace Ryujinx.Ava.UI.Models.Input VolumeDown = VolumeDown, CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement, CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement, + TurboMode = TurboMode, + TurboModeWhileHeld = TurboModeWhileHeld }; } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index a092e97f2..0be6ab3fe 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -60,6 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _enableCustomVSyncInterval; private int _customVSyncIntervalPercentageProxy; private VSyncMode _vSyncMode; + private long _turboModeMultiplier; public event Action CloseWindow; public event Action SaveSettingsEvent; @@ -206,6 +207,25 @@ namespace Ryujinx.Ava.UI.ViewModels } public bool EnablePptc { get; set; } public bool EnableLowPowerPptc { get; set; } + + + public long TurboMultiplier + { + get => _turboModeMultiplier; + set + { + if (_turboModeMultiplier != value) + { + _turboModeMultiplier = value; + + OnPropertyChanged(); + OnPropertyChanged((nameof(TurboMultiplierPercentageText))); + } + } + } + + public string TurboMultiplierPercentageText => $"{TurboMultiplier}%"; + public bool EnableInternetAccess { get; set; } public bool EnableFsIntegrityChecks { get; set; } public bool IgnoreMissingServices { get; set; } @@ -592,6 +612,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableLowPowerPptc = config.System.EnableLowPowerPtc; MemoryMode = (int)config.System.MemoryManagerMode.Value; UseHypervisor = config.System.UseHypervisor; + TurboMultiplier = config.System.TickScalar; // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; @@ -694,6 +715,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; config.System.UseHypervisor.Value = UseHypervisor; + config.System.TickScalar.Value = TurboMultiplier; // Graphics config.Graphics.VSyncMode.Value = VSyncMode; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml index 62f087510..b3a4b66f9 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml @@ -7,6 +7,7 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" mc:Ignorable="d" x:DataType="viewModels:SettingsViewModel"> @@ -76,6 +77,57 @@ ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" /> + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml index 87b6dda7d..917177fb5 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml @@ -19,7 +19,7 @@ - @@ -47,71 +47,79 @@ Classes="h1" Text="{ext:Locale SettingsTabHotkeysHotkeys}" /> - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs index 54ef00c38..82e01b609 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -110,6 +110,9 @@ namespace Ryujinx.Ava.UI.Views.Settings ViewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType(); break; + case "TurboMode": + ViewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType(); + break; } }); } From cb486ad66d9b85c1f91e52fc827e7533ed4de3d8 Mon Sep 17 00:00:00 2001 From: Daenorth Date: Thu, 27 Mar 2025 05:25:02 +0000 Subject: [PATCH 11/14] Update file locales.json --- src/Ryujinx/Assets/locales.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 7d9550849..bd1ec3183 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -460,7 +460,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "스크린샷 폴더 열기", - "no_NO": "", + "no_NO": "Åpne Skjermbilde Mappen", "pl_PL": "", "pt_BR": "Abrir Pasta de Capturas de Tela", "ru_RU": "Открыть папку со скриншотами", @@ -2760,7 +2760,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "사용자 정의 구성 만들기", - "no_NO": "", + "no_NO": "Opprett egendefinert konfigurasjon", "pl_PL": "", "pt_BR": "", "ru_RU": "Задать индивидуальные параметры", @@ -2785,7 +2785,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "사용자 정의 구성 편집", - "no_NO": "", + "no_NO": "Rediger egendefinert konfigurasjon", "pl_PL": "", "pt_BR": "", "ru_RU": "Изменить индивидуальные параметры", @@ -2860,7 +2860,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "선택한 게임에 대한 기존 독립 구성 편집", - "no_NO": "", + "no_NO": "Rediger din eksisterende uavhengige konfigurasjon for det valgte spillet", "pl_PL": "", "pt_BR": "", "ru_RU": "Отредактировать существующие независимые параметры для выбранной игры.", @@ -4935,7 +4935,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "", - "no_NO": "", + "no_NO": "Multiplikator i turbomodus:", "pl_PL": "", "pt_BR": "", "ru_RU": "", @@ -4960,7 +4960,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "", - "no_NO": "", + "no_NO": "Målverdien for multiplikatoren i turbomodus. \n\nLa syå på 200 hvis du er usikker.", "pl_PL": "", "pt_BR": "", "ru_RU": "", @@ -4985,7 +4985,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "", - "no_NO": "", + "no_NO": "Turbo-modus er en emulatorfunksjon som effektivt øker eller senker hastigheten når et spill ikke er følsom for bildefrekvens.\nDu kan slå på denne funksjonen i spillet med en hurtigtast, som kan konfigureres i Ryujinx Keyboard Hotkeys-innstillingene.\n\nLa den stå på 200 hvis du er usikker.", "pl_PL": "", "pt_BR": "", "ru_RU": "", @@ -5360,7 +5360,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "컨트롤러 애플릿 무시", - "no_NO": "", + "no_NO": "Ignorer Kontroller-applet", "pl_PL": "", "pt_BR": "Ignorar Applet do Controlador", "ru_RU": "Игнорировать апплет контроллера", @@ -16760,7 +16760,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "앱이 실행 중일 때, 게임패드의 연결이 끊어지면 컨트롤러 애플릿 대화 상자가 나타나지 않습니다.\n\n모르시면 끔으로 두십시오.", - "no_NO": "", + "no_NO": "Håndkontroller-appleten vises ikke hvis gamepaden kobles fra mens et program kjører.", "pl_PL": "", "pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.", "ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.", @@ -17435,7 +17435,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "Ryujinx 스크린샷 폴더 열기", - "no_NO": "", + "no_NO": "Åpne Ryujinx skjermbildemappen", "pl_PL": "", "pt_BR": "Abre a pasta de capturas de tela do Ryujinx", "ru_RU": "Открывает папку скриншотов Ryujinx", @@ -23985,7 +23985,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "", - "no_NO": "", + "no_NO": "Hurtigtasten for turbo-modus.\nKonfigurer oppførselen til turbo-modus i Ryujinx CPU-innstillinger.\n\nLa være ubundet hvis du er usikker.", "pl_PL": "", "pt_BR": "", "ru_RU": "", @@ -24010,7 +24010,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "", - "no_NO": "", + "no_NO": "Bare mens du trykker på", "pl_PL": "", "pt_BR": "", "ru_RU": "", @@ -24060,7 +24060,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "호환성 목록 - {0}개 항목", - "no_NO": "", + "no_NO": "Kompatibilitetsliste - {0} oppføringer", "pl_PL": "", "pt_BR": "", "ru_RU": "Список совместимости — записей: {0}", @@ -24460,7 +24460,7 @@ "it_IT": "", "ja_JP": "", "ko_KR": "사용자 정의 설정", - "no_NO": "", + "no_NO": "Tilpasset konfigurasjon", "pl_PL": "", "pt_BR": "", "ru_RU": "Индивидуальные параметры", From a6ce065f79add1acb32889561126bc0097a3df39 Mon Sep 17 00:00:00 2001 From: Tartifless Date: Fri, 28 Mar 2025 02:59:56 +0000 Subject: [PATCH 12/14] sdl2 guid, remove the CRC bytes (4 first characters) and replace with 0000 when creating guid --- src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index 67c68d8ec..60120ab58 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -57,16 +57,19 @@ namespace Ryujinx.Input.SDL2 return null; } + // Remove the first 4 char of the guid (CRC part) to make it stable + string guidString = "0000" + guid.ToString().Substring(4); + string id; lock (_lock) { int guidIndex = 0; - id = guidIndex + "-" + guid; + id = guidIndex + "-" + guidString; while (_gamepadsIds.Contains(id)) { - id = (++guidIndex) + "-" + guid; + id = (++guidIndex) + "-" + guidString; } } From 1e6b2ea9ca4b383721fa134dc64ee93033f67e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hack=E8=8C=B6=E3=82=93?= Date: Fri, 28 Mar 2025 10:01:12 +0000 Subject: [PATCH 13/14] Update Korean translation --- src/Ryujinx/Assets/locales.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index bd1ec3183..a8e230fa8 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -4934,7 +4934,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "터보 모드 배수 :", "no_NO": "Multiplikator i turbomodus:", "pl_PL": "", "pt_BR": "", @@ -4959,7 +4959,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "터보 모드 배수 목표 값입니다.\n\n모르면 200으로 두세요.", "no_NO": "Målverdien for multiplikatoren i turbomodus. \n\nLa syå på 200 hvis du er usikker.", "pl_PL": "", "pt_BR": "", @@ -4984,7 +4984,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "터보 모드는 게임이 프레임 속도에 민감하지 않을 때 효과적으로 속도를 높이거나 낮추는 에뮬레이터 기능입니다.\n이 기능은 Ryujinx 키보드 단축키 설정에서 구성할 수 있는 단축키를 사용하여 게임 내에서 전환할 수 있습니다.\n\n모르면 200으로 두세요.", "no_NO": "Turbo-modus er en emulatorfunksjon som effektivt øker eller senker hastigheten når et spill ikke er følsom for bildefrekvens.\nDu kan slå på denne funksjonen i spillet med en hurtigtast, som kan konfigureres i Ryujinx Keyboard Hotkeys-innstillingene.\n\nLa den stå på 200 hvis du er usikker.", "pl_PL": "", "pt_BR": "", @@ -18184,7 +18184,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "{0}FPS({1}밀리초)", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -18209,7 +18209,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "{0}FPS({1}밀리초), ({2}%) 터보", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -23959,7 +23959,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "터보 모드 :", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -23984,7 +23984,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "터보 모드 단축키입니다.\nRyujinx CPU 설정에서 터보 모드의 동작을 구성합니다.\n\n모르면 바인딩 해제 상태로 두세요.", "no_NO": "Hurtigtasten for turbo-modus.\nKonfigurer oppførselen til turbo-modus i Ryujinx CPU-innstillinger.\n\nLa være ubundet hvis du er usikker.", "pl_PL": "", "pt_BR": "", @@ -24009,7 +24009,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "누르고 있는 동안만", "no_NO": "Bare mens du trykker på", "pl_PL": "", "pt_BR": "", @@ -24209,7 +24209,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "플레이 가능", + "ko_KR": "원활", "no_NO": "Spillbar", "pl_PL": "", "pt_BR": "Jogável", @@ -24234,7 +24234,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "게임 내", + "ko_KR": "가능", "no_NO": "", "pl_PL": "", "pt_BR": "No jogo", @@ -24284,7 +24284,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "부츠", + "ko_KR": "부팅", "no_NO": "Starter", "pl_PL": "", "pt_BR": "Inicializa", @@ -24309,7 +24309,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "없음", + "ko_KR": "불가능", "no_NO": "Ingenting", "pl_PL": "", "pt_BR": "Nada", From dfdbb23f0d6d31d79f0eee4151153ff3552187e2 Mon Sep 17 00:00:00 2001 From: Shinyoyo Date: Sat, 29 Mar 2025 03:47:24 -0500 Subject: [PATCH 14/14] Updated Simplified Chinese translation. --- src/Ryujinx/Assets/locales.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index a8e230fa8..3e9a7e886 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -4943,7 +4943,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "涡轮模式倍数:", "zh_TW": "" } }, @@ -4968,7 +4968,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "涡轮模式倍数的目标值。\n\n如果不确定请保留为 200。", "zh_TW": "" } }, @@ -4993,7 +4993,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "涡轮模式是一种模拟器功能当游戏对帧率不敏感时它可以有效地导致加速或降速。\n您可以在游戏中使用热键切换此功能,它可以在 Ryujinx 的键盘热键设置进行设置。\n\n如果不确定则保留为 200。", "zh_TW": "" } }, @@ -18218,7 +18218,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "{0} FPS ({1}ms), 涡轮 ({2}%)", "zh_TW": "" } }, @@ -23968,7 +23968,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "涡轮模式: ", "zh_TW": "" } }, @@ -23993,7 +23993,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "涡轮模式热键。\n可以在 Ryujinx CPU 设置中配置涡轮模式的行为。\n\n如果不确定请保留为未绑定。", "zh_TW": "" } }, @@ -24018,7 +24018,7 @@ "th_TH": "", "tr_TR": "", "uk_UA": "", - "zh_CN": "", + "zh_CN": "仅在按下时", "zh_TW": "" } },