Merge branch 'GreemDev:master' into frenchlocalebut2

This commit is contained in:
GabCoolGuy 2024-12-23 10:15:38 +01:00 committed by GitHub
commit 66c582f6cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 451 additions and 421 deletions

View File

@ -61,9 +61,9 @@ jobs:
| Platform | Artifact | | 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) | | 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 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) | | 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) | | 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 }} **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 | | 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 | | 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 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 | | 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 | | 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 }}" "**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 omitBodyDuringUpdate: true
@ -272,19 +272,7 @@ jobs:
name: "Canary ${{ steps.version_info.outputs.build_version }}" name: "Canary ${{ steps.version_info.outputs.build_version }}"
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz" artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: | 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 }}"
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true

View File

@ -57,9 +57,9 @@ jobs:
# Regular builds: # Regular builds:
| Platform | Artifact | | 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) | | 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 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) | | 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) | | 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 }} **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: # Regular builds:
| Platform | Artifact | | 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 | | 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 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 | | 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 | | 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 }}" "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
@ -261,16 +261,7 @@ jobs:
name: ${{ steps.version_info.outputs.build_version }} name: ${{ steps.version_info.outputs.build_version }}
artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz" artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }}
body: | 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 }}"
omitBodyDuringUpdate: true omitBodyDuringUpdate: true
allowUpdates: true allowUpdates: true
replacesArtifacts: true replacesArtifacts: true

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -14,20 +14,20 @@ namespace Ryujinx.BuildValidationTasks
{ {
string path = System.Reflection.Assembly.GetExecutingAssembly().Location; 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 //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 else
{ {
path = path.Split(new string[] { "src" }, StringSplitOptions.None)[0]; 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; path = new FileInfo(path).Directory!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
} }
string data; string data;
using (StreamReader sr = new StreamReader(path)) using (StreamReader sr = new(path))
{ {
data = sr.ReadToEnd(); data = sr.ReadToEnd();
} }
@ -38,13 +38,10 @@ namespace Ryujinx.BuildValidationTasks
{ {
LocalesEntry locale = json.Locales[i]; 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(langCode, string.Empty);
{ Log.LogMessage(MessageImportance.High, $"Added '{langCode}' to Locale '{locale.ID}'");
locale.Translations.Add(language, "");
Log.LogMessage(MessageImportance.High, $"Added {{{language}}} to Locale {{{locale.ID}}}");
}
} }
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value); 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); string jsonString = JsonConvert.SerializeObject(json, Formatting.Indented);
using (StreamWriter sw = new StreamWriter(path)) using (StreamWriter sw = new(path))
{ {
sw.Write(jsonString); sw.Write(jsonString);
} }

View File

@ -6,7 +6,6 @@ namespace Ryujinx.Common
// DO NOT EDIT, filled by CI // DO NOT EDIT, filled by CI
public static class ReleaseInformation public static class ReleaseInformation
{ {
private const string FlatHubChannel = "flathub";
private const string CanaryChannel = "canary"; private const string CanaryChannel = "canary";
private const string ReleaseChannel = "release"; private const string ReleaseChannel = "release";
@ -29,8 +28,6 @@ namespace Ryujinx.Common
!ReleaseChannelRepo.StartsWith("%%") && !ReleaseChannelRepo.StartsWith("%%") &&
!ConfigFileName.StartsWith("%%"); !ConfigFileName.StartsWith("%%");
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannel);
public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel); public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel);
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel); public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);

View File

@ -1,3 +1,4 @@
using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -27,9 +28,14 @@ namespace Ryujinx.Common.Utilities
ReadCommentHandling = JsonCommentHandling.Skip ReadCommentHandling = JsonCommentHandling.Skip
}; };
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Serialize(value, typeInfo); public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
=> JsonSerializer.Serialize(value, typeInfo);
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Deserialize(value, typeInfo); public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
=> JsonSerializer.Deserialize(value, typeInfo);
public static T Deserialize<T>(ReadOnlySpan<byte> utf8Value, JsonTypeInfo<T> typeInfo)
=> JsonSerializer.Deserialize<T>(utf8Value, typeInfo);
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo) public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
{ {

View File

@ -230,25 +230,20 @@ namespace Ryujinx.Cpu.AppleHv
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<HostMemoryRange>(); yield break;
} }
var guestRegions = GetPhysicalRegionsImpl(va, size); var guestRegions = GetPhysicalRegionsImpl(va, size);
if (guestRegions == null) if (guestRegions == null)
{ {
return null; yield break;
} }
var regions = new HostMemoryRange[guestRegions.Count]; foreach (var guestRegion in guestRegions)
for (int i = 0; i < regions.Length; i++)
{ {
var guestRegion = guestRegions[i];
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); 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;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -256,23 +251,24 @@ namespace Ryujinx.Cpu.AppleHv
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<MemoryRange>(); yield break;
} }
return GetPhysicalRegionsImpl(va, size); foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size))
{
yield return physicalRegion;
}
} }
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size) private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
{ {
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
{ {
return null; yield break;
} }
int pages = GetPagesCount(va, (uint)size, out va); int pages = GetPagesCount(va, (uint)size, out va);
var regions = new List<MemoryRange>();
ulong regionStart = GetPhysicalAddressInternal(va); ulong regionStart = GetPhysicalAddressInternal(va);
ulong regionSize = PageSize; ulong regionSize = PageSize;
@ -280,14 +276,14 @@ namespace Ryujinx.Cpu.AppleHv
{ {
if (!ValidateAddress(va + PageSize)) if (!ValidateAddress(va + PageSize))
{ {
return null; yield break;
} }
ulong newPa = GetPhysicalAddressInternal(va + PageSize); ulong newPa = GetPhysicalAddressInternal(va + PageSize);
if (GetPhysicalAddressInternal(va) + PageSize != newPa) if (GetPhysicalAddressInternal(va) + PageSize != newPa)
{ {
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
regionStart = newPa; regionStart = newPa;
regionSize = 0; regionSize = 0;
} }
@ -296,9 +292,7 @@ namespace Ryujinx.Cpu.AppleHv
regionSize += PageSize; regionSize += PageSize;
} }
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
return regions;
} }
/// <remarks> /// <remarks>

