diff --git a/src/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs index 276dd5f8c..82eeaddc1 100644 --- a/src/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/src/Ryujinx.Common/Utilities/JsonHelper.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Text; using System.Text.Json; @@ -27,9 +28,14 @@ namespace Ryujinx.Common.Utilities ReadCommentHandling = JsonCommentHandling.Skip }; - public static string Serialize(T value, JsonTypeInfo typeInfo) => JsonSerializer.Serialize(value, typeInfo); + public static string Serialize(T value, JsonTypeInfo typeInfo) + => JsonSerializer.Serialize(value, typeInfo); - public static T Deserialize(string value, JsonTypeInfo typeInfo) => JsonSerializer.Deserialize(value, typeInfo); + public static T Deserialize(string value, JsonTypeInfo typeInfo) + => JsonSerializer.Deserialize(value, typeInfo); + + public static T Deserialize(ReadOnlySpan utf8Value, JsonTypeInfo typeInfo) + => JsonSerializer.Deserialize(utf8Value, typeInfo); public static void SerializeToFile(string filePath, T value, JsonTypeInfo typeInfo) { diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs index a49e0179d..f39b295cd 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs @@ -115,6 +115,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked } private readonly AddressIntrusiveRedBlackTree _mappingTree; + + // type is not Lock due to the unique usage of this mechanism, + // an arbitrary object is used as the lock passed in by constructor. private readonly object _lock; public Block(MemoryTracking tracking, Func readPtCallback, MemoryBlock memory, ulong size, object locker) : base(memory, size) @@ -174,6 +177,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked private readonly MemoryTracking _tracking; private readonly Func _readPtCallback; + + // type is not Lock due to the unique usage of this mechanism, + // an arbitrary object is used as the lock passed in by constructor. private readonly object _lock; public AddressSpacePartitionAllocator( diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs index 90231b460..ce2b7185a 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private readonly long[] _current2; private readonly long[] _peak; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private readonly LinkedList _waitingThreads; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs index bfa6b68f6..b3fe5f1b6 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs @@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading class KCriticalSection { private readonly KernelContext _context; - private readonly object _lock = new(); private int _recursionCount; - - public object Lock => _lock; + + // type is not Lock due to Monitor class usage + public object Lock { get; } = new(); public KCriticalSection(KernelContext context) { @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public void Enter() { - Monitor.Enter(_lock); + Monitor.Enter(Lock); _recursionCount++; } @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { ulong scheduledCoresMask = KScheduler.SelectThreads(_context); - Monitor.Exit(_lock); + Monitor.Exit(Lock); KThread currentThread = KernelStatic.GetCurrentThread(); bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable; @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - Monitor.Exit(_lock); + Monitor.Exit(Lock); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs index 65638b04e..03063cb53 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs @@ -333,7 +333,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin"); } - public static bool HasKeyRetailBinPath => File.Exists(GetKeyRetailBinPath()); + public static bool HasAmiiboKeyFile => File.Exists(GetKeyRetailBinPath()); public static DateTime DateTimeFromTag(ushort value) diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs index 7343a40ca..37d587dac 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption private byte[] DeriveKey(AmiiboMasterKey key, bool deriveAes, out byte[] derivedAesKey, out byte[] derivedAesIv) { - List seed = new List(); + List seed = []; // Start with the type string (14 bytes) seed.AddRange(key.TypeString); diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs index f61f8c84d..940dc4c85 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs @@ -33,10 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption byte[] dataBin = combinedBin.Take(80).ToArray(); byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray(); - AmiiboMasterKey dataKey = new AmiiboMasterKey(dataBin); - AmiiboMasterKey tagKey = new AmiiboMasterKey(tagBin); - - return (dataKey, tagKey); + return (new AmiiboMasterKey(dataBin), new AmiiboMasterKey(tagBin)); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index 5b9e6811d..05fc91d32 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -10,6 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl private ulong _value; private readonly EventFdFlags _flags; + // type is not Lock due to Monitor class usage private readonly object _lock = new(); public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); } diff --git a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs b/src/Ryujinx.UI.Common/Helper/OpenHelper.cs index 8b0e1f1fd..bf398a355 100644 --- a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/OpenHelper.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Logging; using System; using System.Diagnostics; @@ -34,6 +35,8 @@ namespace Ryujinx.UI.Common.Helper } } + public static void OpenFolder(FilePath path) => OpenFolder(path.Path); + public static void LocateFile(string path) { if (File.Exists(path)) diff --git a/src/Ryujinx/Assets/Styles/Themes.xaml b/src/Ryujinx/Assets/Styles/Themes.xaml index 46e298035..3a0bd4217 100644 --- a/src/Ryujinx/Assets/Styles/Themes.xaml +++ b/src/Ryujinx/Assets/Styles/Themes.xaml @@ -4,11 +4,10 @@ + #008AA8 #FF00FABB #FFF0F0F0 #FFd6d6d6 - #FFFFFFFF - #FFFFFFFF #FF000000 #C1C1C1 #b3ffffff @@ -22,16 +21,19 @@ + #3ddcff #FF00FABB - #FFF0F0F0 - #FFd6d6d6 - #FFFFFFFF - #FFFFFFFF + #dedede + #c2c2c2 #FF000000 #C1C1C1 #b3ffffff #80cccccc #A0000000 + #fffcd12a + #13c3a4 + #FFFF4554 + #6483F5 #FF00FABB #FF2D2D2D #FF505050 - #FFFFFFFF - #FFFFFFFF #FFFFFFFF #3D3D3D #0FFFFFFF #1EFFFFFF #A0FFFFFF + #fffcd12a + #FF2EEAC9 + #FFFF4554 + #6483F5 diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 1666fcd89..66e74dac2 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -2424,25 +2424,25 @@ { "ID": "StatusBarSystemVersion", "Translations": { - "ar_SA": "إصدار النظام: {0}", - "de_DE": "Systemversion: {0}", - "el_GR": "Έκδοση Συστήματος: {0}", - "en_US": "System Version: {0}", - "es_ES": "Versión del sistema: {0}", + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Firmware Version: {0}", + "es_ES": "", "fr_FR": "Version du Firmware: {0}", - "he_IL": "גרסת מערכת: {0}", - "it_IT": "Versione di sistema: {0}", - "ja_JP": "システムバージョン: {0}", - "ko_KR": "시스템 버전 : {0}", - "no_NO": "System versjon: {0}", - "pl_PL": "Wersja systemu: {0}", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", "pt_BR": "Versão do firmware: {0}", "ru_RU": "Версия прошивки: {0}", - "th_TH": "เวอร์ชั่นของระบบ: {0}", - "tr_TR": "Sistem Sürümü: {0}", - "uk_UA": "Версія системи: {0}", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", "zh_CN": "系统固件版本:{0}", - "zh_TW": "系統版本: {0}" + "zh_TW": "" } }, { @@ -3765,6 +3765,30 @@ "zh_TW": "系統時鐘:" } }, + { + "ID": "SettingsTabSystemSystemTimeMatch", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Match PC Time", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabSystemEnablePptc", "Translations": { @@ -14541,6 +14565,30 @@ "zh_TW": "變更系統時鐘" } }, + { + "ID": "MatchTimeTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Change System Time to match your PC's date & time.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "VSyncToggleTooltip", "Translations": { @@ -21550,4 +21598,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/Ryujinx/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs index 2ea5c83ee..f29efb15a 100644 --- a/src/Ryujinx/Common/LocaleManager.cs +++ b/src/Ryujinx/Common/LocaleManager.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Common; using Ryujinx.Common.Logging; @@ -7,12 +8,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Linq; -using System.Text.Encodings.Web; -using System.Text.Json; using System.Text.Json.Serialization; -using System.Text.Unicode; namespace Ryujinx.Ava.Common.Locale { @@ -147,39 +143,33 @@ namespace Ryujinx.Ava.Common.Locale LocaleChanged?.Invoke(); } + #nullable enable + + private static LocalesJson? _localeData; + + #nullable disable + private static Dictionary LoadJsonLanguage(string languageCode) { var localeStrings = new Dictionary(); - string fileData = EmbeddedResources.ReadAllText($"Ryujinx/Assets/locales.json"); - if (fileData == null) + _localeData ??= EmbeddedResources.ReadAllText("Ryujinx/Assets/locales.json") + .Into(it => JsonHelper.Deserialize(it, LocalesJsonContext.Default.LocalesJson)); + + foreach (LocalesEntry locale in _localeData.Value.Locales) { - // We were unable to find file for that language code. - return null; - } - - LocalesJson json = JsonHelper.Deserialize(fileData, LocalesJsonContext.Default.LocalesJson); - - foreach (LocalesEntry locale in json.Locales) - { - if (locale.Translations.Count != json.Languages.Count) + if (locale.Translations.Count != _localeData.Value.Languages.Count) { - Logger.Error?.Print(LogClass.UI, $"Locale key {{{locale.ID}}} is missing languages!"); - throw new Exception("Missing locale data!"); + throw new Exception($"Locale key {{{locale.ID}}} is missing languages! Has {locale.Translations.Count} translations, expected {_localeData.Value.Languages.Count}!"); } - if (Enum.TryParse(locale.ID, out var localeKey)) - { - if (locale.Translations.TryGetValue(languageCode, out string val) && val != "") - { - localeStrings[localeKey] = val; - } - else - { - locale.Translations.TryGetValue("en_US", out val); - localeStrings[localeKey] = val; - } - } + if (!Enum.TryParse(locale.ID, out var localeKey)) + continue; + + localeStrings[localeKey] = + locale.Translations.TryGetValue(languageCode, out string val) && val != string.Empty + ? val + : locale.Translations[DefaultLanguageCode]; } return localeStrings; @@ -200,5 +190,5 @@ namespace Ryujinx.Ava.Common.Locale [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(LocalesJson))] - internal partial class LocalesJsonContext : JsonSerializerContext { } + internal partial class LocalesJsonContext : JsonSerializerContext; } diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index f607b71f6..cebd65701 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -334,7 +334,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool CanScanAmiiboBinaries => AmiiboBinReader.HasKeyRetailBinPath; + public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile; public bool ShowLoadProgress { @@ -2015,7 +2015,7 @@ namespace Ryujinx.Ava.UI.ViewModels } else { - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "NaN"); } IsAppletMenuActive = hasApplet; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index a5abeb36b..0824e3f86 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -330,6 +330,7 @@ namespace Ryujinx.Ava.UI.ViewModels } public DateTimeOffset CurrentDate { get; set; } + public TimeSpan CurrentTime { get; set; } internal AvaloniaList TimeZones { get; set; } @@ -453,6 +454,18 @@ namespace Ryujinx.Ava.UI.ViewModels Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex))); } + public void MatchSystemTime() + { + var dto = DateTimeOffset.Now; + + CurrentDate = new DateTimeOffset(dto.Year, dto.Month, dto.Day, 0, 0, 0, dto.Offset); + + CurrentTime = dto.TimeOfDay; + + OnPropertyChanged(nameof(CurrentDate)); + OnPropertyChanged(nameof(CurrentTime)); + } + public async Task LoadTimeZones() { _timeZoneContentManager = new TimeZoneContentManager(); diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 04c303cd3..fa900be81 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -178,7 +178,7 @@ namespace Ryujinx.Ava.UI.Views.Main private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) { if (sender is MenuItem) - ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasKeyRetailBinPath; + ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile; } private async void InstallFileTypes_Click(object sender, RoutedEventArgs e) diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index e04e541c3..73cc70a23 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -182,7 +182,20 @@ Width="350" ToolTip.Tip="{ext:Locale TimeTooltip}" /> - + + + + ViewModel.MatchSystemTime(); } }