Compare commits

...

17 Commits

Author SHA1 Message Date
Vudjun
61975ca44d Add logging in socket implementation (#661)
Adds logging to most Socket calls to help with debugging network
issues.

Shouldn't affect any functionality. There's a small chance it could spam
the log in some games.
2025-02-16 01:03:44 -06:00
Evan Husted
66054dd225 UI: RPC: Remove git hash from RPC ryujinx logo hover information 2025-02-16 00:58:25 -06:00
Evan Husted
b1f61e5143 misc: chore: [ci skip] Reformat PlayReports.cs 2025-02-15 20:52:03 -06:00
Evan Husted
0d7d0e8092 UI: Add descriptions of what each dynamic RPC formatter actually shows when hovering whether it has support in the game info popup 2025-02-15 20:45:27 -06:00
Evan Husted
aa2178dbe5 UI: Button to open screenshots folder in File menu 2025-02-15 20:25:17 -06:00
Evan Husted
f92d09711b misc: chore: rewrite PlayReports.Analyzer creation to use Lazy and create the value alongside DiscordIntegrationModule init 2025-02-15 19:02:53 -06:00
Evan Husted
45ee8cd0e8 misc: Play Report Analyzer: Skyward Sword HD rupee count 2025-02-15 19:02:04 -06:00
Evan Husted
395bbd144a misc: chore: Change Analyzer AddSpec logic to log the non-hexadecimal value and ignore the added entry instead of throwing an exception 2025-02-15 19:01:24 -06:00
Evan Husted
744d813b87 misc: i18n: change localization of Ignore Applet to Ignore Controller Applet & redo tooltip 2025-02-15 00:40:18 -06:00
Evan Husted
7d59ada798 misc: chore: rename IgnoreApplet to IgnoreControllerApplet 2025-02-15 00:25:28 -06:00
Evan Husted
a4b5304935 UI: Refresh game list when emulated Switch language is changed (to show different logos/names) 2025-02-15 00:20:01 -06:00
Evan Husted
0965ee905d misc: Fix Match System Time not persisting between emulator launches 2025-02-14 22:18:15 -06:00
FluffyOMC
855161b23b Prevent log from showing negative JIT cache sizes (32bit-int overflow) (#664)
![image](https://github.com/user-attachments/assets/5820ce7b-cbfe-4908-8f5e-7ee82040ee1a)
2025-02-14 21:37:19 -06:00
Daenorth
6b55d158b7 Norwegian locale update & update Jackbox 5 & 6 compat (#658) 2025-02-13 22:38:55 -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
27 changed files with 472 additions and 220 deletions

View File

@@ -2988,8 +2988,8 @@
010015D003EE4000,"The Jackbox Party Pack 2",online-working,playable,2022-08-22 18:23:40
0100CC80013D6000,"The Jackbox Party Pack 3",slow;online-working,playable,2022-08-22 18:41:06
0100E1F003EE8000,"The Jackbox Party Pack 4",online-working,playable,2022-08-22 18:56:34
01006fe0096ac000,"The Jackbox Party Pack 5",ldn-untested,boots,2025-02-03 22:32:00
01005a400db52000,"The Jackbox Party Pack 6",ldn-untested,boots,2025-02-03 22:32:00
01006fe0096ac000,"The Jackbox Party Pack 5",slow;online-working,ingame,2025-02-14 05:32:00
01005a400db52000,"The Jackbox Party Pack 6",slow;online-working,ingame,2025-02-14 05:26:00
010052C00B184000,"The Journey Down: Chapter One",nvdec,playable,2021-02-24 13:32:41
01006BC00B188000,"The Journey Down: Chapter Three",nvdec,playable,2021-02-24 13:45:27
01009AB00B186000,"The Journey Down: Chapter Two",nvdec,playable,2021-02-24 13:32:13
1 title_id game_name labels status last_updated
2988 010015D003EE4000 The Jackbox Party Pack 2 online-working playable 2022-08-22 18:23:40
2989 0100CC80013D6000 The Jackbox Party Pack 3 slow;online-working playable 2022-08-22 18:41:06
2990 0100E1F003EE8000 The Jackbox Party Pack 4 online-working playable 2022-08-22 18:56:34
2991 01006fe0096ac000 The Jackbox Party Pack 5 ldn-untested slow;online-working boots ingame 2025-02-03 22:32:00 2025-02-14 05:32:00
2992 01005a400db52000 The Jackbox Party Pack 6 ldn-untested slow;online-working boots ingame 2025-02-03 22:32:00 2025-02-14 05:26:00
2993 010052C00B184000 The Journey Down: Chapter One nvdec playable 2021-02-24 13:32:41
2994 01006BC00B188000 The Journey Down: Chapter Three nvdec playable 2021-02-24 13:45:27
2995 01009AB00B186000 The Journey Down: Chapter Two nvdec playable 2021-02-24 13:32:13

View File

@@ -186,7 +186,7 @@ namespace ARMeilleure.Translation.Cache
int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize);

View File

@@ -166,7 +166,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
int newRegionNumber = _activeRegionIndex;
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation).");
_cacheAllocator = new CacheMemoryAllocator(CacheSize);

View File

@@ -34,6 +34,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
if (errorCode != LinuxError.SUCCESS)
{
if (errorCode != LinuxError.EWOULDBLOCK)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}.");
}
result = -1;
}
@@ -66,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Creating socket with domain={domain}, type={type}, protocol={protocol}");
BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
type &= BsdSocketType.TypeMask;
@@ -111,6 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (exempt)
{
Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket.");
newBsdSocket.Disconnect();
}
@@ -797,7 +804,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
errno = socket.Listen(backlog);
}
else
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'.");
}
return WriteBsdResult(context, 0, errno);
}

View File