View File

@ -115,6 +115,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked
} }
private readonly AddressIntrusiveRedBlackTree<Mapping> _mappingTree; private readonly AddressIntrusiveRedBlackTree<Mapping> _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; private readonly object _lock;
public Block(MemoryTracking tracking, Func<ulong, ulong> readPtCallback, MemoryBlock memory, ulong size, object locker) : base(memory, size) public Block(MemoryTracking tracking, Func<ulong, ulong> 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 MemoryTracking _tracking;
private readonly Func<ulong, ulong> _readPtCallback; private readonly Func<ulong, ulong> _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; private readonly object _lock;
public AddressSpacePartitionAllocator( public AddressSpacePartitionAllocator(

View File

@ -250,25 +250,20 @@ namespace Ryujinx.Cpu.Jit
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<HostMemoryRange>(); yield break;
} }
var guestRegions = GetPhysicalRegionsImpl(va, size); var guestRegions = GetPhysicalRegionsImpl(va, size);
if (guestRegions == null) if (guestRegions == null)
{ {
return null; yield break;
} }
var regions = new HostMemoryRange[guestRegions.Count]; foreach (var guestRegion in guestRegions)
for (int i = 0; i < regions.Length; i++)
{ {
var guestRegion = guestRegions[i];
nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); 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;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -276,23 +271,24 @@ namespace Ryujinx.Cpu.Jit
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<MemoryRange>(); yield break;
} }
return GetPhysicalRegionsImpl(va, size); foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size))
{
yield return physicalRegion;
}
} }
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size) private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
{ {
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
{ {
return null; yield break;
} }
int pages = GetPagesCount(va, (uint)size, out va); int pages = GetPagesCount(va, (uint)size, out va);
var regions = new List<MemoryRange>();
ulong regionStart = GetPhysicalAddressInternal(va); ulong regionStart = GetPhysicalAddressInternal(va);
ulong regionSize = PageSize; ulong regionSize = PageSize;
@ -300,14 +296,14 @@ namespace Ryujinx.Cpu.Jit
{ {
if (!ValidateAddress(va + PageSize)) if (!ValidateAddress(va + PageSize))
{ {
return null; yield break;
} }
ulong newPa = GetPhysicalAddressInternal(va + PageSize); ulong newPa = GetPhysicalAddressInternal(va + PageSize);
if (GetPhysicalAddressInternal(va) + PageSize != newPa) if (GetPhysicalAddressInternal(va) + PageSize != newPa)
{ {
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
regionStart = newPa; regionStart = newPa;
regionSize = 0; regionSize = 0;
} }
@ -316,9 +312,7 @@ namespace Ryujinx.Cpu.Jit
regionSize += PageSize; regionSize += PageSize;
} }
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
return regions;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@ -475,17 +475,15 @@ namespace Ryujinx.Cpu.Jit
return GetPhysicalRegionsImpl(va, size); return GetPhysicalRegionsImpl(va, size);
} }
private List<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size) private IEnumerable<MemoryRange> GetPhysicalRegionsImpl(ulong va, ulong size)
{ {
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
{ {
return null; yield break;
} }
int pages = GetPagesCount(va, (uint)size, out va); int pages = GetPagesCount(va, (uint)size, out va);
var regions = new List<MemoryRange>();
ulong regionStart = GetPhysicalAddressInternal(va); ulong regionStart = GetPhysicalAddressInternal(va);
ulong regionSize = PageSize; ulong regionSize = PageSize;
@ -493,14 +491,14 @@ namespace Ryujinx.Cpu.Jit
{ {
if (!ValidateAddress(va + PageSize)) if (!ValidateAddress(va + PageSize))
{ {
return null; yield break;
} }
ulong newPa = GetPhysicalAddressInternal(va + PageSize); ulong newPa = GetPhysicalAddressInternal(va + PageSize);
if (GetPhysicalAddressInternal(va) + PageSize != newPa) if (GetPhysicalAddressInternal(va) + PageSize != newPa)
{ {
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
regionStart = newPa; regionStart = newPa;
regionSize = 0; regionSize = 0;
} }
@ -509,9 +507,7 @@ namespace Ryujinx.Cpu.Jit
regionSize += PageSize; regionSize += PageSize;
} }
regions.Add(new MemoryRange(regionStart, regionSize)); yield return new MemoryRange(regionStart, regionSize);
return regions;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@ -8,8 +8,6 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
{ {
public IEnumerable<ulong> GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size) public IEnumerable<ulong> GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size)
{ {
List<ulong> functionPointers = new();
while (true) while (true)
{ {
nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size); nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size);
@ -20,11 +18,9 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64
break; break;
} }
functionPointers.Add((ulong)functionPointer - 4); yield return (ulong)functionPointer - 4;
framePointer = Marshal.ReadIntPtr(framePointer); framePointer = Marshal.ReadIntPtr(framePointer);
} }
return functionPointers;
} }
} }
} }

View File

@ -168,16 +168,14 @@ namespace Ryujinx.Graphics.Vulkan
return BinarySearch(list, offset, size) >= 0; return BinarySearch(list, offset, size) >= 0;
} }
public readonly List<Range> FindOverlaps(int offset, int size) public readonly IEnumerable<Range> FindOverlaps(int offset, int size)
{ {
var list = _ranges; var list = _ranges;
if (list == null) if (list == null)
{ {
return null; yield break;
} }
List<Range> result = null;
int index = BinarySearch(list, offset, size); int index = BinarySearch(list, offset, size);
if (index >= 0) if (index >= 0)
@ -189,12 +187,10 @@ namespace Ryujinx.Graphics.Vulkan
do do
{ {
(result ??= new List<Range>()).Add(list[index++]); yield return list[index++];
} }
while (index < list.Count && list[index].OverlapsWith(offset, size)); while (index < list.Count && list[index].OverlapsWith(offset, size));
} }
return result;
} }
private static int BinarySearch(List<Range> list, int offset, int size) private static int BinarySearch(List<Range> list, int offset, int size)

