diff --git a/docs/compatibility.csv b/docs/compatibility.csv index 6cb10e8d8..7653ced9e 100644 --- a/docs/compatibility.csv +++ b/docs/compatibility.csv @@ -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 diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index bc75e7d3f..5871b5308 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -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); diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs index ccf8ad964..1d79ed0b5 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs @@ -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); diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index dda498c59..3623a0073 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -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": { @@ -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": "", @@ -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": "", @@ -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": "", @@ -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": "Гуч.", @@ -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": "", @@ -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": "", @@ -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": "", @@ -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 @@ } } ] -} +} \ No newline at end of file diff --git a/src/Ryujinx/DiscordIntegrationModule.cs b/src/Ryujinx/DiscordIntegrationModule.cs index 47fc8ad69..075dc65da 100644 --- a/src/Ryujinx/DiscordIntegrationModule.cs +++ b/src/Ryujinx/DiscordIntegrationModule.cs @@ -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 evnt) diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs index a57863d5d..6658ba3ad 100644 --- a/src/Ryujinx/Headless/Options.cs +++ b/src/Ryujinx/Headless/Options.cs @@ -148,7 +148,7 @@ namespace Ryujinx.Headless IgnoreMissingServices = configurationState.System.IgnoreMissingServices; if (NeedsOverride(nameof(IgnoreControllerApplet))) - IgnoreControllerApplet = configurationState.System.IgnoreApplet; + IgnoreControllerApplet = configurationState.System.IgnoreControllerApplet; return; diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index c03c4c45f..c9bd08fa3 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -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 () => diff --git a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml b/src/Ryujinx/UI/Controls/ApplicationDataView.axaml index aee8f7b36..92e4d1ac3 100644 --- a/src/Ryujinx/UI/Controls/ApplicationDataView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationDataView.axaml @@ -119,17 +119,23 @@ TextWrapping="Wrap" > - - + + + TextWrapping="Wrap"> - + 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); diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index bbfe80570..7d7674c4e 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -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(); diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 1b1f8a2a5..fe71a7420 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -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 _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() diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml index cacb4b130..1da91c388 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml @@ -66,6 +66,10 @@ Command="{Binding OpenRyujinxFolder}" Header="{ext:Locale MenuBarFileOpenEmuFolder}" ToolTip.Tip="{ext:Locale OpenRyujinxFolderTooltip}" /> + - + ToolTip.Tip="{ext:Locale IgnoreControllerAppletTooltip}"> + (GameDirsList.SelectedItems.Cast())) { 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(AutoloadDirsList.SelectedItems.Cast())) { ViewModel.AutoloadDirectories.Remove(path); - ViewModel.AutoloadDirectoryChanged = true; + ViewModel.GameListNeedsRefresh = true; } if (AutoloadDirsList.ItemCount > 0) diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs index 36f451fe4..701c3ddaf 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml.cs @@ -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(); } diff --git a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs b/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs index 747ead8ca..2658352d7 100644 --- a/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs +++ b/src/Ryujinx/Utilities/AppLibrary/ApplicationData.cs @@ -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); } } + public Optional 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; } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 7d9a4accb..814a48e53 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -183,7 +183,7 @@ namespace Ryujinx.Ava.Utilities.Configuration public bool ShowConfirmExit { get; set; } /// - /// ignore "Applet" dialog + /// Ignore Controller Applet dialog /// public bool IgnoreApplet { get; set; } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 6bca36340..e735c287b 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -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; diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index 51c40cc99..3f22c6c0f 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -383,7 +383,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// Ignore Controller Applet /// - public ReactiveObject IgnoreApplet { get; private set; } + public ReactiveObject IgnoreControllerApplet { get; private set; } /// /// Uses Hypervisor over JIT if available @@ -424,8 +424,8 @@ namespace Ryujinx.Ava.Utilities.Configuration DramSize.LogChangesToValue(nameof(DramSize)); IgnoreMissingServices = new ReactiveObject(); IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices)); - IgnoreApplet = new ReactiveObject(); - IgnoreApplet.LogChangesToValue(nameof(IgnoreApplet)); + IgnoreControllerApplet = new ReactiveObject(); + IgnoreControllerApplet.LogChangesToValue(nameof(IgnoreControllerApplet)); AudioVolume = new ReactiveObject(); AudioVolume.LogChangesToValue(nameof(AudioVolume)); UseHypervisor = new ReactiveObject(); diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 774b9217e..02a91e51d 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -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; diff --git a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs index 51047cc32..8faf4fb31 100644 --- a/src/Ryujinx/Utilities/PlayReport/Analyzer.cs +++ b/src/Ryujinx/Utilities/PlayReport/Analyzer.cs @@ -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 Specs => new ReadOnlyCollection(_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; + /// /// Add an analysis spec matching a specific game by title ID, with the provided spec configuration. /// @@ -27,10 +33,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// The current , for chaining convenience. public Analyzer AddSpec(string titleId, Func 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; } /// @@ -41,10 +49,12 @@ namespace Ryujinx.Ava.Utilities.PlayReport /// The current , for chaining convenience. public Analyzer AddSpec(string titleId, Action 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; } /// @@ -57,10 +67,19 @@ namespace Ryujinx.Ava.Utilities.PlayReport Func 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; } /// @@ -72,12 +91,21 @@ namespace Ryujinx.Ava.Utilities.PlayReport public Analyzer AddSpec(IEnumerable titleIds, Action 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; } - + /// /// Add an analysis spec matching a specific game by title ID, with the provided pre-configured spec. /// @@ -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; diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs index cabace6bb..43e830819 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs @@ -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"; diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index d5568962e..9feb888b3 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -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 _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}"; } diff --git a/src/Ryujinx/Utilities/PlayReport/Specs.cs b/src/Ryujinx/Utilities/PlayReport/Specs.cs index f1b94de68..c162d4c2c 100644 --- a/src/Ryujinx/Utilities/PlayReport/Specs.cs +++ b/src/Ryujinx/Utilities/PlayReport/Specs.cs @@ -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 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;