@@ -92,18 +92,30 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{
newSocket = new ManagedSocket(Socket.Accept());
IPEndPoint remoteEndPoint = newSocket.RemoteEndPoint;
bool isPrivateIp = remoteEndPoint.Address.ToString().StartsWith("192.168.");
Logger.Info?.PrintMsg(LogClass.ServiceBsd,
isPrivateIp
? $"Accepted connection from {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}"
: $"Accepted connection from {ProtocolType}/***:{remoteEndPoint.Port}");
return LinuxError.SUCCESS;
}
catch (SocketException exception)
{
newSocket = null;
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
public LinuxError Bind(IPEndPoint localEndPoint)
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}");
try
{
Socket.Bind(localEndPoint);
@@ -112,6 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -123,6 +139,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public LinuxError Connect(IPEndPoint remoteEndPoint)
{
bool isLDNPrivateIP = remoteEndPoint.Address.ToString().StartsWith("192.168.");
if (isLDNPrivateIP)
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}");
}
else
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/***:{remoteEndPoint.Port}");
}
try
{
Socket.Connect(remoteEndPoint);
@@ -137,6 +162,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
else
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -144,11 +173,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public void Disconnect()
{
Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting");
Socket.Disconnect(true);
}
public void Dispose()
{
Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed");
Socket.Close();
Socket.Dispose();
}
@@ -159,10 +190,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{
Socket.Listen(backlog);
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}");
return LinuxError.SUCCESS;
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -182,11 +219,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
bool hasEmittedBlockingWarning = false;
private bool _hasEmittedBlockingWarning;
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
{
@@ -202,10 +243,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true;
}
if (Blocking && !hasEmittedBlockingWarning)
if (Blocking && !_hasEmittedBlockingWarning)
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
hasEmittedBlockingWarning = true;
_hasEmittedBlockingWarning = true;
}
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
@@ -214,6 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -245,10 +290,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true;
}
if (Blocking && !hasEmittedBlockingWarning)
if (Blocking && !_hasEmittedBlockingWarning)
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
hasEmittedBlockingWarning = true;
_hasEmittedBlockingWarning = true;
}
if (!Socket.IsBound)
@@ -265,6 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -288,6 +337,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -304,6 +357,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -341,6 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -387,6 +448,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -519,6 +584,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
@@ -557,6 +626,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
}
catch (SocketException exception)
{
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}

View File

@@ -1,4 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -64,10 +66,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
{
if (_proxy.Supported(domain, type, protocol))
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy");
return new LdnProxySocket(domain, type, protocol, _proxy);
}
else
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"LDN proxy does not support socket {domain}, {type}, {protocol}");
}
}
else
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Opening socket using host networking stack");
}
return new DefaultSocket(domain, type, protocol, lanInterfaceId);
}
}

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",
@@ -447,6 +447,31 @@
"zh_TW": "開啟 Ryujinx 資料夾"
}
},
{
"ID": "MenuBarFileOpenScreenshotsFolder",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Open Screenshots Folder",
"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": "MenuBarFileOpenLogsFolder",
"Translations": {
@@ -576,7 +601,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": "",
@@ -585,11 +610,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "UI를 숨긴 상태에서 게임 시작",
"no_NO": "",
"no_NO": "Start Spillet med UI Gjemt",
"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 +776,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 +876,7 @@
"ID": "MenuBarActionsInstallKeys",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Schlüssel installieren",
"el_GR": "",
"en_US": "Install Keys",
"es_ES": "",
@@ -876,7 +901,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 +926,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 +1026,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 +1251,7 @@
"ID": "MenuBarHelpFaqAndGuides",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Anleitungen",
"el_GR": "",
"en_US": "FAQ & Guides",
"es_ES": "",
@@ -1251,7 +1276,7 @@
"ID": "MenuBarHelpFaq",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "FAQ & Fehlerbehebung Seite",
"el_GR": "",
"en_US": "FAQ & Troubleshooting Page",
"es_ES": "",
@@ -1276,7 +1301,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 +1326,7 @@
"ID": "MenuBarHelpSetup",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Setup- und Konfigurationsanleitung",
"el_GR": "",
"en_US": "Setup & Configuration Guide",
"es_ES": "",
@@ -1326,7 +1351,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 +1376,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 +1401,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 +1551,7 @@
"ID": "GameListHeaderDeveloper",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Entwickelt von {0}",
"el_GR": "",
"en_US": "Developed by {0}",
"es_ES": "",
@@ -1535,7 +1560,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Utviklet av {0}",
"pl_PL": "",
"pt_BR": "Desenvolvido por {0}",
"ru_RU": "",
@@ -1826,7 +1851,7 @@
"ID": "GameListHeaderCompatibilityStatus",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Kompatibilität:",
"el_GR": "",
"en_US": "Compatibility:",
"es_ES": "",
@@ -1835,7 +1860,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Kompatibilitet",
"pl_PL": "",
"pt_BR": "Compatibilidade:",
"ru_RU": "",
@@ -1860,7 +1885,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Tittel ID:",
"pl_PL": "",
"pt_BR": "ID do título:",
"ru_RU": "",
@@ -1885,7 +1910,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Spill som Arrangeres: {0}",
"pl_PL": "",
"pt_BR": "Jogos hospedados: {0}",
"ru_RU": "",
@@ -1910,7 +1935,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Online-spillere: {0}",
"pl_PL": "",
"pt_BR": "Jogadores Online: {0}",
"ru_RU": "",
@@ -2260,7 +2285,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "PPTC 캐시 제거",
"no_NO": "",
"no_NO": "Tøm PPTC-bufferen",
"pl_PL": "",
"pt_BR": "Limpar cache PPTC",
"ru_RU": "",
@@ -2285,7 +2310,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "앱의 모든 PPTC 캐시 파일 삭제",
"no_NO": "",
"no_NO": "Sletter alle PPTC-cache-filer for applikasjonen",
"pl_PL": "",
"pt_BR": "Apagar os arquivos de cache PPTC do aplicativo",
"ru_RU": "",
@@ -2614,7 +2639,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": "",
@@ -2760,7 +2785,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "호환성 항목 표시",
"no_NO": "",
"no_NO": "Vis kompatibilitetsoppføring",
"pl_PL": "",
"pt_BR": "Mostrar entrada de compatibilidade",
"ru_RU": "",
@@ -2785,7 +2810,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "일반적으로 도움말 메뉴를 통해 접근할 수 있는 호환성 목록에 선택한 게임을 표시합니다.",
"no_NO": "",
"no_NO": "Vis det valgte spillet i kompatibilitetslisten, som du vanligvis får tilgang til via Hjelp-menyen.",
"pl_PL": "",
"pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.",
"ru_RU": "",
@@ -2810,7 +2835,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "게임 통계 표시",
"no_NO": "",
"no_NO": "Vis Spill Info",
"pl_PL": "",
"pt_BR": "Mostrar informações do jogo",
"ru_RU": "",
@@ -2835,7 +2860,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "그리드 보기 레이아웃에서 누락된 현재 선택된 게임에 대한 다양한 정보를 표시합니다.",
"no_NO": "",
"no_NO": "Vis statistikk og detaljer om det valgte spillet.",
"pl_PL": "",
"pt_BR": "Mostrar estatísticas e detalhes sobre o jogo selecionado no momento.",
"ru_RU": "",
@@ -3062,7 +3087,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": "",
@@ -3360,7 +3385,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Se etter Oppdateringer:",
"pl_PL": "",
"pt_BR": "Verificar atualizações:",
"ru_RU": "",
@@ -3385,7 +3410,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Av",
"pl_PL": "",
"pt_BR": "Desligado",
"ru_RU": "",
@@ -3410,7 +3435,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Spør",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
@@ -3435,7 +3460,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Bakgrunn",
"pl_PL": "",
"pt_BR": "Fundo",
"ru_RU": "",
@@ -3460,7 +3485,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "På Emulator Fokus Tapt:",
"pl_PL": "",
"pt_BR": "Ao perder o Foco do emulador:",
"ru_RU": "",
@@ -3485,7 +3510,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Gjør Ingenting",
"pl_PL": "",
"pt_BR": "Não fazer nada",
"ru_RU": "",
@@ -3510,7 +3535,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Blokkinngang",
"pl_PL": "",
"pt_BR": "Bloquear entrada",
"ru_RU": "",
@@ -3535,7 +3560,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Demp Lyd",
"pl_PL": "",
"pt_BR": "Ficar mudo",
"ru_RU": "",
@@ -3560,7 +3585,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Blokker Inputs og demp Volumet",
"pl_PL": "",
"pt_BR": "Bloquear entrada & Ficar mudo",
"ru_RU": "",
@@ -3585,7 +3610,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Pause Emulatoren",
"pl_PL": "",
"pt_BR": "Pausar a emulação",
"ru_RU": "",
@@ -4635,7 +4660,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "스웨덴어",
"no_NO": "",
"no_NO": "Svensk",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
@@ -4735,7 +4760,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "매치 시스템 시간",
"no_NO": "",
"no_NO": "Match systemtid",
"pl_PL": "",
"pt_BR": "Sincronizar data e hora com o sistema PC",
"ru_RU": "",
@@ -5148,28 +5173,28 @@
}
},
{
"ID": "SettingsTabSystemIgnoreApplet",
"ID": "SettingsTabSystemIgnoreControllerApplet",
"Translations": {
"ar_SA": "",
"de_DE": "Applet ignorieren",
"el_GR": "Αγνοήστε το Applet",
"en_US": "Ignore Applet",
"es_ES": "Ignorar el Applet",
"fr_FR": "Ignorer la déconnexion de la manette",
"de_DE": "",
"el_GR": "",
"en_US": "Ignore Controller Applet",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "Ignora l'applet",
"ja_JP": "アプレットを無視する",
"ko_KR": "애플릿 무시",
"no_NO": "Ignorer appleten",
"pl_PL": "Ignoruj aplet",
"pt_BR": "Ignorar applet",
"ru_RU": "Игнорировать Апплет",
"sv_SE": "Ignorera applet",
"th_TH": "เมินเฉย Applet",
"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": "忽略小程式"
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
@@ -6010,7 +6035,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Aktivere UI-logger",
"pl_PL": "",
"pt_BR": "Habilitar logs da IU",
"ru_RU": "",
@@ -6410,7 +6435,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Tilbakestill innstillinger",
"pl_PL": "",
"pt_BR": "Redefinir configurações",
"ru_RU": "",
@@ -6435,7 +6460,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Jeg vil tilbakestille innstillingene mine.",
"pl_PL": "",
"pt_BR": "Quero redefinir minhas configurações.",
"ru_RU": "",
@@ -6464,7 +6489,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Ok",
"th_TH": "ตกลง",
"tr_TR": "Tamam",
"uk_UA": "",
@@ -8360,11 +8385,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "비활성화",
"no_NO": "",
"no_NO": "Deaktiver",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Inaktivera",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8385,11 +8410,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "레인보우",
"no_NO": "",
"no_NO": "Regnbue",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Regnbåge",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8410,7 +8435,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "레인보우 속도",
"no_NO": "",
"no_NO": "Regnbue Hastighet",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
@@ -8435,11 +8460,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "색상",
"no_NO": "",
"no_NO": "Farge",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"sv_SE": "Färg",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -13685,7 +13710,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "다음에서 모든 PPTC 데이터를 제거하려고 합니다:\n\n{0}\n\n계속하시겠습니까?",
"no_NO": "",
"no_NO": "Du er i ferd med å slette alle PPTC-data fra:\n\n{0}\n\n\nEr du sikker på at du vil fortsette?",
"pl_PL": "",
"pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?",
"ru_RU": "",
@@ -15239,7 +15264,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",
@@ -16523,28 +16548,28 @@
}
},
{
"ID": "IgnoreAppletTooltip",
"ID": "IgnoreControllerAppletTooltip",
"Translations": {
"ar_SA": "لن يظهر مربع الحوار الخارجي \"تطبيق وحدة التحكم\" إذا تم فصل لوحة الألعاب أثناء اللعب. ولن تظهر مطالبة بإغلاق مربع الحوار أو إعداد وحدة تحكم جديدة. وبمجرد إعادة توصيل وحدة التحكم التي تم فصلها سابقًا، ستستأنف اللعبة تلقائيًا.",
"de_DE": "Der externe Dialog \"Controller-Applet\" wird nicht angezeigt, wenn das Gamepad während des Spiels getrennt wird. Es erfolgt keine Aufforderung, den Dialog zu schließen oder einen neuen Controller einzurichten. Sobald der zuvor getrennte Controller wieder angeschlossen wird, wird das Spiel automatisch fortgesetzt.",
"el_GR": "Το εξωτερικό παράθυρο διαλόγου \"Ελεγκτής μικροεφαρμογής\" δεν θα εμφανιστεί εάν το gamepad αποσυνδεθεί κατά τη διάρκεια του παιχνιδιού. Δεν θα σας ζητηθεί να κλείσετε το παράθυρο διαλόγου ή να ρυθμίσετε έναν νέο ελεγκτή. Μόλις επανασυνδεθεί το χειριστήριο που είχε αποσυνδεθεί προηγουμένως, το παιχνίδι θα συνεχιστεί αυτόματα.",
"en_US": "The external dialog \"Controller Applet\" will not appear if the gamepad is disconnected during gameplay. There will be no prompt to close the dialog or set up a new controller. Once the previously disconnected controller is reconnected, the game will automatically resume.",
"es_ES": "El cuadro de diálogo externo \"Applet del controlador\" no aparecerá si el gamepad se desconecta durante el juego. No aparecerá ningún mensaje para cerrar el cuadro de diálogo o configurar un nuevo controlador. Una vez que se vuelva a conectar el controlador que se había desconectado anteriormente, el juego se reanudará automáticamente.",
"fr_FR": "La boîte de dialogue externe \"Programme Manette\" n'apparaîtra pas si la manette est déconnectée en jeu. Il n'y aura aucune boîte de dialogue ouverte pour configurer une nouvelle manette. Une fois que la manette précédemment déconnectée est reconnectée, le jeu reprendra automatiquement. \n\nLaissez désactivé en cas d'incertitude.",
"he_IL": "תיבת הדו-שיח החיצונית \"Controller Applet\" לא תופיע אם ה-Gamepad מנותק במהלך המשחק. לא תהיה הנחיה לסגור את תיבת הדו-שיח או להגדיר בקר חדש. ברגע שהבקר שנותק בעבר יתחבר מחדש, המשחק יתחדש אוטומטית.",
"it_IT": "La finestra di dialogo esterna \"Controller Applet\" non apparirà se il gamepad viene disconnesso durante il gioco. Non ci sarà alcun prompt per chiudere la finestra di dialogo o impostare un nuovo controller. Una volta che il controller disconnesso in precedenza viene ricollegato, il gioco riprenderà automaticamente.",
"ja_JP": "ゲームプレイ中にゲームパッドが切断された場合、外部ダイアログ「コントローラーアプレット」は表示されません。このダイアログを閉じるか、新しいコントローラーを設定するように求めるプロンプトは表示されません。以前に切断されたコントローラーが再接続されると、ゲームは自動的に再開されます。",
"ko_KR": "게임 플레이 중에 게임패드 연결이 끊어지면 외부 대화 상자 \"컨트롤러 애플릿\"이 나타나지 않습니다. 대화 상자를 닫거나 새 컨트롤러를 설정하라는 메시지가 표시되지 않습니다. 이전에 연결이 끊어진 컨트롤러가 다시 연결되면 게임이 자동으로 다시 시작됩니다.",
"no_NO": "Den eksterne dialogboksen «Controller Applet» vises ikke hvis gamepaden kobles fra under spilling. Du blir ikke bedt om å lukke dialogboksen eller konfigurere en ny kontroller. Når den tidligere frakoblede kontrolleren er koblet til igjen, fortsetter spillet automatisk.",
"pl_PL": "Zewnętrzny dialog \"Controller Applet\" nie pojawi się, jeśli gamepad zostanie odłączony podczas rozgrywki. Nie pojawi się monit o zamknięcie dialogu lub skonfigurowanie nowego kontrolera. Po ponownym podłączeniu poprzednio odłączonego kontrolera gra zostanie automatycznie wznowiona.",
"pt_BR": "O diálogo externo \"Controller Applet\" não aparecerá se o gamepad for desconectado durante o jogo. Não haverá prompt para fechar o diálogo ou configurar um novo controle. Assim que o controle desconectado anteriormente for reconectado, o jogo será retomado automaticamente.",
"ru_RU": "Внешний диалог \"Апплет контроллера\" не появится, если геймпад будет отключен во время игры. Не будет предложено закрыть диалог или настроить новый контроллер. После повторного подключения ранее отключенного контроллера игра автоматически возобновится.",
"sv_SE": "Den externa dialogen \"Handkontroller-applet\" kommer inte att visas om din gamepad är frånkopplad under spel. Det finns ingen fråga att stänga dialogen eller konfigurera en ny handkontroller. När den tidigare frånkopplade handkontrollern återansluts så kommer spelet att automatiskt återupptas.",
"th_TH": "กล่องโต้ตอบภายนอก \"แอปเพล็ตตัวควบคุม\" จะไม่ปรากฏขึ้นหากแป้นเกมถูกตัดการเชื่อมต่อระหว่างการเล่นเกม จะไม่มีข้อความแจ้งให้ปิดกล่องโต้ตอบหรือตั้งค่าตัวควบคุมใหม่ เมื่อเชื่อมต่อคอนโทรลเลอร์ที่ตัดการเชื่อมต่อก่อนหน้านี้อีกครั้ง เกมจะดำเนินการต่อโดยอัตโนมัติ",
"tr_TR": "Oyun sırasında oyun kumandasının bağlantısı kesilirse, harici \"Controller Applet\" iletişim kutusu görünmez. İletişim kutusunu kapatma veya yeni bir kumanda ayarlama isteği olmaz. Daha önce bağlantısı kesilen kumanda tekrar bağlandığında oyun otomatik olarak devam eder.",
"uk_UA": "Зовнішнє діалогове вікно \"Аплет контролера\" не з’являтиметься, якщо геймпад буде від’єднано під час гри. Не буде запиту закрити діалогове вікно чи налаштувати новий контролер. Після повторного підключення раніше від’єднаного контролера гра автоматично відновиться.",
"zh_CN": "如果游戏手柄在游戏过程中断开连接,则不会出现外部对话框“控制器小程序”。不会提示关闭对话框或设置新控制器。一旦先前断开连接的控制器重新连接,游戏将自动恢复。",
"zh_TW": "如果遊戲手把在遊戲過程中斷開連接,則外部對話方塊「控制器小程式」將不會出現。不會提示關閉對話方塊或設定新控制器。一旦先前斷開的控制器重新連接,遊戲將自動恢復。"
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "The Controller Applet dialog will not appear if the gamepad is disconnected while an application is running.\n\nLeave OFF if unsure.",
"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": ""
}
},
{
@@ -17010,7 +17035,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.",
"pl_PL": "",
"pt_BR": "Imprimir mensagens de log do Avalonia (IU) no console.",
"ru_RU": "",
@@ -17197,6 +17222,31 @@
"zh_TW": "開啟 Ryujinx 檔案系統資料夾"
}
},
{
"ID": "OpenScreenshotFolderTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Open Ryujinx screenshots folder",
"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": "OpenRyujinxLogsTooltip",
"Translations": {
@@ -17935,7 +17985,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Oppdatering tilgjengelig!",
"pl_PL": "",
"pt_BR": "Atualização disponível!",
"ru_RU": "",
@@ -18664,7 +18714,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} - Інформація",
@@ -19735,11 +19785,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "LED 설정",
"no_NO": "",
"no_NO": "LED-innstillinger",
"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 +21989,7 @@
"pl_PL": "Głoś",
"pt_BR": "",
"ru_RU": "Громкость",
"sv_SE": "",
"sv_SE": "Volym",
"th_TH": "ระดับเสียง",
"tr_TR": "Ses",
"uk_UA": "Гуч.",
@@ -23351,7 +23401,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 +23426,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 +23451,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 +23476,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 +23501,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 +23526,7 @@
"ID": "SettingsTabSystemCustomVSyncIntervalPercentage",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Benutzerdefinierte Bildwiederholfrequenz %:",
"el_GR": "",
"en_US": "Custom Refresh Rate %:",
"es_ES": "",
@@ -23501,7 +23551,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 +23601,7 @@
"ID": "SettingsTabHotkeysToggleVSyncModeHotkey",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "VSync-Modus umschalten:",
"el_GR": "",
"en_US": "Toggle VSync mode:",
"es_ES": "",
@@ -23576,7 +23626,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 +23651,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 +23676,7 @@
"ID": "CompatibilityListLastUpdated",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Zuletzt aktualisiert: {0}",
"el_GR": "",
"en_US": "Last updated: {0}",
"es_ES": "",
@@ -23651,7 +23701,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 +23726,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 +23776,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 +23801,7 @@
"ID": "CompatibilityListPlayable",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Spielbar",
"el_GR": "",
"en_US": "Playable",
"es_ES": "",
@@ -23776,7 +23826,7 @@
"ID": "CompatibilityListIngame",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Im Spiel",
"el_GR": "",
"en_US": "Ingame",
"es_ES": "",
@@ -23885,7 +23935,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.",
"pl_PL": "",
"pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.",
"ru_RU": "",
@@ -23910,7 +23960,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.",
"pl_PL": "",
"pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.",
"ru_RU": "",
@@ -23935,7 +23985,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.",
"pl_PL": "",
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
"ru_RU": "",
@@ -23951,7 +24001,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": "",
@@ -23960,7 +24010,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.",
"pl_PL": "",
"pt_BR": "Inizializa, mas não passa da tela de título.",
"ru_RU": "",
@@ -23976,7 +24026,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": "",
@@ -23985,7 +24035,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.",
"pl_PL": "",
"pt_BR": "Não inicializa ou não mostra sinais de atividade.",
"ru_RU": "",
@@ -24001,7 +24051,7 @@
"ID": "ExtractAocListHeader",
"Translations": {
"ar_SA": "",
"de_DE": "",
"de_DE": "Wähle ein DLC zum Extrahieren aus",
"el_GR": "",
"en_US": "Select a DLC to Extract",
"es_ES": "",
@@ -24014,7 +24064,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": "",
@@ -24035,7 +24085,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Rikt nærværsbilde",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
@@ -24060,7 +24110,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "Dynamisk og rik tilstedeværelse",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
@@ -24073,4 +24123,4 @@
}
}
]
}
}

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.Ava
private static readonly string _description =
ReleaseInformation.IsValid
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}"
: "dev build";
private const string ApplicationId = "1293250299716173864";
@@ -56,6 +56,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
HorizonStatic.PlayReport += HandlePlayReport;
PlayReports.Initialize();
}
private static void Update(object sender, ReactiveEventArgs<bool> evnt)

View File

@@ -148,7 +148,7 @@ namespace Ryujinx.Headless
IgnoreMissingServices = configurationState.System.IgnoreMissingServices;
if (NeedsOverride(nameof(IgnoreControllerApplet)))
IgnoreControllerApplet = configurationState.System.IgnoreApplet;
IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet;
return;

View File

@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Applet
bool okPressed = false;
if (ConfigurationState.Instance.System.IgnoreApplet)
if (ConfigurationState.Instance.System.IgnoreControllerApplet)
return false;
Dispatcher.UIThread.InvokeAsync(async () =>

View File

@@ -119,17 +119,23 @@
TextWrapping="Wrap" >
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<ui:SymbolIcon Foreground="ForestGreen" Symbol="Checkmark" IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
<StackPanel Orientation="Horizontal" Spacing="5" ToolTip.Tip="{Binding DynamicRichPresenceDescription}">
<ui:SymbolIcon
Foreground="ForestGreen"
Symbol="Checkmark"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock
Foreground="ForestGreen"
HorizontalAlignment="Stretch"
IsVisible="{Binding AppData.HasDynamicRichPresenceSupport}"
Text="{ext:Locale GameInfoRpcDynamic}"
TextAlignment="Start"
TextWrapping="Wrap" >
TextWrapping="Wrap">
</TextBlock>
<ui:SymbolIcon Foreground="Red" Symbol="Cancel" IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
<ui:SymbolIcon
Foreground="Red"
Symbol="Cancel"
IsVisible="{Binding !AppData.HasDynamicRichPresenceSupport}"/>
<TextBlock
Foreground="Red"
HorizontalAlignment="Stretch"

View File

@@ -1,6 +1,7 @@
using Gommon;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Ava.Utilities.PlayReport;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -10,6 +11,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationDataViewModel(ApplicationData appData) => AppData = appData;
public string DynamicRichPresenceDescription =>
AppData.HasDynamicRichPresenceSupport
? AppData.RichPresenceSpec.Value.Description
: GameSpec.DefaultDescription;
public string FormattedVersion => LocaleManager.Instance[LocaleKeys.GameListHeaderVersion].Format(AppData.Version);
public string FormattedDeveloper => LocaleManager.Instance[LocaleKeys.GameListHeaderDeveloper].Format(AppData.Developer);
public string FormattedFileExtension => LocaleManager.Instance[LocaleKeys.GameListHeaderFileExtension].Format(AppData.FileExtension);

View File

@@ -1347,6 +1347,25 @@ namespace Ryujinx.Ava.UI.ViewModels
OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
}
public void OpenScreenshotsFolder()
{
string screenshotsDir = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
try
{
if (!Directory.Exists(screenshotsDir))
Directory.CreateDirectory(screenshotsDir);
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {screenshotsDir}. Error : {ex.GetType().Name}", "Screenshot");
return;
}
OpenHelper.OpenFolder(screenshotsDir);
}
public void OpenLogsFolder()
{
string logPath = AppDataManager.GetOrCreateLogsDir();

View File

@@ -49,8 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _graphicsBackendMultithreadingIndex;
private float _volume;
[ObservableProperty] private bool _isVulkanAvailable = true;
[ObservableProperty] private bool _gameDirectoryChanged;
[ObservableProperty] private bool _autoloadDirectoryChanged;
[ObservableProperty] private bool _gameListNeedsRefresh;
private readonly List<string> _gpuIds = [];
private int _graphicsBackendIndex;
private int _scalingFilter;
@@ -527,7 +526,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
DramSize = config.System.DramSize;
IgnoreMissingServices = config.System.IgnoreMissingServices;
IgnoreApplet = config.System.IgnoreApplet;
IgnoreApplet = config.System.IgnoreControllerApplet;
// CPU
EnablePptc = config.System.EnablePtc;
@@ -593,16 +592,8 @@ namespace Ryujinx.Ava.UI.ViewModels
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.GameDirs.Value = [..GameDirectories];
config.UI.AutoloadDirs.Value = [..AutoloadDirectories];
config.UI.BaseStyle.Value = BaseStyleIndex switch
{
@@ -623,8 +614,11 @@ namespace Ryujinx.Ava.UI.ViewModels
// System
config.System.Region.Value = (Region)Region;
if (config.System.Language.Value != (Language)Language)
GameListNeedsRefresh = true;
config.System.Language.Value = (Language)Language;
if (_validTzRegions.Contains(TimeZone))
{
config.System.TimeZone.Value = TimeZone;
@@ -635,7 +629,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.DramSize.Value = DramSize;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.IgnoreApplet.Value = IgnoreApplet;
config.System.IgnoreControllerApplet.Value = IgnoreApplet;
// CPU
config.System.EnablePtc.Value = EnablePptc;
@@ -715,8 +709,7 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettingsEvent?.Invoke();
GameDirectoryChanged = false;
AutoloadDirectoryChanged = false;
GameListNeedsRefresh = false;
}
private static void RevertIfNotSaved()

View File

@@ -66,6 +66,10 @@
Command="{Binding OpenRyujinxFolder}"
Header="{ext:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{Binding OpenScreenshotsFolder}"
Header="{ext:Locale MenuBarFileOpenScreenshotsFolder}"
ToolTip.Tip="{ext:Locale OpenScreenshotFolderTooltip}"/>
<MenuItem
Command="{Binding OpenLogsFolder}"
Header="{ext:Locale MenuBarFileOpenLogsFolder}"

View File

@@ -316,8 +316,8 @@
</CheckBox>
<CheckBox
IsChecked="{Binding IgnoreApplet}"
ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreControllerApplet}" />
</CheckBox>
<CheckBox
IsChecked="{Binding EnableCustomVSyncInterval}"

View File

@@ -36,11 +36,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
directories.Add(path);
addDirBox.Clear();
if (isGameList)
ViewModel.GameDirectoryChanged = true;
else
ViewModel.AutoloadDirectoryChanged = true;
ViewModel.GameListNeedsRefresh = true;
}
else
{
@@ -50,10 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
{
directories.Add(folder.Value.Path.LocalPath);
if (isGameList)
ViewModel.GameDirectoryChanged = true;
else
ViewModel.AutoloadDirectoryChanged = true;
ViewModel.GameListNeedsRefresh = true;
}
}
}
@@ -65,7 +59,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
foreach (string path in new List<string>(GameDirsList.SelectedItems.Cast<string>()))
{
ViewModel.GameDirectories.Remove(path);
ViewModel.GameDirectoryChanged = true;
ViewModel.GameListNeedsRefresh = true;
}
if (GameDirsList.ItemCount > 0)
@@ -81,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
foreach (string path in new List<string>(AutoloadDirsList.SelectedItems.Cast<string>()))
{
ViewModel.AutoloadDirectories.Remove(path);
ViewModel.AutoloadDirectoryChanged = true;
ViewModel.GameListNeedsRefresh = true;
}
if (AutoloadDirsList.ItemCount > 0)

View File

@@ -45,7 +45,7 @@ namespace Ryujinx.Ava.UI.Windows
{
InputPage.InputView?.SaveCurrentProfile();
if (Owner is MainWindow window && (ViewModel.GameDirectoryChanged || ViewModel.AutoloadDirectoryChanged))
if (Owner is MainWindow window && ViewModel.GameListNeedsRefresh)
{
window.LoadApplications();
}

View File

@@ -10,6 +10,7 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Compat;
using Ryujinx.Ava.Utilities.PlayReport;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions;
@@ -35,9 +36,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
{
_id = value;
Compatibility = CompatibilityCsv.Find(Id);
Compatibility = CompatibilityCsv.Find(value);
RichPresenceSpec = PlayReports.Analyzer.TryGetSpec(IdString, out GameSpec gameSpec)
? gameSpec
: default(Optional<GameSpec>);
}
}
public Optional<GameSpec> RichPresenceSpec { get; set; }
public string Developer { get; set; } = "Unknown";
public string Version { get; set; } = "0";
public int PlayerCount { get; set; }
@@ -46,7 +52,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public bool HasLdnGames => PlayerCount != 0 && GameCount != 0;
public bool HasRichPresenceAsset => DiscordIntegrationModule.HasAssetImage(IdString);
public bool HasDynamicRichPresenceSupport => DiscordIntegrationModule.HasAnalyzer(IdString);
public bool HasDynamicRichPresenceSupport => RichPresenceSpec.HasValue;
public TimeSpan TimePlayed { get; set; }
public DateTime? LastPlayed { get; set; }

View File

@@ -183,7 +183,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
public bool ShowConfirmExit { get; set; }
/// <summary>
/// ignore "Applet" dialog
/// Ignore Controller Applet dialog
/// </summary>
public bool IgnoreApplet { get; set; }

View File

@@ -88,6 +88,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.Region.Value = cff.SystemRegion;
System.TimeZone.Value = cff.SystemTimeZone;
System.SystemTimeOffset.Value = cff.SystemTimeOffset;
System.MatchSystemTime.Value = cff.MatchSystemTime;
System.EnableDockedMode.Value = cff.DockedMode;
System.EnablePtc.Value = cff.EnablePtc;
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
@@ -99,7 +100,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.MemoryManagerMode.Value = cff.MemoryManagerMode;
System.DramSize.Value = cff.DramSize;
System.IgnoreMissingServices.Value = cff.IgnoreMissingServices;
System.IgnoreApplet.Value = cff.IgnoreApplet;
System.IgnoreControllerApplet.Value = cff.IgnoreApplet;
System.UseHypervisor.Value = cff.UseHypervisor;
UI.GuiColumns.FavColumn.Value = cff.GuiColumns.FavColumn;

View File

@@ -383,7 +383,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// <summary>
/// Ignore Controller Applet
/// </summary>
public ReactiveObject<bool> IgnoreApplet { get; private set; }
public ReactiveObject<bool> IgnoreControllerApplet { get; private set; }
/// <summary>
/// Uses Hypervisor over JIT if available
@@ -424,8 +424,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
DramSize.LogChangesToValue(nameof(DramSize));
IgnoreMissingServices = new ReactiveObject<bool>();
IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices));
IgnoreApplet = new ReactiveObject<bool>();
IgnoreApplet.LogChangesToValue(nameof(IgnoreApplet));
IgnoreControllerApplet = new ReactiveObject<bool>();
IgnoreControllerApplet.LogChangesToValue(nameof(IgnoreControllerApplet));
AudioVolume = new ReactiveObject<float>();
AudioVolume.LogChangesToValue(nameof(AudioVolume));
UseHypervisor = new ReactiveObject<bool>();

View File

@@ -53,6 +53,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
SystemRegion = System.Region,
SystemTimeZone = System.TimeZone,
SystemTimeOffset = System.SystemTimeOffset,
MatchSystemTime = System.MatchSystemTime,
DockedMode = System.EnableDockedMode,
EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart,
@@ -80,7 +81,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
MemoryManagerMode = System.MemoryManagerMode,
DramSize = System.DramSize,
IgnoreMissingServices = System.IgnoreMissingServices,
IgnoreApplet = System.IgnoreApplet,
IgnoreApplet = System.IgnoreControllerApplet,
UseHypervisor = System.UseHypervisor,
GuiColumns = new GuiColumns
{
@@ -204,7 +205,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe;
System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB;
System.IgnoreMissingServices.Value = false;
System.IgnoreApplet.Value = false;
System.IgnoreControllerApplet.Value = false;
System.UseHypervisor.Value = true;
Multiplayer.LanInterfaceId.Value = "0";
Multiplayer.Mode.Value = MultiplayerMode.Disabled;

View File

@@ -1,5 +1,6 @@
using Gommon;
using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -19,6 +20,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public IReadOnlyList<GameSpec> Specs => new ReadOnlyCollection<GameSpec>(_specs);
public GameSpec GetSpec(string titleId) => _specs.First(x => x.TitleIds.ContainsIgnoreCase(titleId));
public bool TryGetSpec(string titleId, out GameSpec gameSpec)
=> (gameSpec = _specs.FirstOrDefault(x => x.TitleIds.ContainsIgnoreCase(titleId))) != null;
/// <summary>
/// Add an analysis spec matching a specific game by title ID, with the provided spec configuration.
/// </summary>
@@ -27,10 +33,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
public Analyzer AddSpec(string titleId, Func<GameSpec, GameSpec> transform)
{
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _))
return AddSpec(transform(GameSpec.Create(titleId)));
return AddSpec(transform(GameSpec.Create(titleId)));
Logger.Notice.PrintMsg(LogClass.Application,
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
return this;
}
/// <summary>
@@ -41,10 +49,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport
/// <returns>The current <see cref="Analyzer"/>, for chaining convenience.</returns>
public Analyzer AddSpec(string titleId, Action<GameSpec> transform)
{
Guard.Ensure(ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
if (ulong.TryParse(titleId, NumberStyles.HexNumber, null, out _))
return AddSpec(GameSpec.Create(titleId).Apply(transform));
return AddSpec(GameSpec.Create(titleId).Apply(transform));
Logger.Notice.PrintMsg(LogClass.Application,
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{titleId}'");
return this;
}
/// <summary>
@@ -57,10 +67,19 @@ namespace Ryujinx.Ava.Utilities.PlayReport
Func<GameSpec, GameSpec> transform)
{
string[] tids = titleIds.ToArray();
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x)))
return AddSpec(transform(GameSpec.Create(tids)));
return AddSpec(transform(GameSpec.Create(tids)));
Logger.Notice.PrintMsg(LogClass.Application,
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
tids.FormatCollection(
x => x,
separator: ", ",
prefix: "[",
suffix: "]"
)
}'");
return this;
}
/// <summary>
@@ -72,12 +91,21 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public Analyzer AddSpec(IEnumerable<string> titleIds, Action<GameSpec> transform)
{
string[] tids = titleIds.ToArray();
Guard.Ensure(tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _)),
$"Cannot use a non-hexadecimal string as the Title ID for a {nameof(GameSpec)}.");
if (tids.All(x => ulong.TryParse(x, NumberStyles.HexNumber, null, out _) && !string.IsNullOrEmpty(x)))
return AddSpec(GameSpec.Create(tids).Apply(transform));
return AddSpec(GameSpec.Create(tids).Apply(transform));
Logger.Notice.PrintMsg(LogClass.Application,
$"Tried to add a {nameof(GameSpec)} with a non-hexadecimal title ID value. Input: '{
tids.FormatCollection(
x => x,
separator: ", ",
prefix: "[",
suffix: "]"
)
}'");
return this;
}
/// <summary>
/// Add an analysis spec matching a specific game by title ID, with the provided pre-configured spec.
/// </summary>
@@ -105,13 +133,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport
{
if (!playReport.ReportData.IsDictionary)
return FormattedValue.Unhandled;
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out GameSpec spec))
if (!TryGetSpec(runningGameId, out GameSpec spec))
return FormattedValue.Unhandled;
foreach (FormatterSpecBase formatSpec in spec.ValueFormatters.OrderBy(x => x.Priority))
{
if (!formatSpec.Format(appMeta, playReport, out FormattedValue value))
if (!formatSpec.TryFormat(appMeta, playReport, out FormattedValue value))
continue;
return value;

View File

@@ -1,4 +1,5 @@
using Gommon;
using Humanizer;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
@@ -18,6 +19,9 @@ namespace Ryujinx.Ava.Utilities.PlayReport
< -201d => "Exploring the Depths",
_ => "Roaming Hyrule"
};
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
=> "rupee".ToQuantity(value.Matched.IntValue);
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";

View File

@@ -1,11 +1,22 @@
namespace Ryujinx.Ava.Utilities.PlayReport
using System;
namespace Ryujinx.Ava.Utilities.PlayReport
{
public static partial class PlayReports
{
public static Analyzer Analyzer { get; } = new Analyzer()
public static void Initialize()
{
// init lazy value
_ = Analyzer;
}
public static Analyzer Analyzer => _analyzerLazy.Value;
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
.AddSpec(
"01007ef00011e000",
spec => spec
.WithDescription("based on being in Master Mode.")
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
// reset to normal status when switching between normal & master mode in title screen
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
@@ -13,34 +24,49 @@
.AddSpec(
"0100f2c0115b6000",
spec => spec
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
.AddSpec(
"01002da013484000",
spec => spec
.WithDescription("based on how many Rupees you have.")
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
.AddSpec(
"0100000000010000",
spec =>
spec.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
spec => spec
.WithDescription("based on if you're playing with Assist Mode.")
.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
)
.AddSpec(
"010075000ecbe000",
spec =>
spec.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
spec => spec
.WithDescription("based on if you're playing with Assist Mode.")
.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
)
.AddSpec(
"010028600ebda000",
spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
spec => spec
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
)
.AddSpec( // Global & China IDs
["0100152000022000", "010075100e8ec000"],
spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
spec => spec
.WithDescription(
"based on what modes you're selecting in the menu & whether or not you're in a race.")
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
)
.AddSpec(
["0100a3d008c5c000", "01008f6008c5e000"],
spec => spec
.WithDescription("based on what area of Paldea you're exploring.")
.AddValueFormatter("area_no", PokemonSVArea)
.AddValueFormatter("team_circle", PokemonSVUnionCircle)
)
.AddSpec(
"01006a800016e000",
spec => spec
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
.AddSparseMultiValueFormatter(
[
// Metadata to figure out what PlayReport we have.
@@ -58,10 +84,15 @@
)
.AddSpec(
[
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
"010012f017576000", "0100c62011050000", "0100b3c014bda000"],
spec => spec.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
);
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
"010012f017576000", "0100c62011050000", "0100b3c014bda000"
],
spec => spec
.WithDescription(
"based on what game you first launch.\n\nNSO emulators do not print any Play Report information past the first game launch so it's all we got.")
.AddValueFormatter("launch_title_id", NsoEmulator_LaunchedGame)
)
);
private static string Playing(string game) => $"Playing {game}";
}

View File

@@ -23,6 +23,20 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public required string[] TitleIds { get; init; }
public const string DefaultDescription = "Formats the details on your Discord presence based on logged data from the game.";
private string _valueDescription;
public string Description => _valueDescription ?? DefaultDescription;
public GameSpec WithDescription(string description)
{
_valueDescription = description != null
? $"Formats the details on your Discord presence {description}"
: null;
return this;
}
public List<FormatterSpecBase> ValueFormatters { get; } = [];
@@ -197,7 +211,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport
public string[] ReportKeys { get; init; }
public Delegate Formatter { get; init; }
public bool Format(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
public bool TryFormat(ApplicationMetadata appMeta, Horizon.Prepo.Types.PlayReport playReport,
out FormattedValue formattedValue)
{
formattedValue = default;