View File

@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
_api = api; _api = api;
_physicalDevice = physicalDevice; _physicalDevice = physicalDevice;
int totalFormats = Enum.GetNames(typeof(Format)).Length; int totalFormats = Enum.GetNames<Format>().Length;
_bufferTable = new FormatFeatureFlags[totalFormats]; _bufferTable = new FormatFeatureFlags[totalFormats];
_optimalTable = new FormatFeatureFlags[totalFormats]; _optimalTable = new FormatFeatureFlags[totalFormats];
@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan
return (formatFeatureFlags & flags) == flags; return (formatFeatureFlags & flags) == flags;
} }
public VkFormat ConvertToVkFormat(Format srcFormat) public VkFormat ConvertToVkFormat(Format srcFormat, bool storageFeatureFlagRequired)
{ {
var format = FormatTable.GetFormat(srcFormat); var format = FormatTable.GetFormat(srcFormat);
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit; requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit;
} }
if (srcFormat.IsImageCompatible()) if (srcFormat.IsImageCompatible() && storageFeatureFlagRequired)
{ {
requiredFeatures |= FormatFeatureFlags.StorageImageBit; requiredFeatures |= FormatFeatureFlags.StorageImageBit;
} }

View File

@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
static FormatTable() static FormatTable()
{ {
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length]; _table = new VkFormat[Enum.GetNames<Format>().Length];
_reverseMap = new Dictionary<VkFormat, Format>(); _reverseMap = new Dictionary<VkFormat, Format>();
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting

View File

@ -29,11 +29,17 @@ namespace Ryujinx.Graphics.Vulkan
int colorCount = 0; int colorCount = 0;
int maxColorAttachmentIndex = -1; int maxColorAttachmentIndex = -1;
bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.DepthStencilFormat.IsImageCompatible();
for (int i = 0; i < state.AttachmentEnable.Length; i++) for (int i = 0; i < state.AttachmentEnable.Length; i++)
{ {
if (state.AttachmentEnable[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; attachmentIndices[attachmentCount++] = i;
colorCount++; colorCount++;
@ -43,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
if (state.DepthStencilEnable) if (state.DepthStencilEnable)
{ {
attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage);
} }
if (attachmentCount != 0) if (attachmentCount != 0)
@ -296,7 +302,10 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (state.AttachmentEnable[i]) 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; maxColorAttachmentIndex = i;
if (state.AttachmentFormats[i].IsInteger()) if (state.AttachmentFormats[i].IsInteger())
@ -310,7 +319,10 @@ namespace Ryujinx.Graphics.Vulkan
if (state.DepthStencilEnable) 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); pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);

View File

@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
{ {
_pipeline = pipeline; _pipeline = pipeline;
int count = Enum.GetNames(typeof(CounterType)).Length; int count = Enum.GetNames<CounterType>().Length;
_counterQueues = new CounterQueue[count]; _counterQueues = new CounterQueue[count];

View File

@ -77,7 +77,9 @@ namespace Ryujinx.Graphics.Vulkan
_device = device; _device = device;
_info = info; _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 levels = (uint)info.Levels;
var layers = (uint)info.GetLayers(); var layers = (uint)info.GetLayers();
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); 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 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; 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; var usage = DefaultUsageFlags;
@ -318,9 +320,7 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit; usage |= ImageUsageFlags.ColorAttachmentBit;
} }
bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample; if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage)
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
{ {
usage |= ImageUsageFlags.StorageBit; usage |= ImageUsageFlags.StorageBit;
} }

View File

@ -61,8 +61,11 @@ namespace Ryujinx.Graphics.Vulkan
gd.Textures.Add(this); gd.Textures.Add(this);
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
var usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false);
var levels = (uint)info.Levels; var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers(); var layers = (uint)info.GetLayers();

View File

@ -341,7 +341,7 @@ namespace Ryujinx.HLE.HOS
{ {
if (VirtualAmiibo.ApplicationBytes.Length > 0) if (VirtualAmiibo.ApplicationBytes.Length > 0)
{ {
VirtualAmiibo.ApplicationBytes = new byte[0]; VirtualAmiibo.ApplicationBytes = Array.Empty<byte>();
VirtualAmiibo.InputBin = string.Empty; VirtualAmiibo.InputBin = string.Empty;
} }
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag) if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
@ -356,7 +356,7 @@ namespace Ryujinx.HLE.HOS
VirtualAmiibo.InputBin = path; VirtualAmiibo.InputBin = path;
if (VirtualAmiibo.ApplicationBytes.Length > 0) if (VirtualAmiibo.ApplicationBytes.Length > 0)
{ {
VirtualAmiibo.ApplicationBytes = new byte[0]; VirtualAmiibo.ApplicationBytes = Array.Empty<byte>();
} }
byte[] encryptedData = File.ReadAllBytes(path); byte[] encryptedData = File.ReadAllBytes(path);
VirtualAmiiboFile newFile = AmiiboBinReader.ReadBinFile(encryptedData); VirtualAmiiboFile newFile = AmiiboBinReader.ReadBinFile(encryptedData);

View File

@ -15,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private readonly long[] _current2; private readonly long[] _current2;
private readonly long[] _peak; private readonly long[] _peak;
// type is not Lock due to Monitor class usage
private readonly object _lock = new(); private readonly object _lock = new();
private readonly LinkedList<KThread> _waitingThreads; private readonly LinkedList<KThread> _waitingThreads;

View File

@ -5,10 +5,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KCriticalSection class KCriticalSection
{ {
private readonly KernelContext _context; private readonly KernelContext _context;
private readonly object _lock = new();
private int _recursionCount; 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) public KCriticalSection(KernelContext context)
{ {
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public void Enter() public void Enter()
{ {
Monitor.Enter(_lock); Monitor.Enter(Lock);
_recursionCount++; _recursionCount++;
} }
@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
ulong scheduledCoresMask = KScheduler.SelectThreads(_context); ulong scheduledCoresMask = KScheduler.SelectThreads(_context);
Monitor.Exit(_lock); Monitor.Exit(Lock);
KThread currentThread = KernelStatic.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable; bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable;
@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
} }
else else
{ {
Monitor.Exit(_lock); Monitor.Exit(Lock);
} }
} }
} }

