Compare commits

..

11 Commits

Author SHA1 Message Date
Evan Husted
a46aacf2e2 gpu: Switch the 500ms timeout back to 1s
It seemed like it was waiting for 1 second no matter what; might as well have the log & syncpoint map match reality.
2025-02-01 19:21:19 -06:00
Evan Husted
ad9d6588e8 misc: chore: Collapse HLE swkbd character validation utils into a single class 2025-02-01 14:11:35 -06:00
Evan Husted
38ef65aae0 misc: chore: Move all GeneratedRegex methods into one static class with static instance accessors. 2025-02-01 14:07:32 -06:00
Evan Husted
9f94aa1c79 misc: chore: gpu: Lower default Syncpoint wait timeout from 1 second to 500ms 2025-02-01 03:30:13 -06:00
Evan Husted
2c9a26c11c misc: chore: Regular Architecture bool properties in RunningPlatform without OS constraint 2025-02-01 03:29:24 -06:00
Evan Husted
a4a15a4c80 misc: chore: simplify graphics backend selection logic in RendererHost constructor 2025-02-01 03:28:49 -06:00
Evan Husted
cc3b95eee1 misc: chore: More descriptive error for trying to create a Metal EmbeddedWindow on non-ARM Mac 2025-02-01 03:28:26 -06:00
Evan Husted
2ab806f759 UI: [ci skip] Fix ContentDialog symbols being backwards for right-to-left languages 2025-02-01 01:42:12 -06:00
Evan Husted
6d75410bd2 UI: Use the dynamic Ryujinx/Ryujinx Canary for dialog titles 2025-01-30 21:57:03 -06:00
Evan Husted
196b2eaf66 misc: chore: [ci skip] Remove needless fs integrity checks get in aoc extractor 2025-01-30 20:54:08 -06:00
Evan Husted
82fe519766 misc: chore: [ci skip] fix log on AOC extraction failure 2025-01-30 20:52:12 -06:00
21 changed files with 368 additions and 298 deletions

View File

