diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index b352e82d7..3100671e9 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -61,9 +61,9 @@ jobs: | Platform | Artifact | |--|--| - | Windows 64 bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | - | Linux 64 bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | - | Linux arm 64 bit | [Canary Linux arm Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | + | Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | + | Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | + | Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | macOS | [Canary macOS artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }} @@ -200,10 +200,10 @@ jobs: | Platform | Artifact | |--|--| - | Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip | - | Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | - | Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | - | Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | + | Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip | + | Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | + | Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | + | macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | "**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}" omitBodyDuringUpdate: true @@ -272,19 +272,7 @@ jobs: name: "Canary ${{ steps.version_info.outputs.build_version }}" artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz" tag: ${{ steps.version_info.outputs.build_version }} - body: | - # Canary builds: - - These builds are experimental and may sometimes not work, use [regular builds](https://github.com/GreemDev/Ryujinx/releases/latest) instead if that sounds like something you don't want to deal with. - - | Platform | Artifact | - |--|--| - | Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip | - | Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | - | Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | - | Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | - - "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}" + body: "" omitBodyDuringUpdate: true allowUpdates: true replacesArtifacts: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 815321dfe..59c31c71b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,9 +57,9 @@ jobs: # Regular builds: | Platform | Artifact | |--|--| - | Windows 64 bit | [Release Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | - | Linux 64 bit | [Release Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | - | Linux arm 64 bit | [Release Linux arm Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | + | Windows 64-bit | [Release Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) | + | Linux 64-bit | [Release Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) | + | Linux ARM 64-bit | [Release Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) | | macOS | [Release macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) | **Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }} @@ -189,10 +189,10 @@ jobs: # Regular builds: | Platform | Artifact | |--|--| - | Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip | - | Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | - | Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | - | Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | + | Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip | + | Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | + | Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | + | macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}" omitBodyDuringUpdate: true @@ -261,16 +261,7 @@ jobs: name: ${{ steps.version_info.outputs.build_version }} artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz" tag: ${{ steps.version_info.outputs.build_version }} - body: | - # Regular builds: - | Platform | Artifact | - |--|--| - | Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip | - | Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz | - | Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz | - | Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz | - - "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}" + body: "" omitBodyDuringUpdate: true allowUpdates: true replacesArtifacts: true diff --git a/distribution/misc/macOS.svg b/distribution/misc/macOS.svg deleted file mode 100644 index 4bdd453a8..000000000 --- a/distribution/misc/macOS.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs b/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs index 183228fdd..6dc3d8aa8 100644 --- a/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs +++ b/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs @@ -14,20 +14,20 @@ namespace Ryujinx.BuildValidationTasks { string path = System.Reflection.Assembly.GetExecutingAssembly().Location; - if (path.Split(new string[] { "src" }, StringSplitOptions.None).Length == 1 ) + if (path.Split(["src"], StringSplitOptions.None).Length == 1) { //i assume that we are in a build directory in the solution dir - path = new FileInfo(path).Directory.Parent.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName; + path = new FileInfo(path).Directory!.Parent!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName; } else { - path = path.Split(new string[] { "src" }, StringSplitOptions.None)[0]; - path = new FileInfo(path).Directory.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName; + path = path.Split(["src"], StringSplitOptions.None)[0]; + path = new FileInfo(path).Directory!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName; } string data; - using (StreamReader sr = new StreamReader(path)) + using (StreamReader sr = new(path)) { data = sr.ReadToEnd(); } @@ -38,13 +38,10 @@ namespace Ryujinx.BuildValidationTasks { LocalesEntry locale = json.Locales[i]; - foreach (string language in json.Languages) + foreach (string langCode in json.Languages.Where(it => !locale.Translations.ContainsKey(it))) { - if (!locale.Translations.ContainsKey(language)) - { - locale.Translations.Add(language, ""); - Log.LogMessage(MessageImportance.High, $"Added {{{language}}} to Locale {{{locale.ID}}}"); - } + locale.Translations.Add(langCode, string.Empty); + Log.LogMessage(MessageImportance.High, $"Added '{langCode}' to Locale '{locale.ID}'"); } locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value); @@ -53,7 +50,7 @@ namespace Ryujinx.BuildValidationTasks string jsonString = JsonConvert.SerializeObject(json, Formatting.Indented); - using (StreamWriter sw = new StreamWriter(path)) + using (StreamWriter sw = new(path)) { sw.Write(jsonString); } diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index 011d9848a..cbf93013f 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -6,7 +6,6 @@ namespace Ryujinx.Common // DO NOT EDIT, filled by CI public static class ReleaseInformation { - private const string FlatHubChannel = "flathub"; private const string CanaryChannel = "canary"; private const string ReleaseChannel = "release"; @@ -29,8 +28,6 @@ namespace Ryujinx.Common !ReleaseChannelRepo.StartsWith("%%") && !ConfigFileName.StartsWith("%%"); - public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannel); - public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel); public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel); 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/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index bb56a4344..74c39d6a8 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -230,25 +230,20 @@ namespace Ryujinx.Cpu.AppleHv { if (size == 0) { - return Enumerable.Empty(); + yield break; } var guestRegions = GetPhysicalRegionsImpl(va, size); if (guestRegions == null) { - return null; + yield break; } - var regions = new HostMemoryRange[guestRegions.Count]; - - for (int i = 0; i < regions.Length; i++) + foreach (var guestRegion in guestRegions) { - var guestRegion = guestRegions[i]; nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); - regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); } - - return regions; } /// @@ -256,23 +251,24 @@ namespace Ryujinx.Cpu.AppleHv { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetPhysicalRegionsImpl(va, size); + foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size)) + { + yield return physicalRegion; + } } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -280,14 +276,14 @@ namespace Ryujinx.Cpu.AppleHv { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -296,9 +292,7 @@ namespace Ryujinx.Cpu.AppleHv regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// 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.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs index 049e508d0..076fb6ad8 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -250,25 +250,20 @@ namespace Ryujinx.Cpu.Jit { if (size == 0) { - return Enumerable.Empty(); + yield break; } var guestRegions = GetPhysicalRegionsImpl(va, size); if (guestRegions == null) { - return null; + yield break; } - var regions = new HostMemoryRange[guestRegions.Count]; - - for (int i = 0; i < regions.Length; i++) + foreach (var guestRegion in guestRegions) { - var guestRegion = guestRegions[i]; nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); - regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); } - - return regions; } /// @@ -276,23 +271,24 @@ namespace Ryujinx.Cpu.Jit { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetPhysicalRegionsImpl(va, size); + foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size)) + { + yield return physicalRegion; + } } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -300,14 +296,14 @@ namespace Ryujinx.Cpu.Jit { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -316,9 +312,7 @@ namespace Ryujinx.Cpu.Jit regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs index 4dab212a7..499f991f2 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs @@ -475,17 +475,15 @@ namespace Ryujinx.Cpu.Jit return GetPhysicalRegionsImpl(va, size); } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -493,14 +491,14 @@ namespace Ryujinx.Cpu.Jit { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -509,9 +507,7 @@ namespace Ryujinx.Cpu.Jit regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs index 3ce7c4f9c..1432c4598 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs @@ -8,8 +8,6 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 { public IEnumerable GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size) { - List functionPointers = new(); - while (true) { nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size); @@ -20,11 +18,9 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 break; } - functionPointers.Add((ulong)functionPointer - 4); + yield return (ulong)functionPointer - 4; framePointer = Marshal.ReadIntPtr(framePointer); } - - return functionPointers; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs index 5722ca1ac..e79248a47 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs @@ -168,16 +168,14 @@ namespace Ryujinx.Graphics.Vulkan return BinarySearch(list, offset, size) >= 0; } - public readonly List FindOverlaps(int offset, int size) + public readonly IEnumerable FindOverlaps(int offset, int size) { var list = _ranges; if (list == null) { - return null; + yield break; } - List result = null; - int index = BinarySearch(list, offset, size); if (index >= 0) @@ -189,12 +187,10 @@ namespace Ryujinx.Graphics.Vulkan do { - (result ??= new List()).Add(list[index++]); + yield return list[index++]; } while (index < list.Count && list[index].OverlapsWith(offset, size)); } - - return result; } private static int BinarySearch(List list, int offset, int size) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 5a3bd61bd..09f22889c 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan _api = api; _physicalDevice = physicalDevice; - int totalFormats = Enum.GetNames(typeof(Format)).Length; + int totalFormats = Enum.GetNames().Length; _bufferTable = new FormatFeatureFlags[totalFormats]; _optimalTable = new FormatFeatureFlags[totalFormats]; @@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan return (formatFeatureFlags & flags) == flags; } - public VkFormat ConvertToVkFormat(Format srcFormat) + public VkFormat ConvertToVkFormat(Format srcFormat, bool storageFeatureFlagRequired) { var format = FormatTable.GetFormat(srcFormat); @@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit; } - if (srcFormat.IsImageCompatible()) + if (srcFormat.IsImageCompatible() && storageFeatureFlagRequired) { requiredFeatures |= FormatFeatureFlags.StorageImageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index 98796d9bf..305224cad 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan static FormatTable() { - _table = new VkFormat[Enum.GetNames(typeof(Format)).Length]; + _table = new VkFormat[Enum.GetNames().Length]; _reverseMap = new Dictionary(); #pragma warning disable IDE0055 // Disable formatting diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 85069c6b2..8a895f927 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -29,11 +29,17 @@ namespace Ryujinx.Graphics.Vulkan int colorCount = 0; int maxColorAttachmentIndex = -1; + bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || + !state.DepthStencilFormat.IsImageCompatible(); + for (int i = 0; i < state.AttachmentEnable.Length; i++) { if (state.AttachmentEnable[i]) { - attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample || + !state.AttachmentFormats[i].IsImageCompatible(); + + attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorageAttachments); attachmentIndices[attachmentCount++] = i; colorCount++; @@ -43,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan if (state.DepthStencilEnable) { - attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); + attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); } if (attachmentCount != 0) @@ -296,7 +302,10 @@ namespace Ryujinx.Graphics.Vulkan { if (state.AttachmentEnable[i]) { - pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); + bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || + !state.AttachmentFormats[i].IsImageCompatible(); + + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorage); maxColorAttachmentIndex = i; if (state.AttachmentFormats[i].IsInteger()) @@ -310,7 +319,10 @@ namespace Ryujinx.Graphics.Vulkan if (state.DepthStencilEnable) { - pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); + bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() || + gd.Capabilities.SupportsShaderStorageImageMultisample; + + pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); } pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index 518ede5f3..c07e1c09c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries { _pipeline = pipeline; - int count = Enum.GetNames(typeof(CounterType)).Length; + int count = Enum.GetNames().Length; _counterQueues = new CounterQueue[count]; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 10b36a3f9..51ef528d4 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -77,7 +77,9 @@ namespace Ryujinx.Graphics.Vulkan _device = device; _info = info; - var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + + var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); @@ -91,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities); + var usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true); var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; @@ -305,7 +307,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities) + public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage) { var usage = DefaultUsageFlags; @@ -318,9 +320,7 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample; - - if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) + if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage) { usage |= ImageUsageFlags.StorageBit; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index b7b936809..64d976a45 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -61,8 +61,11 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); - var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); - var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities); + bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); + + var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); + var usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false); + var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index c585aed54..f9c5ddecf 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -341,7 +341,7 @@ namespace Ryujinx.HLE.HOS { if (VirtualAmiibo.ApplicationBytes.Length > 0) { - VirtualAmiibo.ApplicationBytes = new byte[0]; + VirtualAmiibo.ApplicationBytes = Array.Empty(); VirtualAmiibo.InputBin = string.Empty; } if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag) @@ -356,7 +356,7 @@ namespace Ryujinx.HLE.HOS VirtualAmiibo.InputBin = path; if (VirtualAmiibo.ApplicationBytes.Length > 0) { - VirtualAmiibo.ApplicationBytes = new byte[0]; + VirtualAmiibo.ApplicationBytes = Array.Empty(); } byte[] encryptedData = File.ReadAllBytes(path); VirtualAmiiboFile newFile = AmiiboBinReader.ReadBinFile(encryptedData); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs index 90231b460..1763edce2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs @@ -15,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private readonly long[] _current2; private readonly long[] _peak; + // type is not Lock due to Monitor class usage private readonly object _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/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index d8c62fc66..4bd695ae5 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -357,7 +357,6 @@ namespace Ryujinx.HLE.HOS { string cheatName = DefaultCheatName; List instructions = new(); - List cheats = new(); using StreamReader cheatData = cheatFile.OpenText(); while (cheatData.ReadLine() is { } line) @@ -373,13 +372,13 @@ namespace Ryujinx.HLE.HOS Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return Array.Empty(); + yield break; } // Add the previous section to the list. if (instructions.Count > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + yield return new Cheat($"<{cheatName} Cheat>", cheatFile, instructions); } // Start a new cheat section. @@ -396,10 +395,8 @@ namespace Ryujinx.HLE.HOS // Add the last section being processed. if (instructions.Count > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + yield return new Cheat($"<{cheatName} Cheat>", cheatFile, instructions); } - - return cheats; } // Assumes searchDirPaths don't overlap diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 808f21c0e..cd3df4edf 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -23,18 +23,18 @@ namespace Ryujinx.HLE.HOS.Services public IpcService(ServerBase server = null) { - CmifCommands = typeof(IpcService).Assembly.GetTypes() + CmifCommands = GetType().Assembly.GetTypes() .Where(type => type == GetType()) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandCmifAttribute)) - .Select(command => (((CommandCmifAttribute)command).Id, methodInfo))) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes() + .Select(command => (command.Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); - TipcCommands = typeof(IpcService).Assembly.GetTypes() + TipcCommands = GetType().Assembly.GetTypes() .Where(type => type == GetType()) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandTipcAttribute)) - .Select(command => (((CommandTipcAttribute)command).Id, methodInfo))) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes() + .Select(command => (command.Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); Server = server; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 9f65aed4b..b8b3014f1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -444,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator private ResultCode ScanInternal(IVirtualMemoryManager memory, ushort channel, ScanFilter scanFilter, ulong bufferPosition, ulong bufferSize, out ulong counter) { - ulong networkInfoSize = (ulong)Marshal.SizeOf(typeof(NetworkInfo)); + ulong networkInfoSize = (ulong)Marshal.SizeOf(); ulong maxGames = bufferSize / networkInfoSize; MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize); diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs index 13a5ef998..03063cb53 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs @@ -33,12 +33,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption const int pageSize = 4; const int totalBytes = totalPages * pageSize; - if (fileBytes.Length < totalBytes) + if (fileBytes.Length == 532) { - return new VirtualAmiiboFile(); + // add 8 bytes to the end of the file + byte[] newFileBytes = new byte[totalBytes]; + Array.Copy(fileBytes, newFileBytes, fileBytes.Length); + fileBytes = newFileBytes; } - AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath); + AmiiboDecryptor amiiboDecryptor = new(keyRetailBinPath); AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(fileBytes); byte[] titleId = new byte[8]; @@ -171,7 +174,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption return false; } - AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath); + if (readBytes.Length == 532) + { + // add 8 bytes to the end of the file + byte[] newFileBytes = new byte[540]; + Array.Copy(readBytes, newFileBytes, readBytes.Length); + readBytes = newFileBytes; + } + + AmiiboDecryptor amiiboDecryptor = new AmiiboDecryptor(keyRetailBinPath); AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes); byte[] oldData = amiiboDump.GetData(); @@ -231,7 +242,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption return false; } - AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath); + if (readBytes.Length == 532) + { + // add 8 bytes to the end of the file + byte[] newFileBytes = new byte[540]; + Array.Copy(readBytes, newFileBytes, readBytes.Length); + readBytes = newFileBytes; + } + + AmiiboDecryptor amiiboDecryptor = new AmiiboDecryptor(keyRetailBinPath); AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes); amiiboDump.AmiiboNickname = newNickName; byte[] oldData = amiiboDump.GetData(); @@ -314,10 +333,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin"); } - public static bool HasKeyRetailBinPath() - { - return File.Exists(GetKeyRetailBinPath()); - } + public static bool HasAmiiboKeyFile => File.Exists(GetKeyRetailBinPath()); + + public static DateTime DateTimeFromTag(ushort value) { try diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecrypter.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs similarity index 77% rename from src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecrypter.cs rename to src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs index 71758c947..cc6d02ea2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecrypter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs @@ -2,12 +2,12 @@ using System.IO; namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption { - public class AmiiboDecrypter + public class AmiiboDecryptor { public AmiiboMasterKey DataKey { get; private set; } public AmiiboMasterKey TagKey { get; private set; } - public AmiiboDecrypter(string keyRetailBinPath) + public AmiiboDecryptor(string keyRetailBinPath) { var combinedKeys = File.ReadAllBytes(keyRetailBinPath); var keys = AmiiboMasterKey.FromCombinedBin(combinedKeys); @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption public AmiiboDump DecryptAmiiboDump(byte[] encryptedDumpData) { // Initialize AmiiboDump with encrypted data - AmiiboDump amiiboDump = new AmiiboDump(encryptedDumpData, DataKey, TagKey, isLocked: true); + AmiiboDump amiiboDump = new(encryptedDumpData, DataKey, TagKey, isLocked: true); // Unlock (decrypt) the dump amiiboDump.Unlock(); @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption public AmiiboDump EncryptAmiiboDump(byte[] decryptedDumpData) { // Initialize AmiiboDump with decrypted data - AmiiboDump amiiboDump = new AmiiboDump(decryptedDumpData, DataKey, TagKey, isLocked: false); + AmiiboDump amiiboDump = new(decryptedDumpData, DataKey, TagKey, isLocked: false); // Lock (encrypt) the dump amiiboDump.Lock(); 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/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index ee43fc307..2cb35472f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp static class VirtualAmiibo { public static uint OpenedApplicationAreaId; - public static byte[] ApplicationBytes = new byte[0]; + public static byte[] ApplicationBytes = Array.Empty(); public static string InputBin = string.Empty; public static string NickName = string.Empty; private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default; @@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (ApplicationBytes.Length > 0) { byte[] bytes = ApplicationBytes; - ApplicationBytes = new byte[0]; + ApplicationBytes = Array.Empty(); return bytes; } VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 7a90c664e..271b8fc84 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm { if (_services.TryGetValue(name, out Type type)) { - ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name); + ServiceAttribute serviceAttribute = type.GetCustomAttributes().First(service => service.Name == name); IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter); 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.Horizon.Kernel.Generators/SyscallGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs index 7419a839a..06b98e09d 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs @@ -34,14 +34,14 @@ namespace Ryujinx.Horizon.Kernel.Generators private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName; private const string TypeExecutionContext = "IExecutionContext"; - private static readonly string[] _expectedResults = new string[] - { + private static readonly string[] _expectedResults = + [ $"{TypeResultName}.Success", $"{TypeKernelResultName}.TimedOut", $"{TypeKernelResultName}.Cancelled", $"{TypeKernelResultName}.PortRemoteClosed", $"{TypeKernelResultName}.InvalidState", - }; + ]; private readonly struct OutParameter { diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs index 807c5c0f4..7bd572d7a 100644 --- a/src/Ryujinx.Memory/AddressSpaceManager.cs +++ b/src/Ryujinx.Memory/AddressSpaceManager.cs @@ -106,10 +106,13 @@ namespace Ryujinx.Memory { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetHostRegionsImpl(va, size); + foreach (var hostRegion in GetHostRegionsImpl(va, size)) + { + yield return hostRegion; + } } /// @@ -117,51 +120,36 @@ namespace Ryujinx.Memory { if (size == 0) { - return Enumerable.Empty(); + yield break; } var hostRegions = GetHostRegionsImpl(va, size); if (hostRegions == null) { - return null; + yield break; } - var regions = new MemoryRange[hostRegions.Count]; - ulong backingStart = (ulong)_backingMemory.Pointer; ulong backingEnd = backingStart + _backingMemory.Size; - int count = 0; - - for (int i = 0; i < regions.Length; i++) + foreach (var hostRegion in hostRegions) { - var hostRegion = hostRegions[i]; - if (hostRegion.Address >= backingStart && hostRegion.Address < backingEnd) { - regions[count++] = new MemoryRange(hostRegion.Address - backingStart, hostRegion.Size); + yield return new MemoryRange(hostRegion.Address - backingStart, hostRegion.Size); } } - - if (count != regions.Length) - { - return new ArraySegment(regions, 0, count); - } - - return regions; } - private List GetHostRegionsImpl(ulong va, ulong size) + private IEnumerable GetHostRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, size, out va); - var regions = new List(); - nuint regionStart = GetHostAddress(va); ulong regionSize = PageSize; @@ -169,14 +157,14 @@ namespace Ryujinx.Memory { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } nuint newHostAddress = GetHostAddress(va + PageSize); if (GetHostAddress(va) + PageSize != newHostAddress) { - regions.Add(new HostMemoryRange(regionStart, regionSize)); + yield return new HostMemoryRange(regionStart, regionSize); regionStart = newHostAddress; regionSize = 0; } @@ -185,9 +173,7 @@ namespace Ryujinx.Memory regionSize += PageSize; } - regions.Add(new HostMemoryRange(regionStart, regionSize)); - - return regions; + yield return new HostMemoryRange(regionStart, regionSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Ryujinx.UI.Common/App/ApplicationData.cs b/src/Ryujinx.UI.Common/App/ApplicationData.cs index 151220f39..657b9a022 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationData.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationData.cs @@ -38,7 +38,7 @@ namespace Ryujinx.UI.App.Common public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); - public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed) ?? LocalizedNever(); + public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever(); public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); diff --git a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs index 9333a1b76..44860d080 100644 --- a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs @@ -23,7 +23,7 @@ namespace Ryujinx.UI.Common.Helper [LibraryImport("shell32.dll", SetLastError = true)] public static partial void SHChangeNotify(uint wEventId, uint uFlags, nint dwItem1, nint dwItem2); - public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild; + public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()); public static bool AreMimeTypesRegistered { 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 a85c8c06a..cfe2b817a 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -705,7 +705,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "Amiibo 스캔(빈에서)", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1077,6 +1077,54 @@ "zh_TW": "" } }, + { + "ID": "MenuBarViewWindow1440", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "1440p", + "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": "MenuBarViewWindow2160", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "2160p", + "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": "MenuBarHelp", "Translations": { @@ -1125,6 +1173,30 @@ "zh_TW": "檢查更新" } }, + { + "ID": "MenuBarHelpFaqAndGuides", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "FAQ & Guides", + "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": "MenuBarHelpFaq", "Translations": { @@ -1137,7 +1209,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "자주 묻는 질문(FAQ) 및 문제해결 페이지", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1161,7 +1233,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "공식 Ryujinx 위키에서 자주 묻는 질문(FAQ) 및 문제 해결 페이지 열기", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1185,7 +1257,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "설치 및 구성 안내", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1209,7 +1281,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "공식 Ryujinx 위키에서 설정 및 구성 안내 열기", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1233,7 +1305,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "멀티플레이어(LDN/LAN) 안내", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -1257,7 +1329,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "공식 Ryujinx 위키에서 멀티플레이어 안내 열기", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -2424,25 +2496,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 +3837,30 @@ "zh_TW": "系統時鐘:" } }, + { + "ID": "SettingsTabSystemSystemTimeMatch", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Resync to PC 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": "SettingsTabSystemEnablePptc", "Translations": { @@ -8049,7 +8145,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "지우기", + "ko_KR": "", "no_NO": "Tøm", "pl_PL": "", "pt_BR": "", @@ -11841,7 +11937,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "{0} : {1}", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -14541,6 +14637,30 @@ "zh_TW": "變更系統時鐘" } }, + { + "ID": "MatchTimeTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Resync System Time to match your PC's current date & time.\n\nThis is not an active setting, it can still fall out of sync; in which case just click this button again.", + "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": { @@ -16125,30 +16245,6 @@ "zh_TW": "CPU 模式" } }, - { - "ID": "DialogUpdaterFlatpakNotSupportedMessage", - "Translations": { - "ar_SA": "الرجاء تحديث ريوجينكس عبر فلات هاب.", - "de_DE": "Bitte aktualisiere Ryujinx über FlatHub", - "el_GR": "Παρακαλούμε ενημερώστε το Ryujinx μέσω FlatHub.", - "en_US": "Please update Ryujinx via FlatHub.", - "es_ES": "Por favor, actualiza Ryujinx a través de FlatHub.", - "fr_FR": "Merci de mettre à jour Ryujinx via FlatHub.", - "he_IL": "בבקשה עדכן את ריוג'ינקס דרך פלאטהב.", - "it_IT": "Aggiorna Ryujinx tramite FlatHub.", - "ja_JP": "FlatHub を使用して Ryujinx をアップデートしてください.", - "ko_KR": "FlatHub를 통해 Ryujinx를 업데이트하세요.", - "no_NO": "Vennligst oppdater Ryujinx via FlatHub.", - "pl_PL": "Zaktualizuj Ryujinx przez FlatHub.", - "pt_BR": "Por favor, atualize o Ryujinx pelo FlatHub.", - "ru_RU": "Пожалуйста, обновите Ryujinx через FlatHub.", - "th_TH": "โปรดอัปเดต Ryujinx ผ่านช่องทาง FlatHub", - "tr_TR": "Lütfen Ryujinx'i FlatHub aracılığıyla güncelleyin.", - "uk_UA": "", - "zh_CN": "请通过 FlatHub 更新 Ryujinx 模拟器。", - "zh_TW": "請透過 Flathub 更新 Ryujinx。" - } - }, { "ID": "UpdaterDisabledWarningTitle", "Translations": { @@ -18753,7 +18849,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "{0:n0}MB", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -19041,7 +19137,7 @@ "he_IL": "{0} הרחבות משחק", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "{0} DLC 사용 가능", "no_NO": "{0} Nedlastbare innhold(er)", "pl_PL": "", "pt_BR": "", @@ -21177,7 +21273,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "수직 동기화 :", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21201,7 +21297,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 활성화(실험적)", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21225,7 +21321,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "스위치", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21249,7 +21345,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "무제한", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21273,7 +21369,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21297,7 +21393,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "에뮬레이트된 수직 동기화. '스위치'는 스위치의 60Hz 주사율을 에뮬레이트합니다. '무한'은 무제한 주사율입니다.", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21321,7 +21417,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "에뮬레이트된 수직 동기화. '스위치'는 스위치의 60Hz 주사율을 에뮬레이트합니다. '무한'은 무제한 주사율입니다. '사용자 지정'은 지정된 사용자 지정 주사율을 에뮬레이트합니다.", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21345,7 +21441,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자가 에뮬레이트된 화면 주사율을 지정할 수 있습니다. 일부 타이틀에서는 게임플레이 로직 속도가 빨라지거나 느려질 수 있습니다. 다른 타이틀에서는 주사율의 배수로 FPS를 제한하거나 예측할 수 없는 동작으로 이어질 수 있습니다. 이는 실험적 기능으로 게임 플레이에 어떤 영향을 미칠지 보장할 수 없습니다. \n\n모르면 끔으로 두세요.", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21369,7 +21465,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 목표 값입니다.", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21393,7 +21489,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "일반 스위치 주사율의 백분율로 나타낸 사용자 지정 주사율입니다.", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21417,7 +21513,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 % :", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21441,7 +21537,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 값 :", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21465,7 +21561,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "간격", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21489,7 +21585,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "수직 동기화 모드 전환 :", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21513,7 +21609,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 증가", "no_NO": "", "pl_PL": "", "pt_BR": "", @@ -21537,7 +21633,7 @@ "he_IL": "", "it_IT": "", "ja_JP": "", - "ko_KR": "", + "ko_KR": "사용자 정의 주사율 감소", "no_NO": "", "pl_PL": "", "pt_BR": "", 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/Controls/ApplicationContextMenu.axaml b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml index 951f7f616..7708936ca 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml @@ -17,7 +17,6 @@ diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index f11d6e404..493e6659d 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input { if (IsModified) { - + _playerIdChoose = value; return; } @@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input IsModified = false; _playerId = value; - if (!Enum.IsDefined(typeof(PlayerIndex), _playerId)) + if (!Enum.IsDefined(_playerId)) { _playerId = PlayerIndex.Player1; diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 283f9b9da..ae373c267 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -127,6 +127,9 @@ namespace Ryujinx.Ava.UI.ViewModels public IEnumerable LastLdnGameData; + // The UI specifically uses a thicker bordered variant of the icon to avoid crunching out the border at lower resolutions. + // For an example of this, download canary 1.2.95, then open the settings menu, and look at the icon in the top-left. + // The border gets reduced to colored pixels in the 4 corners. public static readonly Bitmap IconBitmap = new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Thiccjinx.png")!); @@ -330,6 +333,9 @@ namespace Ryujinx.Ava.UI.ViewModels OnPropertyChanged(); } } + + public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile; + public bool ShowLoadProgress { get => _showLoadProgress; @@ -418,8 +424,6 @@ namespace Ryujinx.Ava.UI.ViewModels public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild; - public string LoadHeading { get => _loadHeading; @@ -2009,7 +2013,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 b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml index d9690e8ce..7d8135dcf 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml @@ -247,6 +247,7 @@ Click="OpenBinFile" Header="{ext:Locale MenuBarActionsScanAmiiboBin}" Icon="{ext:Icon mdi-cube-scan}" + IsVisible="{Binding CanScanAmiiboBinaries}" IsEnabled="{Binding IsAmiiboBinRequested}" /> + + + - - - - - + + + + + diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 79600a3fb..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/Main/MainStatusBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs index dd4ed8297..1b017dbeb 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Ava.UI.Views.Main private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e) { AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; - ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; + ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; } private void Refresh_OnClick(object sender, RoutedEventArgs e) => Window.LoadApplications(); diff --git a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml index e04e541c3..9295413ba 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsSystemView.axaml @@ -181,8 +181,17 @@ SelectedTime="{Binding CurrentTime}" Width="350" ToolTip.Tip="{ext:Locale TimeTooltip}" /> + - + ViewModel.MatchSystemTime(); } } diff --git a/src/Ryujinx/Updater.cs b/src/Ryujinx/Updater.cs index e240ad141..21d991d97 100644 --- a/src/Ryujinx/Updater.cs +++ b/src/Ryujinx/Updater.cs @@ -686,22 +686,11 @@ namespace Ryujinx.Ava #else if (showWarnings) { - if (ReleaseInformation.IsFlatHubBuild) - { - Dispatcher.UIThread.InvokeAsync(() => - ContentDialogHelper.CreateWarningDialog( - LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], - LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]) + Dispatcher.UIThread.InvokeAsync(() => + ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]) ); - } - else - { - Dispatcher.UIThread.InvokeAsync(() => - ContentDialogHelper.CreateWarningDialog( - LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], - LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]) - ); - } } return false;