View File

@ -357,7 +357,6 @@ namespace Ryujinx.HLE.HOS
{ {
string cheatName = DefaultCheatName; string cheatName = DefaultCheatName;
List<string> instructions = new(); List<string> instructions = new();
List<Cheat> cheats = new();
using StreamReader cheatData = cheatFile.OpenText(); using StreamReader cheatData = cheatFile.OpenText();
while (cheatData.ReadLine() is { } line) 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"); Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed");
return Array.Empty<Cheat>(); yield break;
} }
// Add the previous section to the list. // Add the previous section to the list.
if (instructions.Count > 0) 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. // Start a new cheat section.
@ -396,10 +395,8 @@ namespace Ryujinx.HLE.HOS
// Add the last section being processed. // Add the last section being processed.
if (instructions.Count > 0) 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 // Assumes searchDirPaths don't overlap

View File

@ -23,18 +23,18 @@ namespace Ryujinx.HLE.HOS.Services
public IpcService(ServerBase server = null) public IpcService(ServerBase server = null)
{ {
CmifCommands = typeof(IpcService).Assembly.GetTypes() CmifCommands = GetType().Assembly.GetTypes()
.Where(type => type == GetType()) .Where(type => type == GetType())
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandCmifAttribute)) .SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandCmifAttribute>()
.Select(command => (((CommandCmifAttribute)command).Id, methodInfo))) .Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo); .ToDictionary(command => command.Id, command => command.methodInfo);
TipcCommands = typeof(IpcService).Assembly.GetTypes() TipcCommands = GetType().Assembly.GetTypes()
.Where(type => type == GetType()) .Where(type => type == GetType())
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandTipcAttribute)) .SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandTipcAttribute>()
.Select(command => (((CommandTipcAttribute)command).Id, methodInfo))) .Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo); .ToDictionary(command => command.Id, command => command.methodInfo);
Server = server; Server = server;

View File

@ -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) 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<NetworkInfo>();
ulong maxGames = bufferSize / networkInfoSize; ulong maxGames = bufferSize / networkInfoSize;
MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize); MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize);

View File