@@ -0,0 +1,118 @@
using System.Text.RegularExpressions;
namespace Ryujinx.Common.Helper
{
public static partial class Patterns
{
#region Accessors
public static readonly Regex Numeric = NumericRegex();
public static readonly Regex AmdGcn = AmdGcnRegex();
public static readonly Regex NvidiaConsumerClass = NvidiaConsumerClassRegex();
public static readonly Regex DomainLp1Ns = DomainLp1NsRegex();
public static readonly Regex DomainLp1Lp1Npln = DomainLp1Lp1NplnRegex();
public static readonly Regex DomainLp1Znc = DomainLp1ZncRegex();
public static readonly Regex DomainSbApi = DomainSbApiRegex();
public static readonly Regex DomainSbAccounts = DomainSbAccountsRegex();
public static readonly Regex DomainAccounts = DomainAccountsRegex();
public static readonly Regex Module = ModuleRegex();
public static readonly Regex FsSdk = FsSdkRegex();
public static readonly Regex SdkMw = SdkMwRegex();
// ReSharper disable once InconsistentNaming
public static readonly Regex CJK = CJKRegex();
public static readonly Regex LdnPassphrase = LdnPassphraseRegex();
public static readonly Regex CleanText = CleanTextRegex();
#endregion
#region Generated pattern stubs
#region Numeric validation
[GeneratedRegex("[0-9]|.")]
internal static partial Regex NumericRegex();
#endregion
#region GPU names
[GeneratedRegex(
"Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
internal static partial Regex AmdGcnRegex();
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
internal static partial Regex NvidiaConsumerClassRegex();
#endregion
#region DNS blocking
public static readonly Regex[] BlockedHosts =
[
DomainLp1Ns,
DomainLp1Lp1Npln,
DomainLp1Znc,
DomainSbApi,
DomainSbAccounts,
DomainAccounts
];
const RegexOptions DnsRegexOpts =
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1NsRegex();
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1Lp1NplnRegex();
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", DnsRegexOpts)]
internal static partial Regex DomainLp1ZncRegex();
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainSbApiRegex();
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainSbAccountsRegex();
[GeneratedRegex(@"^accounts\.nintendo\.com$", DnsRegexOpts)]
internal static partial Regex DomainAccountsRegex();
#endregion
#region Executable information
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
internal static partial Regex ModuleRegex();
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
internal static partial Regex FsSdkRegex();
[GeneratedRegex(@"SDK MW[ -~]*")]
internal static partial Regex SdkMwRegex();
#endregion
#region CJK
[GeneratedRegex(
"\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
private static partial Regex CJKRegex();
#endregion
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
private static partial Regex LdnPassphraseRegex();
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
private static partial Regex CleanTextRegex();
#endregion
}
}

View File

@@ -10,14 +10,18 @@ namespace Ryujinx.Common.Helper
public static bool IsMacOS => OperatingSystem.IsMacOS();
public static bool IsWindows => OperatingSystem.IsWindows();
public static bool IsLinux => OperatingSystem.IsLinux();
public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsIntelMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.X64;
public static bool IsArmMac => IsMacOS && RuntimeInformation.OSArchitecture is Architecture.Arm64;
public static bool IsIntelMac => IsMacOS && IsX64;
public static bool IsArmMac => IsMacOS && IsArm;
public static bool IsX64Windows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmWindows => IsWindows && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
public static bool IsX64Windows => IsWindows && IsX64;
public static bool IsArmWindows => IsWindows && IsArm;
public static bool IsX64Linux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.X64);
public static bool IsArmLinux => IsLinux && (RuntimeInformation.OSArchitecture is Architecture.Arm64);
public static bool IsX64Linux => IsLinux && IsX64;
public static bool IsArmLinux => IsLinux && IsArmMac;
}
}

View File

@@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
}
using ManualResetEvent waitEvent = new(false);
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, (x) => waitEvent.Set());
SyncpointWaiterHandle info = _syncpoints[id].RegisterCallback(threshold, _ => waitEvent.Set());
if (info == null)
{
@@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
bool signaled = waitEvent.WaitOne(timeout);
if (!signaled && info != null)
if (!signaled)
{
Logger.Error?.Print(LogClass.Gpu, $"Wait on syncpoint {id} for threshold {threshold} took more than {timeout.TotalMilliseconds}ms, resuming execution...");

View File

@@ -16,14 +16,8 @@ namespace Ryujinx.Graphics.Vulkan
Unknown,
}
static partial class VendorUtils
static class VendorUtils
{
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
public static partial Regex AmdGcnRegex();
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
public static partial Regex NvidiaConsumerClassRegex();
public static Vendor FromId(uint id)
{
return id switch

View File

@@ -1,5 +1,6 @@
using Gommon;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
@@ -375,11 +376,11 @@ namespace Ryujinx.Graphics.Vulkan
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && Patterns.AmdGcn.IsMatch(GpuRenderer);
if (Vendor == Vendor.Nvidia)
{
Match match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
Match match = Patterns.NvidiaConsumerClass.Match(GpuRenderer);
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
{

View File

@@ -5,6 +5,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.SystemState;
@@ -30,9 +31,6 @@ namespace Ryujinx.HLE.HOS.Applets.Error
public event EventHandler AppletStateChanged;
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
private static partial Regex CleanTextRegex();
public ErrorApplet(Horizon horizon)
{
_horizon = horizon;
@@ -107,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
private static string CleanText(string value)
{
return CleanTextRegex().Replace(value, string.Empty).Replace("\0", string.Empty);
return Patterns.CleanText.Replace(value, string.Empty).Replace("\0", string.Empty);
}
private string GetMessageText(uint module, uint description, string key)

View File

@@ -1,17 +0,0 @@
using System.Text.RegularExpressions;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
public static partial class CJKCharacterValidation
{
public static bool IsCJK(char value)
{
Regex regex = CJKRegex();
return regex.IsMatch(value.ToString());
}
[GeneratedRegex("\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
private static partial Regex CJKRegex();
}
}

View File

@@ -0,0 +1,10 @@
using Ryujinx.Common.Helper;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
public static class CharacterValidation
{
public static bool IsNumeric(char value) => Patterns.Numeric.IsMatch(value.ToString());
public static bool IsCJK(char value) => Patterns.CJK.IsMatch(value.ToString());
}
}

View File

@@ -1,17 +0,0 @@
using System.Text.RegularExpressions;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
public static partial class NumericCharacterValidation
{
public static bool IsNumeric(char value)
{
Regex regex = NumericRegex();
return regex.IsMatch(value.ToString());
}
[GeneratedRegex("[0-9]|.")]
private static partial Regex NumericRegex();
}
}

View File

@@ -1,37 +1,13 @@
using Ryujinx.Common.Helper;
using System.Text.RegularExpressions;
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
{
static partial class DnsBlacklist
static class DnsBlacklist
{
const RegexOptions RegexOpts = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", RegexOpts)]
private static partial Regex BlockedHost1();
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", RegexOpts)]
private static partial Regex BlockedHost2();
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", RegexOpts)]
private static partial Regex BlockedHost3();
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", RegexOpts)]
private static partial Regex BlockedHost4();
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", RegexOpts)]
private static partial Regex BlockedHost5();
[GeneratedRegex(@"^accounts\.nintendo\.com$", RegexOpts)]
private static partial Regex BlockedHost6();
private static readonly Regex[] _blockedHosts =
[
BlockedHost1(),
BlockedHost2(),
BlockedHost3(),
BlockedHost4(),
BlockedHost5(),
BlockedHost6()
];
public static bool IsHostBlocked(string host)
{
foreach (Regex regex in _blockedHosts)
foreach (Regex regex in Patterns.BlockedHosts)
{
if (regex.IsMatch(host))
{

View File

@@ -2,6 +2,7 @@ using LibHac.Common.FixedArrays;
using LibHac.Fs;
using LibHac.Loader;
using LibHac.Tools.FsSystem;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using System;
using System.Text;
@@ -29,13 +30,6 @@ namespace Ryujinx.HLE.Loaders.Executables
public string Name;
public Array32<byte> BuildId;
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
private static partial Regex ModuleRegex();
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
private static partial Regex FsSdkRegex();
[GeneratedRegex(@"SDK MW[ -~]*")]
private static partial Regex SdkMwRegex();
public NsoExecutable(IStorage inStorage, string name = null)
{
NsoReader reader = new();
@@ -90,7 +84,7 @@ namespace Ryujinx.HLE.Loaders.Executables
if (string.IsNullOrEmpty(modulePath))
{
Match moduleMatch = ModuleRegex().Match(rawTextBuffer);
Match moduleMatch = Patterns.Module.Match(rawTextBuffer);
if (moduleMatch.Success)
{
modulePath = moduleMatch.Value;
@@ -99,13 +93,13 @@ namespace Ryujinx.HLE.Loaders.Executables
stringBuilder.AppendLine($" Module: {modulePath}");
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
Match fsSdkMatch = Patterns.FsSdk.Match(rawTextBuffer);
if (fsSdkMatch.Success)
{
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", string.Empty)}");
}
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
MatchCollection sdkMwMatches = Patterns.SdkMw.Matches(rawTextBuffer);
if (sdkMwMatches.Count != 0)
{
string libHeader = " SDK Libraries: ";

View File

@@ -11475,126 +11475,126 @@
{
"ID": "DialogConfirmationTitle",
"Translations": {
"ar_SA": "ريوجينكس - تأكيد",
"de_DE": "Ryujinx - Bestätigung",
"el_GR": "Ryujinx - Επιβεβαίωση",
"en_US": "Ryujinx - Confirmation",
"es_ES": "Ryujinx - Confirmación",
"ar_SA": "{0} - تأكيد",
"de_DE": "{0} - Bestätigung",
"el_GR": "{0} - Επιβεβαίωση",
"en_US": "{0} - Confirmation",
"es_ES": "{0} - Confirmación",
"fr_FR": "",
"he_IL": "ריוג'ינקס - אישור",
"it_IT": "Ryujinx - Conferma",
"ja_JP": "Ryujinx - 確認",
"ko_KR": "Ryujinx - 확인",
"no_NO": "Ryujinx - Bekreftelse",
"pl_PL": "Ryujinx - Potwierdzenie",
"pt_BR": "Ryujinx - Confirmação",
"ru_RU": "Ryujinx - Подтверждение",
"sv_SE": "Ryujinx - Bekräftelse",
"th_TH": "Ryujinx - ยืนยัน",
"tr_TR": "Ryujinx - Onay",
"uk_UA": "Ryujinx - Підтвердження",
"zh_CN": "Ryujinx - 确认",
"zh_TW": "Ryujinx - 確認"
"he_IL": "{0} - אישור",
"it_IT": "{0} - Conferma",
"ja_JP": "{0} - 確認",
"ko_KR": "{0} - 확인",
"no_NO": "{0} - Bekreftelse",
"pl_PL": "{0} - Potwierdzenie",
"pt_BR": "{0} - Confirmação",
"ru_RU": "{0} - Подтверждение",
"sv_SE": "{0} - Bekräftelse",
"th_TH": "{0} - ยืนยัน",
"tr_TR": "{0} - Onay",
"uk_UA": "{0} - Підтвердження",
"zh_CN": "{0} - 确认",
"zh_TW": "{0} - 確認"
}
},
{
"ID": "DialogUpdaterTitle",
"Translations": {
"ar_SA": "ريوجينكس - المحدث",
"ar_SA": "{0} - المحدث",
"de_DE": "",
"el_GR": "Ryujinx - Ενημερωτής",
"en_US": "Ryujinx - Updater",
"es_ES": "Ryujinx - Actualizador",
"fr_FR": "Ryujinx - Mise à Jour",
"he_IL": "ריוג'ינקס - מעדכן",
"it_IT": "Ryujinx - Aggiornamento",
"ja_JP": "Ryujinx - アップデータ",
"ko_KR": "Ryujinx - 업데이터",
"no_NO": "Ryujinx Oppdaterer",
"pl_PL": "Ryujinx - Asystent aktualizacji",
"pt_BR": "Ryujinx - Atualizador",
"ru_RU": "Ryujinx - Обновление",
"sv_SE": "Ryujinx - Uppdatering",
"th_TH": "Ryujinx - อัพเดต",
"tr_TR": "Ryujinx - Güncelleyici",
"uk_UA": "Ryujinx - Програма оновлення",
"zh_CN": "Ryujinx - 更新",
"zh_TW": "Ryujinx - 更新程式"
"el_GR": "{0} - Ενημερωτής",
"en_US": "{0} - Updater",
"es_ES": "{0} - Actualizador",
"fr_FR": "{0} - Mise à Jour",
"he_IL": "{0} - מעדכן",
"it_IT": "{0} - Aggiornamento",
"ja_JP": "{0} - アップデータ",
"ko_KR": "{0} - 업데이터",
"no_NO": "{0} Oppdaterer",
"pl_PL": "{0} - Asystent aktualizacji",
"pt_BR": "{0} - Atualizador",
"ru_RU": "{0} - Обновление",
"sv_SE": "{0} - Uppdatering",
"th_TH": "{0} - อัพเดต",
"tr_TR": "{0} - Güncelleyici",
"uk_UA": "{0} - Програма оновлення",
"zh_CN": "{0} - 更新",
"zh_TW": "{0} - 更新程式"
}
},
{
"ID": "DialogErrorTitle",
"Translations": {
"ar_SA": "ريوجينكس - خطأ",
"de_DE": "Ryujinx - Fehler",
"el_GR": "Ryujinx - Σφάλμα",
"en_US": "Ryujinx - Error",
"ar_SA": "{0} - خطأ",
"de_DE": "{0} - Fehler",
"el_GR": "{0} - Σφάλμα",
"en_US": "{0} - Error",
"es_ES": "",
"fr_FR": "Ryujinx - Erreur",
"he_IL": "ריוג'ינקס - שגיאה",
"it_IT": "Ryujinx - Errore",
"ja_JP": "Ryujinx - エラー",
"ko_KR": "Ryujinx - 오류",
"no_NO": "Ryujinx - Feil",
"pl_PL": "Ryujinx - Błąd",
"pt_BR": "Ryujinx - Erro",
"ru_RU": "Ryujinx - Ошибка",
"sv_SE": "Ryujinx - Fel",
"th_TH": "Ryujinx - ผิดพลาด",
"tr_TR": "Ryujinx - Hata",
"uk_UA": "Ryujinx - Помилка",
"zh_CN": "Ryujinx - 错误",
"zh_TW": "Ryujinx - 錯誤"
"fr_FR": "{0} - Erreur",
"he_IL": "{0} - שגיאה",
"it_IT": "{0} - Errore",
"ja_JP": "{0} - エラー",
"ko_KR": "{0} - 오류",
"no_NO": "{0} - Feil",
"pl_PL": "{0} - Błąd",
"pt_BR": "{0} - Erro",
"ru_RU": "{0} - Ошибка",
"sv_SE": "{0} - Fel",
"th_TH": "{0} - ผิดพลาด",
"tr_TR": "{0} - Hata",
"uk_UA": "{0} - Помилка",
"zh_CN": "{0} - 错误",
"zh_TW": "{0} - 錯誤"
}
},
{
"ID": "DialogWarningTitle",
"Translations": {
"ar_SA": "ريوجينكس - تحذير",
"de_DE": "Ryujinx - Warnung",
"el_GR": "Ryujinx - Προειδοποίηση",
"en_US": "Ryujinx - Warning",
"es_ES": "Ryujinx - Advertencia",
"fr_FR": "Ryujinx - Avertissement",
"he_IL": "ריוג'ינקס - אזהרה",
"it_IT": "Ryujinx - Avviso",
"ja_JP": "Ryujinx - 警告",
"ko_KR": "Ryujinx - 경고",
"no_NO": "Ryujinx - Advarsel",
"pl_PL": "Ryujinx - Ostrzeżenie",
"pt_BR": "Ryujinx - Alerta",
"ru_RU": "Ryujinx - Предупреждение",
"sv_SE": "Ryujinx - Varning",
"th_TH": "Ryujinx - คำเตือน",
"tr_TR": "Ryujinx - Uyarı",
"uk_UA": "Ryujinx - Попередження",
"zh_CN": "Ryujinx - 警告",
"zh_TW": "Ryujinx - 警告"
"ar_SA": "{0} - تحذير",
"de_DE": "{0} - Warnung",
"el_GR": "{0} - Προειδοποίηση",
"en_US": "{0} - Warning",
"es_ES": "{0} - Advertencia",
"fr_FR": "{0} - Avertissement",
"he_IL": "{0} - אזהרה",
"it_IT": "{0} - Avviso",
"ja_JP": "{0} - 警告",
"ko_KR": "{0} - 경고",
"no_NO": "{0} - Advarsel",
"pl_PL": "{0} - Ostrzeżenie",
"pt_BR": "{0} - Alerta",
"ru_RU": "{0} - Предупреждение",
"sv_SE": "{0} - Varning",
"th_TH": "{0} - คำเตือน",
"tr_TR": "{0} - Uyarı",
"uk_UA": "{0} - Попередження",
"zh_CN": "{0} - 警告",
"zh_TW": "{0} - 警告"
}
},
{
"ID": "DialogExitTitle",
"Translations": {
"ar_SA": "ريوجينكس - الخروج",
"de_DE": "Ryujinx - Beenden",
"el_GR": "Ryujinx - Έξοδος",
"en_US": "Ryujinx - Exit",
"es_ES": "Ryujinx - Salir",
"fr_FR": "Ryujinx - Quitter",
"he_IL": "ריוג'ינקס - יציאה",
"it_IT": "Ryujinx - Esci",
"ja_JP": "Ryujinx - 終了",
"ko_KR": "Ryujinx - 종료",
"no_NO": "Ryujinx - Avslutt",
"pl_PL": "Ryujinx - Wyjdź",
"pt_BR": "Ryujinx - Sair",
"ru_RU": "Ryujinx - Выход",
"sv_SE": "Ryujinx - Avslut",
"th_TH": "Ryujinx - ออก",
"tr_TR": "Ryujinx - Çıkış",
"uk_UA": "Ryujinx - Вихід",
"zh_CN": "Ryujinx - 退出",
"zh_TW": "Ryujinx - 結束"
"ar_SA": "{0} - الخروج",
"de_DE": "{0} - Beenden",
"el_GR": "{0} - Έξοδος",
"en_US": "{0} - Exit",
"es_ES": "{0} - Salir",
"fr_FR": "{0} - Quitter",
"he_IL": "{0} - יציאה",
"it_IT": "{0} - Esci",
"ja_JP": "{0} - 終了",
"ko_KR": "{0} - 종료",
"no_NO": "{0} - Avslutt",
"pl_PL": "{0} - Wyjdź",
"pt_BR": "{0} - Sair",
"ru_RU": "{0} - Выход",
"sv_SE": "{0} - Avslut",
"th_TH": "{0} - ออก",
"tr_TR": "{0} - Çıkış",
"uk_UA": "{0} - Вихід",
"zh_CN": "{0} - 退出",
"zh_TW": "{0} - 結束"
}
},
{
@@ -17025,26 +17025,26 @@
{
"ID": "DialogStopEmulationTitle",
"Translations": {
"ar_SA": "ريوجينكس - إيقاف المحاكاة",
"de_DE": "Ryujinx - Beende Emulation",
"el_GR": "Ryujinx - Διακοπή εξομοίωσης",
"en_US": "Ryujinx - Stop Emulation",
"es_ES": "Ryujinx - Detener emulación",
"fr_FR": "Ryujinx - Arrêt de l'émulation",
"he_IL": "ריוג'ינקס - עצור אמולציה",
"it_IT": "Ryujinx - Ferma emulazione",
"ja_JP": "Ryujinx - エミュレーションを中止",
"ko_KR": "Ryujinx - 에뮬레이션 중지",
"no_NO": "Ryujinx - Stopp emulasjon",
"pl_PL": "Ryujinx - Zatrzymaj Emulację",
"pt_BR": "Ryujinx - Parar emulação",
"ru_RU": "Ryujinx - Остановка эмуляции",
"sv_SE": "Ryujinx - Stoppa emulering",
"th_TH": "Ryujinx - หยุดการจำลอง",
"tr_TR": "Ryujinx - Emülasyonu Durdur",
"uk_UA": "Ryujinx - Зупинити емуляцію",
"zh_CN": "Ryujinx - 停止模拟",
"zh_TW": "Ryujinx - 停止模擬"
"ar_SA": "{0} - إيقاف المحاكاة",
"de_DE": "{0} - Beende Emulation",
"el_GR": "{0} - Διακοπή εξομοίωσης",
"en_US": "{0} - Stop Emulation",
"es_ES": "{0} - Detener emulación",
"fr_FR": "{0} - Arrêt de l'émulation",
"he_IL": "{0} - עצור אמולציה",
"it_IT": "{0} - Ferma emulazione",
"ja_JP": "{0} - エミュレーションを中止",
"ko_KR": "{0} - 에뮬레이션 중지",
"no_NO": "{0} - Stopp emulasjon",
"pl_PL": "{0} - Zatrzymaj Emulację",
"pt_BR": "{0} - Parar emulação",
"ru_RU": "{0} - Остановка эмуляции",
"sv_SE": "{0} - Stoppa emulering",
"th_TH": "{0} - หยุดการจำลอง",
"tr_TR": "{0} - Emülasyonu Durdur",
"uk_UA": "{0} - Зупинити емуляцію",
"zh_CN": "{0} - 停止模拟",
"zh_TW": "{0} - 停止模擬"
}
},
{
@@ -17950,51 +17950,51 @@
{
"ID": "RyujinxInfo",
"Translations": {
"ar_SA": "ريوجينكس - معلومات",
"ar_SA": "{0} - معلومات",
"de_DE": "",
"el_GR": "Ryujinx - Πληροφορίες",
"en_US": "Ryujinx - Info",
"el_GR": "{0} - Πληροφορίες",
"en_US": "{0} - Info",
"es_ES": "",
"fr_FR": "",
"he_IL": "ריוג'ינקס - מידע",
"it_IT": "Ryujinx - Informazioni",
"ja_JP": "Ryujinx - 情報",
"ko_KR": "Ryujinx - 정보",
"no_NO": "Ryujinx - Informasjon",
"he_IL": "{0} - מידע",
"it_IT": "{0} - Informazioni",
"ja_JP": "{0} - 情報",
"ko_KR": "{0} - 정보",
"no_NO": "{0} - Informasjon",
"pl_PL": "",
"pt_BR": "Ryujinx - Informação",
"ru_RU": "Ryujinx - Информация",
"pt_BR": "{0} - Informação",
"ru_RU": "{0} - Информация",
"sv_SE": "",
"th_TH": "Ryujinx ข้อมูล",
"tr_TR": "Ryujinx - Bilgi",
"uk_UA": "Ryujin x - Інформація",
"zh_CN": "Ryujinx - 信息",
"zh_TW": "Ryujinx - 資訊"
"th_TH": "{0} ข้อมูล",
"tr_TR": "{0} - Bilgi",
"uk_UA": "{0} - Інформація",
"zh_CN": "{0} - 信息",
"zh_TW": "{0} - 資訊"
}
},
{
"ID": "RyujinxConfirm",
"Translations": {
"ar_SA": "ريوجينكس - تأكيد",
"de_DE": "Ryujinx - Bestätigung",
"el_GR": "Ryujinx - Επιβεβαίωση",
"en_US": "Ryujinx - Confirmation",
"es_ES": "Ryujinx - Confirmación",
"ar_SA": "{0} - تأكيد",
"de_DE": "{0} - Bestätigung",
"el_GR": "{0} - Επιβεβαίωση",
"en_US": "{0} - Confirmation",
"es_ES": "{0} - Confirmación",
"fr_FR": "",
"he_IL": "ריוג'ינקס - אישור",
"it_IT": "Ryujinx - Conferma",
"ja_JP": "Ryujinx - 確認",
"ko_KR": "Ryujinx - 확인",
"no_NO": "Ryujinx - Bekreftelse",
"pl_PL": "Ryujinx - Potwierdzenie",
"pt_BR": "Ryujinx - Confirmação",
"ru_RU": "Ryujinx - Подтверждение",
"sv_SE": "Ryujinx - Bekräfta",
"th_TH": "Ryujinx - ยืนยัน",
"tr_TR": "Ryujinx - Doğrulama",
"uk_UA": "Ryujinx - Підтвердження",
"zh_CN": "Ryujinx - 确认",
"zh_TW": "Ryujinx - 確認"
"he_IL": "{0} - אישור",
"it_IT": "{0} - Conferma",
"ja_JP": "{0} - 確認",
"ko_KR": "{0} - 확인",
"no_NO": "{0} - Bekreftelse",
"pl_PL": "{0} - Potwierdzenie",
"pt_BR": "{0} - Confirmação",
"ru_RU": "{0} - Подтверждение",
"sv_SE": "{0} - Bekräfta",
"th_TH": "{0} - ยืนยัน",
"tr_TR": "{0} - Doğrulama",
"uk_UA": "{0} - Підтвердження",
"zh_CN": "{0} - 确认",
"zh_TW": "{0} - 確認"
}
},
{
@@ -18800,26 +18800,26 @@
{
"ID": "RyujinxUpdater",
"Translations": {
"ar_SA": "محدث ريوجينكس",
"de_DE": "Ryujinx - Updater",
"el_GR": "Ryujinx Ενημερωτής",
"en_US": "Ryujinx Updater",
"es_ES": "Actualizador de Ryujinx",
"fr_FR": "Mise à jour de Ryujinx",
"he_IL": "מעדכן ריוג'ינקס",
"it_IT": "Aggiornamento di Ryujinx",
"ja_JP": "Ryujinx アップデータ",
"ko_KR": "Ryujinx 업데이터",
"no_NO": "Ryujinx Oppgradering",
"pl_PL": "Aktualizator Ryujinx",
"pt_BR": "Atualizador do Ryujinx",
"ru_RU": "Ryujinx - Обновление",
"sv_SE": "Uppdaterare för Ryujinx",
"th_TH": "ตัวอัปเดต Ryujinx",
"tr_TR": "Ryujinx Güncelleyicisi",
"uk_UA": "Програма оновлення Ryujinx",
"zh_CN": "Ryujinx 更新",
"zh_TW": "Ryujinx 更新程式"
"ar_SA": "محدث {0}",
"de_DE": "",
"el_GR": "{0} Ενημερωτής",
"en_US": "{0} Updater",
"es_ES": "Actualizador de {0}",
"fr_FR": "Mise à jour de {0}",
"he_IL": "מעדכן {0}",
"it_IT": "Aggiornamento di {0}",
"ja_JP": "{0} アップデータ",
"ko_KR": "{0} 업데이터",
"no_NO": "{0} Oppgradering",
"pl_PL": "Aktualizator {0}",
"pt_BR": "Atualizador do {0}",
"ru_RU": "{0} Обновление",
"sv_SE": "Uppdaterare för {0}",
"th_TH": "ตัวอัปเดต {0}",
"tr_TR": "{0} Güncelleyicisi",
"uk_UA": "Програма оновлення {0}",
"zh_CN": "{0} 更新",
"zh_TW": "{0} 更新程式"
}
},
{

View File

@@ -337,7 +337,7 @@ namespace Ryujinx.Ava.Common
if (publicDataNca is null)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The NCA was not present in the selected file");
Logger.Error?.Print(LogClass.Application, "Extraction failure. The PublicData NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () =>
{
@@ -349,10 +349,6 @@ namespace Ryujinx.Ava.Common
return;
}
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
int index = Nca.GetSectionIndexFromType(NcaSectionType.Data, publicDataNca.Header.ContentType);
try

View File

@@ -44,6 +44,16 @@ namespace Ryujinx.Ava.Common.Locale
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
SetDynamicValues(LocaleKeys.DialogConfirmationTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogUpdaterTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogErrorTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogWarningTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogExitTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.DialogStopEmulationTitle, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxInfo, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxConfirm, RyujinxApp.FullAppName);
SetDynamicValues(LocaleKeys.RyujinxUpdater, RyujinxApp.FullAppName);
}
public string this[LocaleKeys key]
@@ -88,11 +98,16 @@ namespace Ryujinx.Ava.Common.Locale
public static string FormatDynamicValue(LocaleKeys key, params object[] values)
=> Instance.UpdateAndGetDynamicValue(key, values);
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
public void SetDynamicValues(LocaleKeys key, params object[] values)
{
_dynamicValues[key] = values;
OnPropertyChanged("Translation");
}
public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
{
SetDynamicValues(key, values);
return this[key];
}

View File

@@ -22,12 +22,12 @@ namespace Ryujinx.Ava
{
public class RyujinxApp : Application
{
internal static string FormatTitle(LocaleKeys? windowTitleKey = null)
internal static string FormatTitle(LocaleKeys? windowTitleKey = null, bool includeVersion = true)
=> windowTitleKey is null
? $"{FullAppName} {Program.Version}"
: $"{FullAppName} {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}";
? $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)}"
: $"{FullAppName}{(includeVersion ? $" {Program.Version}" : string.Empty)} - {LocaleManager.Instance[windowTitleKey.Value]}";
public static readonly string FullAppName = ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx";
public static readonly string FullAppName = string.Intern(ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx");
public static MainWindow MainWindow => Current!
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()

View File

@@ -144,12 +144,12 @@ namespace Ryujinx.Ava.UI.Controls
case KeyboardMode.Numeric:
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumeric);
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
_checkInput = text => text.All(NumericCharacterValidation.IsNumeric);
_checkInput = text => text.All(CharacterValidation.IsNumeric);
break;
case KeyboardMode.Alphabet:
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet);
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
_checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value));
_checkInput = text => text.All(value => !CharacterValidation.IsCJK(value));
break;
case KeyboardMode.ASCII:
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII);

View File

@@ -159,6 +159,7 @@ namespace Ryujinx.Ava.UI.Helpers
Symbol = (Symbol)symbol,
Margin = new Thickness(10),
FontSize = 40,
FlowDirection = FlowDirection.LeftToRight,
VerticalAlignment = VerticalAlignment.Center,
};

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Helper;
using SharpMetal.QuartzCore;
using System;
@@ -7,14 +8,12 @@ namespace Ryujinx.Ava.UI.Renderer
{
public CAMetalLayer CreateSurface()
{
if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS() && RunningPlatform.IsArm)
{
return new CAMetalLayer(MetalLayer);
}
else
{
throw new NotSupportedException();
}
throw new NotSupportedException($"Cannot create a {nameof(CAMetalLayer)} without being on ARM Mac.");
}
}
}

View File

@@ -43,19 +43,19 @@ namespace Ryujinx.Ava.UI.Renderer
public RendererHost(string titleId)
{
switch (TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend))
{
case GraphicsBackend.OpenGl:
EmbeddedWindow = new EmbeddedWindowOpenGL();
break;
case GraphicsBackend.Metal:
EmbeddedWindow = new EmbeddedWindowMetal();
break;
case GraphicsBackend.Vulkan:
EmbeddedWindow = new EmbeddedWindowVulkan();
break;
}
Focusable = true;
FlowDirection = FlowDirection.LeftToRight;
EmbeddedWindow =
#pragma warning disable CS8509
TitleIDs.SelectGraphicsBackend(titleId, ConfigurationState.Instance.Graphics.GraphicsBackend) switch
#pragma warning restore CS8509
{
GraphicsBackend.OpenGl => new EmbeddedWindowOpenGL(),
GraphicsBackend.Metal => new EmbeddedWindowMetal(),
GraphicsBackend.Vulkan => new EmbeddedWindowVulkan(),
};
string backendText = EmbeddedWindow switch
{
EmbeddedWindowVulkan => "Vulkan",

View File

@@ -16,6 +16,7 @@ using Ryujinx.Ava.Utilities.Configuration.System;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Vulkan;
@@ -330,9 +331,6 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
private static partial Regex LdnPassphraseRegex();
public bool IsInvalidLdnPassphraseVisible { get; set; }
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
@@ -470,7 +468,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool ValidateLdnPassphrase(string passphrase)
{
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && LdnPassphraseRegex().IsMatch(passphrase));
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && Patterns.LdnPassphrase.IsMatch(passphrase));
}
public void ValidateAndSetTimeZone(string location)

View File

@@ -302,7 +302,7 @@ namespace Ryujinx.Ava.UI.Windows
LinuxHelper.RecommendedVmMaxMapCount);
UserResult response = await ContentDialogHelper.ShowTextDialog(
$"Ryujinx - {LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTitle]}",
RyujinxApp.FormatTitle(LocaleKeys.LinuxVmMaxMapCountDialogTitle, false),
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary],
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextSecondary],
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonUntilRestart],