Compare commits

...

10 Commits

Author SHA1 Message Date
Marco Carvalho
decd37ce6d Add missing "yield return" (#424) 2024-12-21 23:28:31 -06:00
Hack茶ん
67ec10feea Korean translation update (#422) 2024-12-21 22:46:57 -06:00
Evan Husted
4c7cb54ec6 misc: I may be stupid 2024-12-21 21:52:04 -06:00
Evan Husted
f898a5ecf4 Remove code references to having a flatpak version 2024-12-21 20:06:59 -06:00
Evan Husted
2fac0f4db1 Specify it's date & time 2024-12-21 20:00:16 -06:00
Evan Husted
0f18df982f UI: localize the button & make it smaller 2024-12-21 19:59:16 -06:00
Evan Husted
d9fe0da345 UI: Button to set emulator time based on system time in settings, under the time settings.
Partially resolves #355. I think that wanted automatic. If automatic functionality is still desired even with this change then that will be considered.
2024-12-21 19:43:40 -06:00
Evan Husted
1f0fa525a3 UI: some languages did already say Firmware version oddly enough 2024-12-21 19:03:08 -06:00
Evan Husted
e15a207656 misc: Improve broken locale.json crash message 2024-12-21 18:58:53 -06:00
Evan Husted
77ef82d92a misc: Cache LocalesJson when loading locale 2024-12-21 18:57:05 -06:00
19 changed files with 200 additions and 207 deletions

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

@@ -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

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

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

@@ -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

@@ -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

@@ -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": "",
@@ -1137,7 +1137,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 +1161,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 +1185,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 +1209,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 +1233,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 +1257,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": "",
@@ -2429,19 +2429,19 @@
"el_GR": "", "el_GR": "",
"en_US": "Firmware Version: {0}", "en_US": "Firmware Version: {0}",
"es_ES": "", "es_ES": "",
"fr_FR": "", "fr_FR": "Version du Firmware: {0}",
"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": "Versão do firmware: {0}",
"ru_RU": "", "ru_RU": "Версия прошивки: {0}",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "", "zh_CN": "系统固件版本:{0}",
"zh_TW": "" "zh_TW": ""
} }
}, },
@@ -3765,6 +3765,30 @@
"zh_TW": "系統時鐘:" "zh_TW": "系統時鐘:"
} }
}, },
{
"ID": "SettingsTabSystemSystemTimeMatch",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Match PC Time",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "SettingsTabSystemEnablePptc", "ID": "SettingsTabSystemEnablePptc",
"Translations": { "Translations": {
@@ -8049,7 +8073,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 +11865,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 +14565,30 @@
"zh_TW": "變更系統時鐘" "zh_TW": "變更系統時鐘"
} }
}, },
{
"ID": "MatchTimeTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Change System Time to match your PC's date & time.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "VSyncToggleTooltip", "ID": "VSyncToggleTooltip",
"Translations": { "Translations": {
@@ -16125,30 +16173,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 +18777,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 +19065,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 +21201,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 +21225,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 +21249,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 +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": "",
@@ -21273,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": "",
@@ -21297,7 +21321,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 +21345,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 +21369,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 +21393,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 +21417,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 +21441,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 +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": "",
@@ -21465,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": "",
@@ -21489,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": "",
@@ -21513,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": "",
@@ -21537,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": "",

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

@@ -424,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;

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

@@ -182,7 +182,20 @@
Width="350" Width="350"
ToolTip.Tip="{ext:Locale TimeTooltip}" /> ToolTip.Tip="{ext:Locale TimeTooltip}" />
</StackPanel> </StackPanel>
<StackPanel Margin="0,0,0,10" <StackPanel
Margin="350,0,0,10"
Orientation="Horizontal">
<Button
VerticalAlignment="Center"
Click="MatchSystemTime_OnClick"
Background="{DynamicResource SystemAccentColor}"
Width="150"
ToolTip.Tip="{ext:Locale MatchTimeTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemSystemTimeMatch}" />
</Button>
</StackPanel>
<Separator />
<StackPanel Margin="0,10,0,10"
Orientation="Horizontal"> Orientation="Horizontal">
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@@ -1,5 +1,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using System;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone; using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.Views.Settings namespace Ryujinx.Ava.UI.Views.Settings
@@ -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;