@ -33,12 +33,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
const int pageSize = 4; const int pageSize = 4;
const int totalBytes = totalPages * pageSize; 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); AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(fileBytes);
byte[] titleId = new byte[8]; byte[] titleId = new byte[8];
@ -171,7 +174,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
return false; 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 amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
byte[] oldData = amiiboDump.GetData(); byte[] oldData = amiiboDump.GetData();
@ -231,7 +242,15 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
return false; 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 amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
amiiboDump.AmiiboNickname = newNickName; amiiboDump.AmiiboNickname = newNickName;
byte[] oldData = amiiboDump.GetData(); byte[] oldData = amiiboDump.GetData();
@ -314,10 +333,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin"); return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin");
} }
public static bool HasKeyRetailBinPath() public static bool HasAmiiboKeyFile => File.Exists(GetKeyRetailBinPath());
{
return File.Exists(GetKeyRetailBinPath());
}
public static DateTime DateTimeFromTag(ushort value) public static DateTime DateTimeFromTag(ushort value)
{ {
try try

View File

@ -2,12 +2,12 @@ using System.IO;
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
{ {
public class AmiiboDecrypter public class AmiiboDecryptor
{ {
public AmiiboMasterKey DataKey { get; private set; } public AmiiboMasterKey DataKey { get; private set; }
public AmiiboMasterKey TagKey { get; private set; } public AmiiboMasterKey TagKey { get; private set; }
public AmiiboDecrypter(string keyRetailBinPath) public AmiiboDecryptor(string keyRetailBinPath)
{ {
var combinedKeys = File.ReadAllBytes(keyRetailBinPath); var combinedKeys = File.ReadAllBytes(keyRetailBinPath);
var keys = AmiiboMasterKey.FromCombinedBin(combinedKeys); var keys = AmiiboMasterKey.FromCombinedBin(combinedKeys);
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
public AmiiboDump DecryptAmiiboDump(byte[] encryptedDumpData) public AmiiboDump DecryptAmiiboDump(byte[] encryptedDumpData)
{ {
// Initialize AmiiboDump with encrypted data // 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 // Unlock (decrypt) the dump
amiiboDump.Unlock(); amiiboDump.Unlock();
@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
public AmiiboDump EncryptAmiiboDump(byte[] decryptedDumpData) public AmiiboDump EncryptAmiiboDump(byte[] decryptedDumpData)
{ {
// Initialize AmiiboDump with decrypted data // 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 // Lock (encrypt) the dump
amiiboDump.Lock(); amiiboDump.Lock();

View File

@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
private byte[] DeriveKey(AmiiboMasterKey key, bool deriveAes, out byte[] derivedAesKey, out byte[] derivedAesIv) private byte[] DeriveKey(AmiiboMasterKey key, bool deriveAes, out byte[] derivedAesKey, out byte[] derivedAesIv)
{ {
List<byte> seed = new List<byte>(); List<byte> seed = [];
// Start with the type string (14 bytes) // Start with the type string (14 bytes)
seed.AddRange(key.TypeString); seed.AddRange(key.TypeString);

View File

@ -33,10 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
byte[] dataBin = combinedBin.Take(80).ToArray(); byte[] dataBin = combinedBin.Take(80).ToArray();
byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray(); byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray();
AmiiboMasterKey dataKey = new AmiiboMasterKey(dataBin); return (new AmiiboMasterKey(dataBin), new AmiiboMasterKey(tagBin));
AmiiboMasterKey tagKey = new AmiiboMasterKey(tagBin);
return (dataKey, tagKey);
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
static class VirtualAmiibo static class VirtualAmiibo
{ {
public static uint OpenedApplicationAreaId; public static uint OpenedApplicationAreaId;
public static byte[] ApplicationBytes = new byte[0]; public static byte[] ApplicationBytes = Array.Empty<byte>();
public static string InputBin = string.Empty; public static string InputBin = string.Empty;
public static string NickName = string.Empty; public static string NickName = string.Empty;
private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default; private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default;
@ -137,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
if (ApplicationBytes.Length > 0) if (ApplicationBytes.Length > 0)
{ {
byte[] bytes = ApplicationBytes; byte[] bytes = ApplicationBytes;
ApplicationBytes = new byte[0]; ApplicationBytes = Array.Empty<byte>();
return bytes; return bytes;
} }
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);

View File

@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{ {
if (_services.TryGetValue(name, out Type type)) if (_services.TryGetValue(name, out Type type))
{ {
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name); ServiceAttribute serviceAttribute = type.GetCustomAttributes<ServiceAttribute>().First(service => service.Name == name);
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter); IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);

View File

@ -10,6 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
private ulong _value; private ulong _value;
private readonly EventFdFlags _flags; private readonly EventFdFlags _flags;
// type is not Lock due to Monitor class usage
private readonly object _lock = new(); private readonly object _lock = new();
public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); } public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); }

View File

@ -34,14 +34,14 @@ namespace Ryujinx.Horizon.Kernel.Generators
private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName; private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName;
private const string TypeExecutionContext = "IExecutionContext"; private const string TypeExecutionContext = "IExecutionContext";
private static readonly string[] _expectedResults = new string[] private static readonly string[] _expectedResults =
{ [
$"{TypeResultName}.Success", $"{TypeResultName}.Success",
$"{TypeKernelResultName}.TimedOut", $"{TypeKernelResultName}.TimedOut",
$"{TypeKernelResultName}.Cancelled", $"{TypeKernelResultName}.Cancelled",
$"{TypeKernelResultName}.PortRemoteClosed", $"{TypeKernelResultName}.PortRemoteClosed",
$"{TypeKernelResultName}.InvalidState", $"{TypeKernelResultName}.InvalidState",
}; ];
private readonly struct OutParameter private readonly struct OutParameter
{ {

View File

@ -106,10 +106,13 @@ namespace Ryujinx.Memory
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<HostMemoryRange>(); yield break;
} }
return GetHostRegionsImpl(va, size); foreach (var hostRegion in GetHostRegionsImpl(va, size))
{
yield return hostRegion;
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -117,51 +120,36 @@ namespace Ryujinx.Memory
{ {
if (size == 0) if (size == 0)
{ {
return Enumerable.Empty<MemoryRange>(); yield break;
} }
var hostRegions = GetHostRegionsImpl(va, size); var hostRegions = GetHostRegionsImpl(va, size);
if (hostRegions == null) if (hostRegions == null)
{ {
return null; yield break;
} }
var regions = new MemoryRange[hostRegions.Count];
ulong backingStart = (ulong)_backingMemory.Pointer; ulong backingStart = (ulong)_backingMemory.Pointer;
ulong backingEnd = backingStart + _backingMemory.Size; ulong backingEnd = backingStart + _backingMemory.Size;
int count = 0; foreach (var hostRegion in hostRegions)
for (int i = 0; i < regions.Length; i++)
{ {
var hostRegion = hostRegions[i];
if (hostRegion.Address >= backingStart && hostRegion.Address < backingEnd) 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<MemoryRange>(regions, 0, count);
}
return regions;
} }
private List<HostMemoryRange> GetHostRegionsImpl(ulong va, ulong size) private IEnumerable<HostMemoryRange> GetHostRegionsImpl(ulong va, ulong size)
{ {
if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size))
{ {
return null; yield break;
} }
int pages = GetPagesCount(va, size, out va); int pages = GetPagesCount(va, size, out va);
var regions = new List<HostMemoryRange>();
nuint regionStart = GetHostAddress(va); nuint regionStart = GetHostAddress(va);
ulong regionSize = PageSize; ulong regionSize = PageSize;
@ -169,14 +157,14 @@ namespace Ryujinx.Memory
{ {
if (!ValidateAddress(va + PageSize)) if (!ValidateAddress(va + PageSize))
{ {
return null; yield break;
} }
nuint newHostAddress = GetHostAddress(va + PageSize); nuint newHostAddress = GetHostAddress(va + PageSize);
if (GetHostAddress(va) + PageSize != newHostAddress) if (GetHostAddress(va) + PageSize != newHostAddress)
{ {
regions.Add(new HostMemoryRange(regionStart, regionSize)); yield return new HostMemoryRange(regionStart, regionSize);
regionStart = newHostAddress; regionStart = newHostAddress;
regionSize = 0; regionSize = 0;
} }
@ -185,9 +173,7 @@ namespace Ryujinx.Memory
regionSize += PageSize; regionSize += PageSize;
} }
regions.Add(new HostMemoryRange(regionStart, regionSize)); yield return new HostMemoryRange(regionStart, regionSize);
return regions;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@ -38,7 +38,7 @@ namespace Ryujinx.UI.App.Common
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); 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); public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);

View File

@ -23,7 +23,7 @@ namespace Ryujinx.UI.Common.Helper
[LibraryImport("shell32.dll", SetLastError = true)] [LibraryImport("shell32.dll", SetLastError = true)]
public static partial void SHChangeNotify(uint wEventId, uint uFlags, nint dwItem1, nint dwItem2); 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 public static bool AreMimeTypesRegistered
{ {

View File

@ -1,3 +1,4 @@
using Gommon;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using System; using System;
using System.Diagnostics; 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) public static void LocateFile(string path)
{ {
if (File.Exists(path)) if (File.Exists(path))

View File

@ -4,11 +4,10 @@
<ResourceDictionary x:Key="Default"> <ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" /> Color="{DynamicResource DataGridSelectionColor}" />
<Color x:Key="ControlFillColorSecondary">#008AA8</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color> <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color> <Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color>
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color> <Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color> <Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color> <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
@ -22,16 +21,19 @@
<ResourceDictionary x:Key="Light"> <ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
Color="{DynamicResource DataGridSelectionColor}" /> Color="{DynamicResource DataGridSelectionColor}" />
<Color x:Key="ControlFillColorSecondary">#3ddcff</Color>
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color> <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FFF0F0F0</Color> <Color x:Key="ThemeContentBackgroundColor">#dedede</Color>
<Color x:Key="ThemeControlBorderColor">#FFd6d6d6</Color> <Color x:Key="ThemeControlBorderColor">#c2c2c2</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color> <Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color> <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color> <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color> <Color x:Key="SecondaryTextColor">#A0000000</Color>
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
<Color x:Key="Switch">#13c3a4</Color>
<Color x:Key="Unbounded">#FFFF4554</Color>
<Color x:Key="Custom">#6483F5</Color>
</ResourceDictionary> </ResourceDictionary>
<ResourceDictionary x:Key="Dark"> <ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush" <SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
@ -40,13 +42,15 @@
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color> <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color> <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
<Color x:Key="ThemeControlBorderColor">#FF505050</Color> <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
<Color x:Key="TextOnAccentFillColorPrimary">#FFFFFFFF</Color>
<Color x:Key="SystemChromeWhiteColor">#FFFFFFFF</Color>
<Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color> <Color x:Key="ThemeForegroundColor">#FFFFFFFF</Color>
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color> <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color> <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color> <Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
<Color x:Key="FavoriteApplicationIconColor">#fffcd12a</Color>
<Color x:Key="Switch">#FF2EEAC9</Color>
<Color x:Key="Unbounded">#FFFF4554</Color>
<Color x:Key="Custom">#6483F5</Color>
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
</ResourceDictionary> </ResourceDictionary>

View File

@ -705,7 +705,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "Amiibo 스캔(빈에서)",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1077,6 +1077,54 @@
"zh_TW": "" "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", "ID": "MenuBarHelp",
"Translations": { "Translations": {
@ -1125,6 +1173,30 @@
"zh_TW": "檢查更新" "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", "ID": "MenuBarHelpFaq",
"Translations": { "Translations": {
@ -1137,7 +1209,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "자주 묻는 질문(FAQ) 및 문제해결 페이지",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1161,7 +1233,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "공식 Ryujinx 위키에서 자주 묻는 질문(FAQ) 및 문제 해결 페이지 열기",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1185,7 +1257,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "설치 및 구성 안내",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1209,7 +1281,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "공식 Ryujinx 위키에서 설정 및 구성 안내 열기",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1233,7 +1305,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "멀티플레이어(LDN/LAN) 안내",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -1257,7 +1329,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "공식 Ryujinx 위키에서 멀티플레이어 안내 열기",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -2424,25 +2496,25 @@
{ {
"ID": "StatusBarSystemVersion", "ID": "StatusBarSystemVersion",
"Translations": { "Translations": {
"ar_SA": "إصدار النظام: {0}", "ar_SA": "",
"de_DE": "Systemversion: {0}", "de_DE": "",
"el_GR": "Έκδοση Συστήματος: {0}", "el_GR": "",
"en_US": "System Version: {0}", "en_US": "Firmware Version: {0}",
"es_ES": "Versión del sistema: {0}", "es_ES": "",
"fr_FR": "Version du Firmware: {0}", "fr_FR": "Version du Firmware: {0}",
"he_IL": "גרסת מערכת: {0}", "he_IL": "",
"it_IT": "Versione di sistema: {0}", "it_IT": "",
"ja_JP": "システムバージョン: {0}", "ja_JP": "",
"ko_KR": "시스템 버전 : {0}", "ko_KR": "",
"no_NO": "System versjon: {0}", "no_NO": "",
"pl_PL": "Wersja systemu: {0}", "pl_PL": "",
"pt_BR": "Versão do firmware: {0}", "pt_BR": "Versão do firmware: {0}",
"ru_RU": "Версия прошивки: {0}", "ru_RU": "Версия прошивки: {0}",
"th_TH": "เวอร์ชั่นของระบบ: {0}", "th_TH": "",
"tr_TR": "Sistem Sürümü: {0}", "tr_TR": "",
"uk_UA": "Версія системи: {0}", "uk_UA": "",
"zh_CN": "系统固件版本:{0}", "zh_CN": "系统固件版本:{0}",
"zh_TW": "系統版本: {0}" "zh_TW": ""
} }
}, },
{ {
@ -3765,6 +3837,30 @@
"zh_TW": "系統時鐘:" "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", "ID": "SettingsTabSystemEnablePptc",
"Translations": { "Translations": {
@ -8049,7 +8145,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "지우기", "ko_KR": "",
"no_NO": "Tøm", "no_NO": "Tøm",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -11841,7 +11937,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "{0} : {1}",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -14541,6 +14637,30 @@
"zh_TW": "變更系統時鐘" "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", "ID": "VSyncToggleTooltip",
"Translations": { "Translations": {
@ -16125,30 +16245,6 @@
"zh_TW": "CPU 模式" "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", "ID": "UpdaterDisabledWarningTitle",
"Translations": { "Translations": {
@ -18753,7 +18849,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "{0:n0}MB",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -19041,7 +19137,7 @@
"he_IL": "{0} הרחבות משחק", "he_IL": "{0} הרחבות משחק",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "{0} DLC 사용 가능",
"no_NO": "{0} Nedlastbare innhold(er)", "no_NO": "{0} Nedlastbare innhold(er)",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21177,7 +21273,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "수직 동기화 :",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21201,7 +21297,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 활성화(실험적)",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21225,7 +21321,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "스위치",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21249,7 +21345,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "무제한",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21273,7 +21369,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21297,7 +21393,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "에뮬레이트된 수직 동기화. '스위치'는 스위치의 60Hz 주사율을 에뮬레이트합니다. '무한'은 무제한 주사율입니다.",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21321,7 +21417,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "에뮬레이트된 수직 동기화. '스위치'는 스위치의 60Hz 주사율을 에뮬레이트합니다. '무한'은 무제한 주사율입니다. '사용자 지정'은 지정된 사용자 지정 주사율을 에뮬레이트합니다.",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21345,7 +21441,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자가 에뮬레이트된 화면 주사율을 지정할 수 있습니다. 일부 타이틀에서는 게임플레이 로직 속도가 빨라지거나 느려질 수 있습니다. 다른 타이틀에서는 주사율의 배수로 FPS를 제한하거나 예측할 수 없는 동작으로 이어질 수 있습니다. 이는 실험적 기능으로 게임 플레이에 어떤 영향을 미칠지 보장할 수 없습니다. \n\n모르면 끔으로 두세요.",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21369,7 +21465,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 목표 값입니다.",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21393,7 +21489,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "일반 스위치 주사율의 백분율로 나타낸 사용자 지정 주사율입니다.",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21417,7 +21513,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 % :",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21441,7 +21537,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 값 :",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21465,7 +21561,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "간격",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21489,7 +21585,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "수직 동기화 모드 전환 :",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21513,7 +21609,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 증가",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@ -21537,7 +21633,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "사용자 정의 주사율 감소",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",

View File

@ -1,3 +1,4 @@
using Gommon;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@ -7,12 +8,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; 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.Json.Serialization;
using System.Text.Unicode;
namespace Ryujinx.Ava.Common.Locale namespace Ryujinx.Ava.Common.Locale
{ {
@ -147,39 +143,33 @@ namespace Ryujinx.Ava.Common.Locale
LocaleChanged?.Invoke(); LocaleChanged?.Invoke();
} }
#nullable enable
private static LocalesJson? _localeData;
#nullable disable
private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode) private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode)
{ {
var localeStrings = new Dictionary<LocaleKeys, string>(); var localeStrings = new Dictionary<LocaleKeys, string>();
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. if (locale.Translations.Count != _localeData.Value.Languages.Count)
return null;
}
LocalesJson json = JsonHelper.Deserialize(fileData, LocalesJsonContext.Default.LocalesJson);
foreach (LocalesEntry locale in json.Locales)
{
if (locale.Translations.Count != json.Languages.Count)
{ {
Logger.Error?.Print(LogClass.UI, $"Locale key {{{locale.ID}}} is missing languages!"); throw new Exception($"Locale key {{{locale.ID}}} is missing languages! Has {locale.Translations.Count} translations, expected {_localeData.Value.Languages.Count}!");
throw new Exception("Missing locale data!");
} }
if (Enum.TryParse<LocaleKeys>(locale.ID, out var localeKey)) if (!Enum.TryParse<LocaleKeys>(locale.ID, out var localeKey))
{ continue;
if (locale.Translations.TryGetValue(languageCode, out string val) && val != "")
{ localeStrings[localeKey] =
localeStrings[localeKey] = val; locale.Translations.TryGetValue(languageCode, out string val) && val != string.Empty
} ? val
else : locale.Translations[DefaultLanguageCode];
{
locale.Translations.TryGetValue("en_US", out val);
localeStrings[localeKey] = val;
}
}
} }
return localeStrings; return localeStrings;
@ -200,5 +190,5 @@ namespace Ryujinx.Ava.Common.Locale
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(LocalesJson))] [JsonSerializable(typeof(LocalesJson))]
internal partial class LocalesJsonContext : JsonSerializerContext { } internal partial class LocalesJsonContext : JsonSerializerContext;
} }

View File

@ -17,7 +17,6 @@
<MenuItem <MenuItem
Click="CreateApplicationShortcut_Click" Click="CreateApplicationShortcut_Click"
Header="{ext:Locale GameListContextMenuCreateShortcut}" Header="{ext:Locale GameListContextMenuCreateShortcut}"
IsEnabled="{Binding CreateShortcutEnabled}"
Icon="{ext:Icon fa-solid fa-bookmark}" Icon="{ext:Icon fa-solid fa-bookmark}"
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" /> ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
<Separator /> <Separator />

View File

@ -97,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
if (IsModified) if (IsModified)
{ {
_playerIdChoose = value; _playerIdChoose = value;
return; return;
} }
@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
IsModified = false; IsModified = false;
_playerId = value; _playerId = value;
if (!Enum.IsDefined(typeof(PlayerIndex), _playerId)) if (!Enum.IsDefined<PlayerIndex>(_playerId))
{ {
_playerId = PlayerIndex.Player1; _playerId = PlayerIndex.Player1;

View File

@ -127,6 +127,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public IEnumerable<LdnGameData> LastLdnGameData; public IEnumerable<LdnGameData> 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 = public static readonly Bitmap IconBitmap =
new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Thiccjinx.png")!); new(Assembly.GetAssembly(typeof(ConfigurationState))!.GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Thiccjinx.png")!);
@ -330,6 +333,9 @@ namespace Ryujinx.Ava.UI.ViewModels
OnPropertyChanged(); OnPropertyChanged();
} }
} }
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
public bool ShowLoadProgress public bool ShowLoadProgress
{ {
get => _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 OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
public string LoadHeading public string LoadHeading
{ {
get => _loadHeading; get => _loadHeading;
@ -2009,7 +2013,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
else else
{ {
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "NaN");
} }
IsAppletMenuActive = hasApplet; IsAppletMenuActive = hasApplet;

View File

@ -330,6 +330,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
public DateTimeOffset CurrentDate { get; set; } public DateTimeOffset CurrentDate { get; set; }
public TimeSpan CurrentTime { get; set; } public TimeSpan CurrentTime { get; set; }
internal AvaloniaList<TimeZone> TimeZones { get; set; } internal AvaloniaList<TimeZone> TimeZones { get; set; }
@ -453,6 +454,18 @@ namespace Ryujinx.Ava.UI.ViewModels
Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex))); 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() public async Task LoadTimeZones()
{ {
_timeZoneContentManager = new TimeZoneContentManager(); _timeZoneContentManager = new TimeZoneContentManager();

View File

@ -247,6 +247,7 @@
Click="OpenBinFile" Click="OpenBinFile"
Header="{ext:Locale MenuBarActionsScanAmiiboBin}" Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
Icon="{ext:Icon mdi-cube-scan}" Icon="{ext:Icon mdi-cube-scan}"
IsVisible="{Binding CanScanAmiiboBinaries}"
IsEnabled="{Binding IsAmiiboBinRequested}" /> IsEnabled="{Binding IsAmiiboBinRequested}" />
<MenuItem <MenuItem
Command="{Binding TakeScreenshot}" Command="{Binding TakeScreenshot}"
@ -286,9 +287,16 @@
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}">
<MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="1280 720" Click="ChangeWindowSize_Click" /> <MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="1280 720" Click="ChangeWindowSize_Click" />
<MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1920 1080" Click="ChangeWindowSize_Click" /> <MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1920 1080" Click="ChangeWindowSize_Click" />
<MenuItem Header="{ext:Locale MenuBarViewWindow1440}" Tag="2560 1440" Click="ChangeWindowSize_Click" />
<MenuItem Header="{ext:Locale MenuBarViewWindow2160}" Tag="3840 2160" Click="ChangeWindowSize_Click" />
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}"> <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">
<MenuItem
Click="OpenAboutWindow"
Header="{ext:Locale MenuBarHelpAbout}"
Icon="{ext:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
<MenuItem <MenuItem
Name="UpdateMenuItem" Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}" IsEnabled="{Binding CanUpdate}"
@ -297,30 +305,26 @@
Icon="{ext:Icon mdi-update}" Icon="{ext:Icon mdi-update}"
ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" /> ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" />
<Separator /> <Separator />
<MenuItem <MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelpFaqAndGuides}" Icon="{ext:Icon fa-solid fa-question}" >
Click="MenuItem_OnClick" <MenuItem
Header="{ext:Locale MenuBarHelpFaq}" Click="MenuItem_OnClick"
Icon="{ext:Icon fa-github}" Header="{ext:Locale MenuBarHelpFaq}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting" Icon="{ext:Icon fa-github}"
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" /> Tag="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting"
<MenuItem ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
Click="MenuItem_OnClick" <MenuItem
Header="{ext:Locale MenuBarHelpSetup}" Click="MenuItem_OnClick"
Icon="{ext:Icon fa-github}" Header="{ext:Locale MenuBarHelpSetup}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&amp;-Configuration-Guide" Icon="{ext:Icon fa-github}"
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" /> Tag="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&amp;-Configuration-Guide"
<MenuItem ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
Click="MenuItem_OnClick" <MenuItem
Header="{ext:Locale MenuBarHelpMultiplayer}" Click="MenuItem_OnClick"
Icon="{ext:Icon fa-github}" Header="{ext:Locale MenuBarHelpMultiplayer}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide" Icon="{ext:Icon fa-github}"
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" /> Tag="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide"
<Separator /> ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
<MenuItem </MenuItem>
Click="OpenAboutWindow"
Header="{ext:Locale MenuBarHelpAbout}"
Icon="{ext:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
</MenuItem> </MenuItem>
</Menu> </Menu>
</DockPanel> </DockPanel>

View File

@ -178,7 +178,7 @@ namespace Ryujinx.Ava.UI.Views.Main
private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{ {
if (sender is MenuItem) 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) private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)

View File

@ -52,7 +52,7 @@ namespace Ryujinx.Ava.UI.Views.Main
private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e) private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e)
{ {
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; 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<AspectRatio>().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
} }
private void Refresh_OnClick(object sender, RoutedEventArgs e) => Window.LoadApplications(); private void Refresh_OnClick(object sender, RoutedEventArgs e) => Window.LoadApplications();

View File

@ -181,8 +181,17 @@
SelectedTime="{Binding CurrentTime}" SelectedTime="{Binding CurrentTime}"
Width="350" Width="350"
ToolTip.Tip="{ext:Locale TimeTooltip}" /> ToolTip.Tip="{ext:Locale TimeTooltip}" />
<Button
Margin="10, 0, 0, 0"
VerticalAlignment="Center"
Click="MatchSystemTime_OnClick"
Background="{DynamicResource SystemAccentColor}"
ToolTip.Tip="{ext:Locale MatchTimeTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemSystemTimeMatch}" />
</Button>
</StackPanel> </StackPanel>
<StackPanel Margin="0,0,0,10" <Separator />
<StackPanel Margin="0,10,0,10"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@ -1,4 +1,6 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
@ -33,5 +35,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
ViewModel.ValidateAndSetTimeZone(timeZone.Location); ViewModel.ValidateAndSetTimeZone(timeZone.Location);
} }
} }
private void MatchSystemTime_OnClick(object sender, RoutedEventArgs e) => ViewModel.MatchSystemTime();
} }
} }

View File

@ -686,22 +686,11 @@ namespace Ryujinx.Ava
#else #else
if (showWarnings) if (showWarnings)
{ {
if (ReleaseInformation.IsFlatHubBuild) Dispatcher.UIThread.InvokeAsync(() =>
{ ContentDialogHelper.CreateWarningDialog(
Dispatcher.UIThread.InvokeAsync(() => LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
ContentDialogHelper.CreateWarningDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage])
); );
}
else
{
Dispatcher.UIThread.InvokeAsync(() =>
ContentDialogHelper.CreateWarningDialog(
LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle],
LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage])
);
}
} }
return false; return false;