Compare commits
18 Commits
f9fe7ffc6b
...
Canary-1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
158ea7b4d6 | ||
|
|
8bc3de8303 | ||
|
|
c812106611 | ||
|
|
11e4d8f970 | ||
|
|
774edb7b29 | ||
|
|
55536f5d78 | ||
|
|
b2eecd28ce | ||
|
|
fe43c32e60 | ||
|
|
8117e160c2 | ||
|
|
bf713a80d6 | ||
|
|
b38b5a1e70 | ||
|
|
2d7700949c | ||
|
|
ea2287af03 | ||
|
|
37af8c70aa | ||
|
|
50cee3fd19 | ||
|
|
a46aacf2e2 | ||
|
|
ad9d6588e8 | ||
|
|
38ef65aae0 |
@@ -2483,7 +2483,7 @@
|
||||
0100A5200C2E0000,"Safety First!",,playable,2021-01-06 09:05:23
|
||||
0100A51013530000,"SaGa Frontier Remastered",nvdec,playable,2022-11-03 13:54:56
|
||||
010003A00D0B4000,"SaGa SCARLET GRACE: AMBITIONS™",,playable,2022-10-06 13:20:31
|
||||
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN,playable,2023-12-04 18:33:37
|
||||
01008D100D43E000,"Saints Row IV®: Re-Elected™",ldn-untested;LAN;deadlock,ingame,2025-02-02 16:57:53
|
||||
0100DE600BEEE000,"SAINTS ROW®: THE THIRD™ - THE FULL PACKAGE",slow;LAN,playable,2023-08-24 02:40:58
|
||||
01007F000EB36000,"Sakai and...",nvdec,playable,2022-12-15 13:53:19
|
||||
0100B1400E8FE000,"Sakuna: Of Rice and Ruin",,playable,2023-07-24 13:47:13
|
||||
|
||||
|
118
src/Ryujinx.Common/Helpers/Patterns.cs
Normal file
118
src/Ryujinx.Common/Helpers/Patterns.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ryujinx.Common.Helper
|
||||
{
|
||||
public static partial class Patterns
|
||||
{
|
||||
#region Accessors
|
||||
|
||||
public static readonly Regex Numeric = NumericRegex();
|
||||
|
||||
public static readonly Regex AmdGcn = AmdGcnRegex();
|
||||
public static readonly Regex NvidiaConsumerClass = NvidiaConsumerClassRegex();
|
||||
|
||||
public static readonly Regex DomainLp1Ns = DomainLp1NsRegex();
|
||||
public static readonly Regex DomainLp1Lp1Npln = DomainLp1Lp1NplnRegex();
|
||||
public static readonly Regex DomainLp1Znc = DomainLp1ZncRegex();
|
||||
public static readonly Regex DomainSbApi = DomainSbApiRegex();
|
||||
public static readonly Regex DomainSbAccounts = DomainSbAccountsRegex();
|
||||
public static readonly Regex DomainAccounts = DomainAccountsRegex();
|
||||
|
||||
public static readonly Regex Module = ModuleRegex();
|
||||
public static readonly Regex FsSdk = FsSdkRegex();
|
||||
public static readonly Regex SdkMw = SdkMwRegex();
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static readonly Regex CJK = CJKRegex();
|
||||
|
||||
public static readonly Regex LdnPassphrase = LdnPassphraseRegex();
|
||||
|
||||
public static readonly Regex CleanText = CleanTextRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Generated pattern stubs
|
||||
|
||||
#region Numeric validation
|
||||
|
||||
[GeneratedRegex("[0-9]|.")]
|
||||
internal static partial Regex NumericRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region GPU names
|
||||
|
||||
[GeneratedRegex(
|
||||
"Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||
internal static partial Regex AmdGcnRegex();
|
||||
|
||||
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
|
||||
internal static partial Regex NvidiaConsumerClassRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region DNS blocking
|
||||
|
||||
public static readonly Regex[] BlockedHosts =
|
||||
[
|
||||
DomainLp1Ns,
|
||||
DomainLp1Lp1Npln,
|
||||
DomainLp1Znc,
|
||||
DomainSbApi,
|
||||
DomainSbAccounts,
|
||||
DomainAccounts
|
||||
];
|
||||
|
||||
const RegexOptions DnsRegexOpts =
|
||||
RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainLp1NsRegex();
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainLp1Lp1NplnRegex();
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainLp1ZncRegex();
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainSbApiRegex();
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainSbAccountsRegex();
|
||||
|
||||
[GeneratedRegex(@"^accounts\.nintendo\.com$", DnsRegexOpts)]
|
||||
internal static partial Regex DomainAccountsRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Executable information
|
||||
|
||||
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
internal static partial Regex ModuleRegex();
|
||||
|
||||
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
|
||||
internal static partial Regex FsSdkRegex();
|
||||
|
||||
[GeneratedRegex(@"SDK MW[ -~]*")]
|
||||
internal static partial Regex SdkMwRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region CJK
|
||||
|
||||
[GeneratedRegex(
|
||||
"\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
|
||||
private static partial Regex CJKRegex();
|
||||
|
||||
#endregion
|
||||
|
||||
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
|
||||
private static partial Regex LdnPassphraseRegex();
|
||||
|
||||
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
|
||||
private static partial Regex CleanTextRegex();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,12 @@ namespace Ryujinx.Common
|
||||
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
||||
private const string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%";
|
||||
private const string ConfigFileName = "%%RYUJINX_CONFIG_FILE_NAME%%";
|
||||
private const string ConfigFileNameOverride = "%%RYUJINX_CONFIG_FILE_NAME_OVERRIDE%%";
|
||||
|
||||
public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%";
|
||||
public const string ReleaseChannelSourceRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO%%";
|
||||
public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%";
|
||||
|
||||
public static string ConfigName => !ConfigFileName.StartsWith("%%") ? ConfigFileName : "Config.json";
|
||||
public static string CustomConfigNameOverride => !ConfigFileNameOverride.StartsWith("%%") ? ConfigFileNameOverride : "CustomConfigOverride.json";
|
||||
|
||||
public static bool IsValid =>
|
||||
!BuildGitHash.StartsWith("%%") &&
|
||||
@@ -28,8 +26,7 @@ namespace Ryujinx.Common
|
||||
!ReleaseChannelOwner.StartsWith("%%") &&
|
||||
!ReleaseChannelSourceRepo.StartsWith("%%") &&
|
||||
!ReleaseChannelRepo.StartsWith("%%") &&
|
||||
!ConfigFileName.StartsWith("%%") &&
|
||||
!ConfigFileNameOverride.StartsWith("%%");
|
||||
!ConfigFileName.StartsWith("%%");
|
||||
|
||||
public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel);
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ namespace Ryujinx.Common
|
||||
"01006f8002326000", // Animal Crossings: New Horizons
|
||||
"01009bf0072d4000", // Captain Toad: Treasure Tracker
|
||||
"01009510001ca000", // Fast RMX
|
||||
"01005CA01580E000", // Persona 5 Royale
|
||||
"01005CA01580E000", // Persona 5 Royal
|
||||
"0100b880154fc000", // Persona 5 The Royal (Japan)
|
||||
"010015100b514000", // Super Mario Bros. Wonder
|
||||
"0100000000010000", // Super Mario Odyssey
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Gpu.Synchronization
|
||||
// TODO: Remove this when GPU channel scheduling will be implemented.
|
||||
if (timeout == Timeout.InfiniteTimeSpan)
|
||||
{
|
||||
timeout = TimeSpan.FromMilliseconds(500);
|
||||
timeout = TimeSpan.FromSeconds(1);
|
||||
}
|
||||
|
||||
using ManualResetEvent waitEvent = new(false);
|
||||
|
||||
@@ -16,14 +16,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Unknown,
|
||||
}
|
||||
|
||||
static partial class VendorUtils
|
||||
static class VendorUtils
|
||||
{
|
||||
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||
public static partial Regex AmdGcnRegex();
|
||||
|
||||
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
|
||||
public static partial Regex NvidiaConsumerClassRegex();
|
||||
|
||||
public static Vendor FromId(uint id)
|
||||
{
|
||||
return id switch
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
@@ -375,11 +376,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
|
||||
|
||||
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
|
||||
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && Patterns.AmdGcn.IsMatch(GpuRenderer);
|
||||
|
||||
if (Vendor == Vendor.Nvidia)
|
||||
{
|
||||
Match match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
|
||||
Match match = Patterns.NvidiaConsumerClass.Match(GpuRenderer);
|
||||
|
||||
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
@@ -30,9 +31,6 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
||||
|
||||
public event EventHandler AppletStateChanged;
|
||||
|
||||
[GeneratedRegex(@"[^\u0000\u0009\u000A\u000D\u0020-\uFFFF]..")]
|
||||
private static partial Regex CleanTextRegex();
|
||||
|
||||
public ErrorApplet(Horizon horizon)
|
||||
{
|
||||
_horizon = horizon;
|
||||
@@ -107,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
||||
|
||||
private static string CleanText(string value)
|
||||
{
|
||||
return CleanTextRegex().Replace(value, string.Empty).Replace("\0", string.Empty);
|
||||
return Patterns.CleanText.Replace(value, string.Empty).Replace("\0", string.Empty);
|
||||
}
|
||||
|
||||
private string GetMessageText(uint module, uint description, string key)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
public static partial class CJKCharacterValidation
|
||||
{
|
||||
public static bool IsCJK(char value)
|
||||
{
|
||||
Regex regex = CJKRegex();
|
||||
|
||||
return regex.IsMatch(value.ToString());
|
||||
}
|
||||
|
||||
[GeneratedRegex("\\p{IsHangulJamo}|\\p{IsCJKRadicalsSupplement}|\\p{IsCJKSymbolsandPunctuation}|\\p{IsEnclosedCJKLettersandMonths}|\\p{IsCJKCompatibility}|\\p{IsCJKUnifiedIdeographsExtensionA}|\\p{IsCJKUnifiedIdeographs}|\\p{IsHangulSyllables}|\\p{IsCJKCompatibilityForms}")]
|
||||
private static partial Regex CJKRegex();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Ryujinx.Common.Helper;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
public static class CharacterValidation
|
||||
{
|
||||
public static bool IsNumeric(char value) => Patterns.Numeric.IsMatch(value.ToString());
|
||||
public static bool IsCJK(char value) => Patterns.CJK.IsMatch(value.ToString());
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
public static partial class NumericCharacterValidation
|
||||
{
|
||||
public static bool IsNumeric(char value)
|
||||
{
|
||||
Regex regex = NumericRegex();
|
||||
|
||||
return regex.IsMatch(value.ToString());
|
||||
}
|
||||
|
||||
[GeneratedRegex("[0-9]|.")]
|
||||
private static partial Regex NumericRegex();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,13 @@
|
||||
using Ryujinx.Common.Helper;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
|
||||
{
|
||||
static partial class DnsBlacklist
|
||||
static class DnsBlacklist
|
||||
{
|
||||
const RegexOptions RegexOpts = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
|
||||
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.(n|s)\.n\.srv\.nintendo\.net$", RegexOpts)]
|
||||
private static partial Regex BlockedHost1();
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.lp1\.t\.npln\.srv\.nintendo\.net$", RegexOpts)]
|
||||
private static partial Regex BlockedHost2();
|
||||
[GeneratedRegex(@"^(.*)\-lp1\.(znc|p)\.srv\.nintendo\.net$", RegexOpts)]
|
||||
private static partial Regex BlockedHost3();
|
||||
[GeneratedRegex(@"^(.*)\-sb\-api\.accounts\.nintendo\.com$", RegexOpts)]
|
||||
private static partial Regex BlockedHost4();
|
||||
[GeneratedRegex(@"^(.*)\-sb\.accounts\.nintendo\.com$", RegexOpts)]
|
||||
private static partial Regex BlockedHost5();
|
||||
[GeneratedRegex(@"^accounts\.nintendo\.com$", RegexOpts)]
|
||||
private static partial Regex BlockedHost6();
|
||||
|
||||
private static readonly Regex[] _blockedHosts =
|
||||
[
|
||||
BlockedHost1(),
|
||||
BlockedHost2(),
|
||||
BlockedHost3(),
|
||||
BlockedHost4(),
|
||||
BlockedHost5(),
|
||||
BlockedHost6()
|
||||
];
|
||||
|
||||
public static bool IsHostBlocked(string host)
|
||||
{
|
||||
foreach (Regex regex in _blockedHosts)
|
||||
foreach (Regex regex in Patterns.BlockedHosts)
|
||||
{
|
||||
if (regex.IsMatch(host))
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using LibHac.Common.FixedArrays;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Loader;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Text;
|
||||
@@ -29,13 +30,6 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||
public string Name;
|
||||
public Array32<byte> BuildId;
|
||||
|
||||
[GeneratedRegex(@"[a-z]:[\\/][ -~]{5,}\.nss", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ModuleRegex();
|
||||
[GeneratedRegex(@"sdk_version: ([0-9.]*)")]
|
||||
private static partial Regex FsSdkRegex();
|
||||
[GeneratedRegex(@"SDK MW[ -~]*")]
|
||||
private static partial Regex SdkMwRegex();
|
||||
|
||||
public NsoExecutable(IStorage inStorage, string name = null)
|
||||
{
|
||||
NsoReader reader = new();
|
||||
@@ -90,7 +84,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||
|
||||
if (string.IsNullOrEmpty(modulePath))
|
||||
{
|
||||
Match moduleMatch = ModuleRegex().Match(rawTextBuffer);
|
||||
Match moduleMatch = Patterns.Module.Match(rawTextBuffer);
|
||||
if (moduleMatch.Success)
|
||||
{
|
||||
modulePath = moduleMatch.Value;
|
||||
@@ -99,13 +93,13 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||
|
||||
stringBuilder.AppendLine($" Module: {modulePath}");
|
||||
|
||||
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
|
||||
Match fsSdkMatch = Patterns.FsSdk.Match(rawTextBuffer);
|
||||
if (fsSdkMatch.Success)
|
||||
{
|
||||
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", string.Empty)}");
|
||||
}
|
||||
|
||||
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
|
||||
MatchCollection sdkMwMatches = Patterns.SdkMw.Matches(rawTextBuffer);
|
||||
if (sdkMwMatches.Count != 0)
|
||||
{
|
||||
string libHeader = " SDK Libraries: ";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using MsgPack;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
@@ -6,6 +7,10 @@ namespace Ryujinx.Horizon
|
||||
{
|
||||
public static class HorizonStatic
|
||||
{
|
||||
internal static void HandlePlayReport(MessagePackObject report) => PlayReportPrinted?.Invoke(report);
|
||||
|
||||
public static event Action<MessagePackObject> PlayReportPrinted;
|
||||
|
||||
[ThreadStatic]
|
||||
private static HorizonOptions _options;
|
||||
|
||||
|
||||
@@ -230,6 +230,8 @@ namespace Ryujinx.Horizon.Prepo.Ipc
|
||||
|
||||
builder.AppendLine($" Room: {gameRoom}");
|
||||
builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
|
||||
|
||||
HorizonStatic.HandlePlayReport(deserializedReport);
|
||||
|
||||
Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
|
||||
|
||||
|
||||
@@ -938,7 +938,9 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||
ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.MatchSystemTime
|
||||
? 0
|
||||
: ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.TimeZone,
|
||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||
|
||||
@@ -4153,23 +4153,23 @@
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "Resync to PC Date & Time",
|
||||
"en_US": "Match System Time",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Resynchronier la Date à celle du PC",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "Sincronizza data e ora con il PC",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "PC 날짜와 시간에 동기화",
|
||||
"no_NO": "Resynkroniser til PC-dato og -klokkeslett",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Повторная синхронизация с датой и временем на компьютере",
|
||||
"sv_SE": "Återsynka till datorns datum och tid",
|
||||
"ru_RU": "",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Синхронізувати з датою та часом ПК",
|
||||
"zh_CN": "与 PC 日期和时间重新同步",
|
||||
"zh_TW": "重新同步至 PC 的日期和時間"
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -15553,23 +15553,23 @@
|
||||
"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.",
|
||||
"en_US": "Sync System Time to match your PC's current date & time.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Resynchronise la Date du Système pour qu'elle soit la même que celle du PC.\n\nCeci n'est pas un paramètrage automatique, la date peut se désynchroniser; dans ce cas là, rappuyer sur le boutton.",
|
||||
"fr_FR": "Resynchronise la Date du Système pour qu'elle soit la même que celle du PC.",
|
||||
"he_IL": "",
|
||||
"it_IT": "Sincronizza data e ora del sistema con quelle del PC.\n\nQuesta non è un'opzione attiva, perciò data e ora potrebbero tornare a non essere sincronizzate: in tal caso basterà cliccare nuovamente questo pulsante.",
|
||||
"it_IT": "Sincronizza data e ora del sistema con quelle del PC.",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.\n\n이 설정은 활성 설정이 아니므로 여전히 동기화되지 않을 수 있으며, 이 경우 이 버튼을 다시 클릭하면 됩니다.",
|
||||
"no_NO": "Resynkroniser systemtiden slik at den samsvarer med PC-ens gjeldende dato og klokkeslett. \\Dette er ikke en aktiv innstilling, men den kan likevel komme ut av synkronisering; i så fall er det bare å klikke på denne knappen igjen.",
|
||||
"ko_KR": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.",
|
||||
"no_NO": "Resynkroniser systemtiden slik at den samsvarer med PC-ens gjeldende dato og klokkeslett.",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Повторно синхронизирует системное время, чтобы оно соответствовало текущей дате и времени вашего компьютера.\n\nЭто не активная настройка, она все еще может рассинхронизироваться; в этом случае просто нажмите эту кнопку еще раз.",
|
||||
"sv_SE": "Återsynkronisera systemtiden för att matcha din dators aktuella datum och tid.\n\nDetta är inte en aktiv inställning och den kan tappa synken och om det händer så kan du klicka på denna knapp igen.",
|
||||
"ru_RU": "Повторно синхронизирует системное время, чтобы оно соответствовало текущей дате и времени вашего компьютера.",
|
||||
"sv_SE": "Återsynkronisera systemtiden för att matcha din dators aktuella datum och tid.",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "Синхронізувати системний час, щоб він відповідав поточній даті та часу вашого ПК.\n\nЦе не активне налаштування, тому синхронізація може збитися; у такому разі просто натискайте цю кнопку знову.",
|
||||
"zh_CN": "重新同步系统时间以匹配您电脑的当前日期和时间。\n\n这个操作不会实时同步系统时间与电脑时间,时间仍然可能不同步;在这种情况下,只需再次单击此按钮即可。",
|
||||
"zh_TW": "重新同步系統韌體時間至 PC 目前的日期和時間。\n\n這不是一個主動設定,它仍然可能會失去同步;在這種情況下,只需再次點擊此按鈕。"
|
||||
"uk_UA": "Синхронізувати системний час, щоб він відповідав поточній даті та часу вашого ПК.",
|
||||
"zh_CN": "重新同步系统时间以匹配您电脑的当前日期和时间。",
|
||||
"zh_TW": "重新同步系統韌體時間至 PC 目前的日期和時間。"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23069,7 +23069,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "可游玩",
|
||||
"zh_TW": "可暢順遊玩 (Playable)"
|
||||
"zh_TW": "可暢順遊玩"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23094,7 +23094,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "进入游戏",
|
||||
"zh_TW": "大致可遊玩 (Ingame)"
|
||||
"zh_TW": "大致可遊玩"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23119,7 +23119,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "菜单",
|
||||
"zh_TW": "只開啟至遊戲開始功能表 (Menus)"
|
||||
"zh_TW": "只開啟至遊戲開始功能表"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23144,7 +23144,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "启动",
|
||||
"zh_TW": "只能啟動 (Boots)"
|
||||
"zh_TW": "只能啟動"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23169,7 +23169,7 @@
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "什么都没有",
|
||||
"zh_TW": "無法啟動 (Nothing)"
|
||||
"zh_TW": "無法啟動"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
using DiscordRPC;
|
||||
using Gommon;
|
||||
using MsgPack;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Ava
|
||||
@@ -16,12 +24,12 @@ namespace Ryujinx.Ava
|
||||
public static Timestamps GuestAppStartedAt { get; set; }
|
||||
|
||||
private static string VersionString
|
||||
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
|
||||
=> (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}";
|
||||
|
||||
private static readonly string _description =
|
||||
ReleaseInformation.IsValid
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
||||
: "dev build";
|
||||
private static readonly string _description =
|
||||
ReleaseInformation.IsValid
|
||||
? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}"
|
||||
: "dev build";
|
||||
|
||||
private const string ApplicationId = "1293250299716173864";
|
||||
|
||||
@@ -30,6 +38,8 @@ namespace Ryujinx.Ava
|
||||
|
||||
private static DiscordRpcClient _discordClient;
|
||||
private static RichPresence _discordPresenceMain;
|
||||
private static RichPresence _discordPresencePlaying;
|
||||
private static ApplicationMetadata _currentApp;
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
@@ -37,8 +47,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = "ryujinx",
|
||||
LargeImageText = TruncateToByteLength(_description)
|
||||
LargeImageKey = "ryujinx", LargeImageText = TruncateToByteLength(_description)
|
||||
},
|
||||
Details = "Main Menu",
|
||||
State = "Idling",
|
||||
@@ -47,6 +56,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
||||
HorizonStatic.PlayReportPrinted += HandlePlayReport;
|
||||
}
|
||||
|
||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
||||
@@ -77,16 +87,15 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
if (titleId.TryGet(out string tid))
|
||||
SwitchToPlayingState(
|
||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||
Switch.Shared.Processes.ActiveApplication
|
||||
);
|
||||
else
|
||||
else
|
||||
SwitchToMainState();
|
||||
}
|
||||
|
||||
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
||||
{
|
||||
_discordClient?.SetPresence(new RichPresence
|
||||
private static RichPresence CreatePlayingState(ApplicationMetadata appMeta, ProcessResult procRes) =>
|
||||
new()
|
||||
{
|
||||
Assets = new Assets
|
||||
{
|
||||
@@ -100,10 +109,48 @@ namespace Ryujinx.Ava
|
||||
? $"Total play time: {ValueFormatUtils.FormatTimeSpan(appMeta.TimePlayed)}"
|
||||
: "Never played",
|
||||
Timestamps = GuestAppStartedAt ??= Timestamps.Now
|
||||
});
|
||||
};
|
||||
|
||||
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
||||
{
|
||||
_discordClient?.SetPresence(_discordPresencePlaying ??= CreatePlayingState(appMeta, procRes));
|
||||
_currentApp = appMeta;
|
||||
}
|
||||
|
||||
private static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain);
|
||||
private static void UpdatePlayingState()
|
||||
{
|
||||
_discordClient?.SetPresence(_discordPresencePlaying);
|
||||
}
|
||||
|
||||
private static void SwitchToMainState()
|
||||
{
|
||||
_discordClient?.SetPresence(_discordPresenceMain);
|
||||
_discordPresencePlaying = null;
|
||||
_currentApp = null;
|
||||
}
|
||||
|
||||
private static void HandlePlayReport(MessagePackObject playReport)
|
||||
{
|
||||
if (_discordClient is null) return;
|
||||
if (!TitleIDs.CurrentApplication.Value.HasValue) return;
|
||||
if (_discordPresencePlaying is null) return;
|
||||
|
||||
PlayReportFormattedValue value = PlayReport.Analyzer.Run(TitleIDs.CurrentApplication.Value, _currentApp, playReport);
|
||||
|
||||
if (!value.Handled) return;
|
||||
|
||||
if (value.Reset)
|
||||
{
|
||||
_discordPresencePlaying.Details = $"Playing {_currentApp.Title}";
|
||||
Logger.Info?.Print(LogClass.UI, "Reset Discord RPC based on a supported play report value formatter.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_discordPresencePlaying.Details = value.FormattedString;
|
||||
Logger.Info?.Print(LogClass.UI, "Updated Discord RPC based on a supported play report.");
|
||||
}
|
||||
UpdatePlayingState();
|
||||
}
|
||||
|
||||
private static string TruncateToByteLength(string input)
|
||||
{
|
||||
|
||||
@@ -160,28 +160,6 @@ namespace Ryujinx.Ava
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
|
||||
string overrideLocalConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.CustomConfigNameOverride);
|
||||
string overrideAppDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.CustomConfigNameOverride);
|
||||
|
||||
// Copies and reloads the configuration file if the game was loaded with arguments
|
||||
// based on global configuration
|
||||
if (CommandLineState.CountArguments > 0)
|
||||
{
|
||||
if (File.Exists(localConfigurationPath))
|
||||
{
|
||||
File.Copy(localConfigurationPath, overrideLocalConfigurationPath, overwrite: true);
|
||||
}
|
||||
|
||||
localConfigurationPath = overrideLocalConfigurationPath;
|
||||
|
||||
if (File.Exists(appDataConfigurationPath))
|
||||
{
|
||||
File.Copy(appDataConfigurationPath, overrideAppDataConfigurationPath, overwrite: true);
|
||||
}
|
||||
|
||||
appDataConfigurationPath = overrideAppDataConfigurationPath;
|
||||
}
|
||||
|
||||
// Now load the configuration as the other subsystems are now registered
|
||||
if (File.Exists(localConfigurationPath))
|
||||
{
|
||||
@@ -254,35 +232,8 @@ namespace Ryujinx.Ava
|
||||
_ => ConfigurationState.Instance.HideCursor,
|
||||
};
|
||||
|
||||
// Check if memoryManagerMode was overridden.
|
||||
if (CommandLineState.OverrideMemoryManagerMode is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
|
||||
{
|
||||
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
|
||||
}
|
||||
|
||||
// Check if PPTC was overridden.
|
||||
if (CommandLineState.OverridePPTC is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
|
||||
{
|
||||
ConfigurationState.Instance.System.EnablePtc.Value = result;
|
||||
}
|
||||
|
||||
// Check if region was overridden.
|
||||
if (CommandLineState.OverrideSystemRegion is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Ryujinx.HLE.HOS.SystemState.RegionCode result))
|
||||
{
|
||||
ConfigurationState.Instance.System.Region.Value = (Utilities.Configuration.System.Region)result;
|
||||
}
|
||||
|
||||
//Check if language was overridden.
|
||||
if (CommandLineState.OverrideSystemLanguage is not null)
|
||||
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Ryujinx.HLE.HOS.SystemState.SystemLanguage result))
|
||||
{
|
||||
ConfigurationState.Instance.System.Language.Value = (Utilities.Configuration.System.Language)result;
|
||||
}
|
||||
|
||||
// Check if hardware-acceleration was overridden. MemoryManagerMode ( outdated! )
|
||||
// Check if hardware-acceleration was overridden.
|
||||
if (CommandLineState.OverrideHardwareAcceleration != null)
|
||||
UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value;
|
||||
}
|
||||
|
||||
@@ -144,12 +144,12 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
case KeyboardMode.Numeric:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumeric);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(NumericCharacterValidation.IsNumeric);
|
||||
_checkInput = text => text.All(CharacterValidation.IsNumeric);
|
||||
break;
|
||||
case KeyboardMode.Alphabet:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value));
|
||||
_checkInput = text => text.All(value => !CharacterValidation.IsCJK(value));
|
||||
break;
|
||||
case KeyboardMode.ASCII:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII);
|
||||
|
||||
@@ -375,11 +375,15 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
png.SaveTo(fileStream);
|
||||
}
|
||||
|
||||
public async void CreateApplicationShortcut_Click(object sender, RoutedEventArgs args)
|
||||
public void CreateApplicationShortcut_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
await new ArgumentsConfigWindows(viewModel).ShowDialog((Window)viewModel.TopLevel);
|
||||
|
||||
ShortcutHelper.CreateAppShortcut(
|
||||
viewModel.SelectedApplication.Path,
|
||||
viewModel.SelectedApplication.Name,
|
||||
viewModel.SelectedApplication.IdString,
|
||||
viewModel.SelectedApplication.Icon
|
||||
);
|
||||
}
|
||||
|
||||
public async void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||
|
||||
@@ -86,6 +86,13 @@
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
IsVisible="{Binding HasPlayabilityInfo}"
|
||||
HorizontalAlignment="Stretch"
|
||||
Text="{Binding LocalizedStatus}"
|
||||
Foreground="{Binding PlayabilityStatus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
TextAlignment="Start"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Gommon;
|
||||
using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.OpenAL;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
@@ -18,6 +16,7 @@ using Ryujinx.Ava.Utilities.Configuration.System;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Multiplayer;
|
||||
using Ryujinx.Common.GraphicsDriver;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
@@ -27,7 +26,6 @@ using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -65,56 +63,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public event Action CloseWindow;
|
||||
public event Action SaveSettingsEvent;
|
||||
public event Action CompareSettingsEvent;
|
||||
private int _networkInterfaceIndex;
|
||||
private int _multiplayerModeIndex;
|
||||
private string _ldnPassphrase;
|
||||
[ObservableProperty] private string _ldnServer;
|
||||
|
||||
public SettingsHacksViewModel DirtyHacks { get; }
|
||||
public string GamePath { get; }
|
||||
public string GameName { get; }
|
||||
|
||||
private Bitmap _gameIcon;
|
||||
|
||||
private string _gameTitle;
|
||||
private string _gameId;
|
||||
public Bitmap GameIcon
|
||||
{
|
||||
get => _gameIcon;
|
||||
set
|
||||
{
|
||||
if (_gameIcon != value)
|
||||
{
|
||||
_gameIcon = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GameTitle
|
||||
{
|
||||
get => _gameTitle;
|
||||
set
|
||||
{
|
||||
if (_gameTitle != value)
|
||||
{
|
||||
_gameTitle = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GameId
|
||||
{
|
||||
get => _gameId;
|
||||
set
|
||||
{
|
||||
if (_gameId != value)
|
||||
{
|
||||
_gameId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
@@ -162,10 +116,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
|
||||
|
||||
public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
|
||||
|
||||
public bool IsMacOS => OperatingSystem.IsMacOS();
|
||||
|
||||
public bool EnableDiscordIntegration { get; set; }
|
||||
public bool CheckUpdatesOnStart { get; set; }
|
||||
public bool ShowConfirmExit { get; set; }
|
||||
@@ -247,7 +197,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableTextureRecompression { get; set; }
|
||||
public bool EnableMacroHLE { get; set; }
|
||||
public bool EnableColorSpacePassthrough { get; set; }
|
||||
public bool ColorSpacePassthroughAvailable => IsMacOS;
|
||||
public bool ColorSpacePassthroughAvailable => RunningPlatform.IsMacOS;
|
||||
public bool EnableFileLog { get; set; }
|
||||
public bool EnableStub { get; set; }
|
||||
public bool EnableInfo { get; set; }
|
||||
@@ -343,6 +293,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[ObservableProperty] private bool _matchSystemTime;
|
||||
|
||||
public DateTimeOffset CurrentDate { get; set; }
|
||||
|
||||
public TimeSpan CurrentTime { get; set; }
|
||||
@@ -377,9 +329,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
[GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
|
||||
private static partial Regex LdnPassphraseRegex();
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
@@ -395,30 +344,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, string gamePath, string gameName, string gameId, byte[] gameIconData) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
|
||||
if (gameIconData != null && gameIconData.Length > 0)
|
||||
{
|
||||
using (var ms = new MemoryStream(gameIconData))
|
||||
{
|
||||
GameIcon = new Bitmap(ms);
|
||||
}
|
||||
}
|
||||
|
||||
GameTitle = gameName;
|
||||
GameId = gameId;
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
Task.Run(LoadTimeZones);
|
||||
|
||||
DirtyHacks = new SettingsHacksViewModel(this);
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel()
|
||||
{
|
||||
GameDirectories = [];
|
||||
@@ -485,17 +410,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex)));
|
||||
}
|
||||
|
||||
public void MatchSystemTime()
|
||||
{
|
||||
(DateTimeOffset dto, TimeSpan timeOfDay) = DateTimeOffset.Now.Extract();
|
||||
|
||||
CurrentDate = dto;
|
||||
CurrentTime = timeOfDay;
|
||||
|
||||
OnPropertyChanged(nameof(CurrentDate));
|
||||
OnPropertyChanged(nameof(CurrentTime));
|
||||
}
|
||||
|
||||
public async Task LoadTimeZones()
|
||||
{
|
||||
_timeZoneContentManager = new TimeZoneContentManager();
|
||||
@@ -541,7 +455,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private bool ValidateLdnPassphrase(string passphrase)
|
||||
{
|
||||
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && LdnPassphraseRegex().IsMatch(passphrase));
|
||||
return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && Patterns.LdnPassphrase.IsMatch(passphrase));
|
||||
}
|
||||
|
||||
public void ValidateAndSetTimeZone(string location)
|
||||
@@ -597,7 +511,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
CurrentDate = currentDateTime.Date;
|
||||
CurrentTime = currentDateTime.TimeOfDay;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval.Value;
|
||||
MatchSystemTime = config.System.MatchSystemTime;
|
||||
|
||||
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval;
|
||||
CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
|
||||
VSyncMode = config.Graphics.VSyncMode;
|
||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||
@@ -702,6 +618,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
config.System.TimeZone.Value = TimeZone;
|
||||
}
|
||||
|
||||
config.System.MatchSystemTime.Value = MatchSystemTime;
|
||||
config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
|
||||
config.System.DramSize.Value = DramSize;
|
||||
@@ -799,11 +716,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
public void CreateShortcut()
|
||||
{
|
||||
CompareSettingsEvent?.Invoke(); //raises an event to create a shortcut with arguments
|
||||
}
|
||||
|
||||
public void OkButton()
|
||||
{
|
||||
SaveSettings();
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsApplyOverride"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<ScrollViewer
|
||||
Name="AllSettings"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<Border Classes="settings">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabSystemCore}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center" Text="{ext:Locale SettingsTabSystemSystemRegion}" Width="250" />
|
||||
<ComboBox
|
||||
SelectedIndex="{Binding Region}"
|
||||
ToolTip.Tip="{ext:Locale RegionTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionJapan}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionUSA}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionEurope}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionAustralia}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionChina}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionKorea}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemRegionTaiwan}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabSystemSystemLanguage}"
|
||||
ToolTip.Tip="{ext:Locale LanguageTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox
|
||||
SelectedIndex="{Binding Language}"
|
||||
ToolTip.Tip="{ext:Locale LanguageTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageJapanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageGerman}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageItalian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageKorean}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageDutch}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguagePortuguese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageRussian}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageSwedish}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemLanguageNorwegian}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabCpu}" />
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{Binding EnablePptc}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemEnablePptc}"
|
||||
ToolTip.Tip="{ext:Locale PptcToggleTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
Text="{ext:Locale SettingsTabSystemMemoryManagerMode}"
|
||||
ToolTip.Tip="{ext:Locale MemoryManagerTooltip}"
|
||||
Width="250" />
|
||||
<ComboBox SelectedIndex="{Binding MemoryMode}"
|
||||
ToolTip.Tip="{ext:Locale MemoryManagerTooltip}"
|
||||
HorizontalContentAlignment="Left"
|
||||
Width="350">
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{ext:Locale MemoryManagerSoftwareTooltip}">
|
||||
<TextBlock
|
||||
Text="{ext:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{ext:Locale MemoryManagerHostTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemMemoryManagerModeHost}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem
|
||||
ToolTip.Tip="{ext:Locale MemoryManagerUnsafeTooltip}">
|
||||
<TextBlock
|
||||
Text="{ext:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding UseHypervisor}"
|
||||
IsVisible="{Binding IsAppleSiliconMac}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemUseHypervisor}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGraphics}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
Text="{ext:Locale SettingsTabGraphicsBackend}"
|
||||
Width="250" />
|
||||
<ComboBox
|
||||
Name="GraphicsBackendSelector"
|
||||
Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendIndex}">
|
||||
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}" ToolTip.Tip="{ext:Locale SettingsTabGraphicsBackendAutoTooltip}" >
|
||||
<TextBlock Text="{ext:Locale SettingsTabGraphicsBackendAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
|
||||
<TextBlock Text="Vulkan" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
|
||||
<TextBlock Text="OpenGL" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsAppleSiliconMac}">
|
||||
<TextBlock Text="Metal (ARM Mac only, Experimental)" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale GraphicsBackendThreadingTooltip}"
|
||||
Text="{ext:Locale SettingsTabGraphicsBackendMultithreading}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{ext:Locale GalThreadingTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOn}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
@@ -1,12 +0,0 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Settings
|
||||
{
|
||||
public partial class SettingsApplyOverride : UserControl
|
||||
{
|
||||
public SettingsApplyOverride()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
<Design.DataContext>
|
||||
@@ -69,7 +70,7 @@
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
<CheckBox IsChecked="{Binding UseHypervisor}"
|
||||
IsVisible="{Binding IsAppleSiliconMac}"
|
||||
IsVisible="{x:Static helper:RunningPlatform.IsArmMac}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemUseHypervisor}"
|
||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
Design.Width="1000"
|
||||
mc:Ignorable="d"
|
||||
x:DataType="viewModels:SettingsViewModel">
|
||||
@@ -48,7 +49,7 @@
|
||||
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
|
||||
<TextBlock Text="OpenGL" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem IsEnabled="{Binding IsAppleSiliconMac}">
|
||||
<ComboBoxItem IsEnabled="{x:Static helper:RunningPlatform.IsArmMac}">
|
||||
<TextBlock Text="Metal (ARM Mac only, Experimental)" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
|
||||
@@ -170,7 +170,8 @@
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}"
|
||||
Width="250"/>
|
||||
<DatePicker
|
||||
VerticalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{Binding !MatchSystemTime}"
|
||||
SelectedDate="{Binding CurrentDate}"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}"
|
||||
Width="350" />
|
||||
@@ -181,17 +182,21 @@
|
||||
<TimePicker
|
||||
VerticalAlignment="Center"
|
||||
ClockIdentifier="24HourClock"
|
||||
IsEnabled="{Binding !MatchSystemTime}"
|
||||
SelectedTime="{Binding CurrentTime}"
|
||||
Width="350"
|
||||
ToolTip.Tip="{ext:Locale TimeTooltip}" />
|
||||
<Button
|
||||
Margin="10, 0, 0, 0"
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Click="MatchSystemTime_OnClick"
|
||||
Background="{DynamicResource SystemAccentColor}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}">
|
||||
<TextBlock Text="{ext:Locale SettingsTabSystemSystemTimeMatch}" />
|
||||
</Button>
|
||||
Text="{ext:Locale SettingsTabSystemSystemTimeMatch}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}"
|
||||
Width="250"/>
|
||||
<CheckBox
|
||||
VerticalAlignment="Center"
|
||||
IsChecked="{Binding MatchSystemTime}"
|
||||
ToolTip.Tip="{ext:Locale MatchTimeTooltip}"/>
|
||||
</StackPanel>
|
||||
<Separator />
|
||||
<StackPanel Margin="0,10,0,10"
|
||||
|
||||
@@ -34,7 +34,5 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
||||
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
|
||||
}
|
||||
}
|
||||
|
||||
private void MatchSystemTime_OnClick(object sender, RoutedEventArgs e) => ViewModel.MatchSystemTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<window:StyleableAppWindow
|
||||
x:Class="Ryujinx.Ava.UI.Windows.ArgumentsConfigWindows"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
Width="1100"
|
||||
Height="768"
|
||||
MinWidth="800"
|
||||
MinHeight="480"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
x:DataType="viewModels:SettingsViewModel"
|
||||
mc:Ignorable="d"
|
||||
Focusable="True">
|
||||
<Design.DataContext>
|
||||
<viewModels:SettingsViewModel />
|
||||
</Design.DataContext>
|
||||
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600" RowDefinitions="Auto,*,Auto">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
Grid.Row="1"
|
||||
IsVisible="False"
|
||||
KeyboardNavigation.IsTabStop="False"/>
|
||||
<Grid Name="Pages" IsVisible="False" Grid.Row="2">
|
||||
<settings:SettingsApplyOverride Name="AllSettings" />
|
||||
</Grid>
|
||||
<ui:NavigationView
|
||||
Grid.Row="1"
|
||||
IsSettingsVisible="False"
|
||||
Name="NavPanel"
|
||||
IsBackEnabled="False"
|
||||
PaneDisplayMode="Left"
|
||||
Margin="2,10,10,0"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
OpenPaneLength="200"
|
||||
IsPaneToggleButtonVisible="False">
|
||||
|
||||
<!-- For image -->
|
||||
<ui:NavigationView.PaneHeader>
|
||||
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="5"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="{Binding GameId}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10"
|
||||
TextAlignment="Center" Grid.Row="0" />
|
||||
<Image Source="{Binding GameIcon}"
|
||||
Width="160"
|
||||
Height="160"
|
||||
Grid.Row="1"
|
||||
Margin="0,0,0,10"
|
||||
HorizontalAlignment="Center" />
|
||||
<TextBlock Text="{Binding GameTitle}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="0,0,0,10"
|
||||
TextAlignment="Center" Grid.Row="2" />
|
||||
<Separator Height="1" Grid.Row="3" Margin="0,0,0,10" HorizontalAlignment="Stretch"/>
|
||||
</Grid>
|
||||
</ui:NavigationView.PaneHeader>
|
||||
|
||||
<ui:NavigationView.MenuItems>
|
||||
<ui:NavigationViewItem
|
||||
Margin="2,10,10,0"
|
||||
Content="{ext:Locale Settings}"
|
||||
Tag="AllSettings">
|
||||
<ui:NavigationViewItem.IconSource>
|
||||
<ui:FontIconSource
|
||||
FontFamily="avares://Ryujinx/Assets/Fonts#Segoe Fluent Icons"
|
||||
Glyph="{helpers:GlyphValueConverter Chip}" />
|
||||
</ui:NavigationViewItem.IconSource>
|
||||
</ui:NavigationViewItem>
|
||||
</ui:NavigationView.MenuItems>
|
||||
</ui:NavigationView>
|
||||
|
||||
<ReversibleStackPanel
|
||||
Grid.Row="2"
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{Binding IsMacOS}">
|
||||
<Button
|
||||
Content="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||
Command="{Binding CreateShortcut}" />
|
||||
<Button
|
||||
HotKey="Escape"
|
||||
Content="{ext:Locale ControllerSettingsClose}"
|
||||
Command="{Binding CancelButton}" />
|
||||
</ReversibleStackPanel>
|
||||
</Grid>
|
||||
</window:StyleableAppWindow>
|
||||
@@ -1,162 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Shapes;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Media.Imaging;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Projektanker.Icons.Avalonia;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.ViewModels.Input;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Ava.Utilities.Configuration;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Key = Avalonia.Input.Key;
|
||||
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class ArgumentsConfigWindows : StyleableAppWindow
|
||||
{
|
||||
internal readonly SettingsViewModel ViewModel;
|
||||
public string GamePath { get; }
|
||||
public string GameName { get; }
|
||||
public string GameId { get; }
|
||||
public byte[] GameIconData { get; }
|
||||
|
||||
public static int OverrideBackendThreading { get; private set; }
|
||||
public static int OverrideGraphicsBackend { get; private set; }
|
||||
public static int OverrideSystemLanguage { get; private set; }
|
||||
public static int OverrideSystemRegion { get; private set; }
|
||||
public static bool OverridePPTC { get; private set; }
|
||||
public static int OverrideMemoryManagerMode { get; private set; }
|
||||
|
||||
|
||||
public ArgumentsConfigWindows(MainWindowViewModel viewModel)
|
||||
{
|
||||
Title = RyujinxApp.FormatTitle(LocaleKeys.Settings);
|
||||
|
||||
DataContext = ViewModel = new SettingsViewModel(
|
||||
viewModel.VirtualFileSystem,
|
||||
viewModel.ContentManager,
|
||||
viewModel.SelectedApplication.Path,
|
||||
viewModel.SelectedApplication.Name,
|
||||
viewModel.SelectedApplication.IdString,
|
||||
viewModel.SelectedApplication.Icon);
|
||||
|
||||
GamePath = viewModel.SelectedApplication.Path;
|
||||
GameName = viewModel.SelectedApplication.Name;
|
||||
GameId = viewModel.SelectedApplication.IdString;
|
||||
GameIconData = viewModel.SelectedApplication.Icon;
|
||||
|
||||
OverrideBackendThreading = ViewModel.GraphicsBackendMultithreadingIndex;
|
||||
OverrideGraphicsBackend = ViewModel.GraphicsBackendIndex;
|
||||
OverrideSystemLanguage = ViewModel.Language;
|
||||
OverrideSystemRegion = ViewModel.Region;
|
||||
OverridePPTC = ViewModel.EnablePptc;
|
||||
OverrideMemoryManagerMode = ViewModel.MemoryMode;
|
||||
|
||||
ViewModel.CloseWindow += Close;
|
||||
ViewModel.CompareSettingsEvent += CompareConfiguration;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
|
||||
#if DEBUG
|
||||
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
|
||||
#endif
|
||||
}
|
||||
|
||||
public void CompareConfiguration()
|
||||
{
|
||||
ShortcutHelper.CreateAppShortcut(
|
||||
GamePath,
|
||||
GameName,
|
||||
GameId,
|
||||
GameIconData,
|
||||
GetArguments()
|
||||
);
|
||||
}
|
||||
|
||||
private string GetArguments()
|
||||
{
|
||||
|
||||
string line = "";
|
||||
|
||||
if (OverrideBackendThreading != ViewModel.GraphicsBackendMultithreadingIndex)
|
||||
{
|
||||
string _result = Enum.GetName(typeof(BackendThreading), ViewModel.GraphicsBackendMultithreadingIndex);
|
||||
line += " --backend-threading " + _result;
|
||||
}
|
||||
|
||||
if (OverrideGraphicsBackend != ViewModel.GraphicsBackendIndex)
|
||||
{
|
||||
string _result = Enum.GetName(typeof(GraphicsBackend), ViewModel.GraphicsBackendIndex);
|
||||
line += " -g " + _result;
|
||||
}
|
||||
|
||||
if (OverridePPTC != ViewModel.EnablePptc)
|
||||
{
|
||||
string _result = ViewModel.EnablePptc.ToString();
|
||||
line += " --pptc " + _result;
|
||||
}
|
||||
|
||||
if (OverrideMemoryManagerMode != ViewModel.MemoryMode)
|
||||
{
|
||||
string _result = Enum.GetName(typeof(MemoryManagerMode), ViewModel.MemoryMode);
|
||||
line += " -m " + _result;
|
||||
}
|
||||
|
||||
if (OverrideSystemRegion != ViewModel.Region)
|
||||
{
|
||||
string _result = Enum.GetName(typeof(RegionCode), ViewModel.Region);
|
||||
line += " --system-region " + _result;
|
||||
}
|
||||
|
||||
if (OverrideSystemLanguage != ViewModel.Language)
|
||||
{
|
||||
string _result = Enum.GetName(typeof(SystemLanguage), ViewModel.Language);
|
||||
line += " --system-language " + _result;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
Pages.Children.Clear();
|
||||
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||
|
||||
}
|
||||
|
||||
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
|
||||
{
|
||||
|
||||
if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
|
||||
{
|
||||
switch (navItem.Tag.ToString())
|
||||
{
|
||||
case nameof(AllSettings):
|
||||
NavPanel.Content = AllSettings;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
base.OnClosing(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||
Width="1100"
|
||||
Height="768"
|
||||
MinWidth="800"
|
||||
@@ -113,7 +114,7 @@
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{Binding IsMacOS}">
|
||||
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
|
||||
<Button
|
||||
Classes="accent"
|
||||
Content="{ext:Locale SettingsButtonOk}"
|
||||
|
||||
@@ -7,6 +7,8 @@ using LibHac.Ns;
|
||||
using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Utilities.Compat;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
@@ -21,9 +23,30 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
||||
public bool Favorite { get; set; }
|
||||
public byte[] Icon { get; set; }
|
||||
public string Name { get; set; } = "Unknown";
|
||||
public ulong Id { get; set; }
|
||||
|
||||
private ulong _id;
|
||||
|
||||
public ulong Id
|
||||
{
|
||||
get => _id;
|
||||
set
|
||||
{
|
||||
_id = value;
|
||||
PlayabilityStatus = CompatibilityCsv.GetStatus(Id);
|
||||
}
|
||||
}
|
||||
public string Developer { get; set; } = "Unknown";
|
||||
public string Version { get; set; } = "0";
|
||||
|
||||
public bool HasPlayabilityInfo => PlayabilityStatus != null;
|
||||
|
||||
public string LocalizedStatus =>
|
||||
PlayabilityStatus.HasValue
|
||||
? LocaleManager.Instance[PlayabilityStatus!.Value]
|
||||
: string.Empty;
|
||||
|
||||
public LocaleKeys? PlayabilityStatus { get; set; }
|
||||
|
||||
public int PlayerCount { get; set; }
|
||||
public int GameCount { get; set; }
|
||||
public TimeSpan TimePlayed { get; set; }
|
||||
|
||||
@@ -6,15 +6,11 @@ namespace Ryujinx.Ava.Utilities
|
||||
public static class CommandLineState
|
||||
{
|
||||
public static string[] Arguments { get; private set; }
|
||||
public static int CountArguments { get; private set; }
|
||||
|
||||
public static bool? OverrideDockedMode { get; private set; }
|
||||
public static bool? OverrideHardwareAcceleration { get; private set; }
|
||||
public static string OverrideGraphicsBackend { get; private set; }
|
||||
public static string OverrideBackendThreading { get; private set; }
|
||||
public static string OverridePPTC { get; private set; }
|
||||
public static string OverrideMemoryManagerMode { get; private set; }
|
||||
public static string OverrideSystemRegion { get; private set; }
|
||||
public static string OverrideSystemLanguage { get; private set; }
|
||||
public static string OverrideHideCursor { get; private set; }
|
||||
public static string BaseDirPathArg { get; private set; }
|
||||
public static string Profile { get; private set; }
|
||||
@@ -32,11 +28,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
{
|
||||
string arg = args[i];
|
||||
|
||||
if (arg.Contains("-") || arg.Contains("--"))
|
||||
{
|
||||
CountArguments++;
|
||||
}
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
case "-r":
|
||||
@@ -94,47 +85,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
|
||||
OverrideBackendThreading = args[++i];
|
||||
break;
|
||||
case "--pptc":
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
OverridePPTC = args[++i];
|
||||
break;
|
||||
case "-m":
|
||||
case "--memory-manager-mode":
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
OverrideMemoryManagerMode = args[++i];
|
||||
break;
|
||||
case "--system-region":
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
OverrideSystemRegion = args[++i];
|
||||
break;
|
||||
case "--system-language":
|
||||
if (i + 1 >= args.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
OverrideSystemLanguage = args[++i];
|
||||
break;
|
||||
case "-i":
|
||||
case "--application-id":
|
||||
LaunchApplicationId = args[++i];
|
||||
|
||||
@@ -47,11 +47,6 @@ namespace Ryujinx.Ava.Utilities.Compat
|
||||
Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded.", "LoadCompatibility");
|
||||
}
|
||||
|
||||
public static void Unload()
|
||||
{
|
||||
_entries = null;
|
||||
}
|
||||
|
||||
private static CompatibilityEntry[] _entries;
|
||||
|
||||
public static CompatibilityEntry[] Entries
|
||||
@@ -64,6 +59,11 @@ namespace Ryujinx.Ava.Utilities.Compat
|
||||
return _entries;
|
||||
}
|
||||
}
|
||||
|
||||
public static LocaleKeys? GetStatus(string titleId)
|
||||
=> Entries.FirstOrDefault(x => x.TitleId.HasValue && x.TitleId.Value.EqualsIgnoreCase(titleId))?.Status;
|
||||
|
||||
public static LocaleKeys? GetStatus(ulong titleId) => GetStatus(titleId.ToString("X16"));
|
||||
}
|
||||
|
||||
public class CompatibilityEntry
|
||||
|
||||
@@ -32,8 +32,6 @@ namespace Ryujinx.Ava.Utilities.Compat
|
||||
contentDialog.Styles.Add(closeButtonParent);
|
||||
|
||||
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||
|
||||
CompatibilityCsv.Unload();
|
||||
}
|
||||
|
||||
public CompatibilityList()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 62;
|
||||
public const int CurrentVersion = 63;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
@@ -141,6 +141,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// Change System Time Offset in seconds
|
||||
/// </summary>
|
||||
public long SystemTimeOffset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instead of setting the time via configuration, use the values provided by the system.
|
||||
/// </summary>
|
||||
public bool MatchSystemTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
|
||||
@@ -429,7 +429,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
};
|
||||
}
|
||||
}),
|
||||
(62, static cff => cff.RainbowSpeed = 1f)
|
||||
(62, static cff => cff.RainbowSpeed = 1f),
|
||||
(63, static cff => cff.MatchSystemTime = false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,6 +312,11 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
/// System Time Offset in Seconds
|
||||
/// </summary>
|
||||
public ReactiveObject<long> SystemTimeOffset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instead of setting the time via configuration, use the values provided by the system.
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> MatchSystemTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
@@ -388,6 +393,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
||||
TimeZone.LogChangesToValue(nameof(TimeZone));
|
||||
SystemTimeOffset = new ReactiveObject<long>();
|
||||
SystemTimeOffset.LogChangesToValue(nameof(SystemTimeOffset));
|
||||
MatchSystemTime = new ReactiveObject<bool>();
|
||||
MatchSystemTime.LogChangesToValue(nameof(MatchSystemTime));
|
||||
EnableDockedMode = new ReactiveObject<bool>();
|
||||
EnableDockedMode.LogChangesToValue(nameof(EnableDockedMode));
|
||||
EnablePtc = new ReactiveObject<bool>();
|
||||
|
||||
218
src/Ryujinx/Utilities/PlayReport.cs
Normal file
218
src/Ryujinx/Utilities/PlayReport.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using Gommon;
|
||||
using MsgPack;
|
||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||
using Ryujinx.Common.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Ava.Utilities
|
||||
{
|
||||
public static class PlayReport
|
||||
{
|
||||
public static PlayReportAnalyzer Analyzer { get; } = new PlayReportAnalyzer()
|
||||
.AddSpec(
|
||||
"01007ef00011e000",
|
||||
spec => spec.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
|
||||
)
|
||||
.AddSpec(
|
||||
"0100f2c0115b6000",
|
||||
spec => spec.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
|
||||
.AddSpec(
|
||||
"0100000000010000",
|
||||
spec =>
|
||||
spec.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
|
||||
)
|
||||
.AddSpec(
|
||||
"010075000ECBE000",
|
||||
spec =>
|
||||
spec.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
|
||||
)
|
||||
.AddSpec(
|
||||
"010028600EBDA000",
|
||||
spec => spec.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
|
||||
)
|
||||
.AddSpec( // Global & China IDs
|
||||
["0100152000022000", "010075100E8EC000"],
|
||||
spec => spec.AddValueFormatter("To", MarioKart8Deluxe_Mode)
|
||||
);
|
||||
|
||||
private static PlayReportFormattedValue BreathOfTheWild_MasterMode(ref PlayReportValue value)
|
||||
=> value.BoxedValue is 1 ? "Playing Master Mode" : PlayReportFormattedValue.ForceReset;
|
||||
|
||||
private static PlayReportFormattedValue TearsOfTheKingdom_CurrentField(ref PlayReportValue value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return (double)value.BoxedValue switch
|
||||
{
|
||||
> 800d => "Exploring the Sky Islands",
|
||||
< -201d => "Exploring the Depths",
|
||||
_ => "Roaming Hyrule"
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return PlayReportFormattedValue.ForceReset;
|
||||
}
|
||||
}
|
||||
|
||||
private static PlayReportFormattedValue SuperMarioOdyssey_AssistMode(ref PlayReportValue value)
|
||||
=> value.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
||||
|
||||
private static PlayReportFormattedValue SuperMarioOdysseyChina_AssistMode(ref PlayReportValue value)
|
||||
=> value.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
||||
|
||||
private static PlayReportFormattedValue SuperMario3DWorldOrBowsersFury(ref PlayReportValue value)
|
||||
=> value.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
||||
|
||||
private static PlayReportFormattedValue MarioKart8Deluxe_Mode(ref PlayReportValue value)
|
||||
=> value.BoxedValue switch
|
||||
{
|
||||
// Single Player
|
||||
"Single" => "Single Player",
|
||||
// Multiplayer
|
||||
"Multi-2players" => "Multiplayer 2 Players",
|
||||
"Multi-3players" => "Multiplayer 3 Players",
|
||||
"Multi-4players" => "Multiplayer 4 Players",
|
||||
// Wireless/LAN Play
|
||||
"Local-Single" => "Wireless/LAN Play",
|
||||
"Local-2players" => "Wireless/LAN Play 2 Players",
|
||||
// CC Classes
|
||||
"50cc" => "50cc",
|
||||
"100cc" => "100cc",
|
||||
"150cc" => "150cc",
|
||||
"Mirror" => "Mirror (150cc)",
|
||||
"200cc" => "200cc",
|
||||
// Modes
|
||||
"GrandPrix" => "Grand Prix",
|
||||
"TimeAttack" => "Time Trials",
|
||||
"VS" => "VS Races",
|
||||
"Battle" => "Battle Mode",
|
||||
"RaceStart" => "Selecting a Course",
|
||||
"Race" => "Racing",
|
||||
_ => PlayReportFormattedValue.ForceReset
|
||||
};
|
||||
}
|
||||
|
||||
#region Analyzer implementation
|
||||
|
||||
public class PlayReportAnalyzer
|
||||
{
|
||||
private readonly List<PlayReportGameSpec> _specs = [];
|
||||
|
||||
public PlayReportAnalyzer AddSpec(string titleId, Func<PlayReportGameSpec, PlayReportGameSpec> transform)
|
||||
{
|
||||
_specs.Add(transform(new PlayReportGameSpec { TitleIds = [titleId] }));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayReportAnalyzer AddSpec(string titleId, Action<PlayReportGameSpec> transform)
|
||||
{
|
||||
_specs.Add(new PlayReportGameSpec { TitleIds = [titleId] }.Apply(transform));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayReportAnalyzer AddSpec(IEnumerable<string> titleIds, Func<PlayReportGameSpec, PlayReportGameSpec> transform)
|
||||
{
|
||||
_specs.Add(transform(new PlayReportGameSpec { TitleIds = [..titleIds] }));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayReportAnalyzer AddSpec(IEnumerable<string> titleIds, Action<PlayReportGameSpec> transform)
|
||||
{
|
||||
_specs.Add(new PlayReportGameSpec { TitleIds = [..titleIds] }.Apply(transform));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayReportFormattedValue Run(string runningGameId, ApplicationMetadata appMeta, MessagePackObject playReport)
|
||||
{
|
||||
if (!playReport.IsDictionary)
|
||||
return PlayReportFormattedValue.Unhandled;
|
||||
|
||||
if (!_specs.TryGetFirst(s => runningGameId.EqualsAnyIgnoreCase(s.TitleIds), out PlayReportGameSpec spec))
|
||||
return PlayReportFormattedValue.Unhandled;
|
||||
|
||||
foreach (PlayReportValueFormatterSpec formatSpec in spec.Analyses.OrderBy(x => x.Priority))
|
||||
{
|
||||
if (!playReport.AsDictionary().TryGetValue(formatSpec.ReportKey, out MessagePackObject valuePackObject))
|
||||
continue;
|
||||
|
||||
PlayReportValue value = new()
|
||||
{
|
||||
Application = appMeta,
|
||||
BoxedValue = valuePackObject.ToObject()
|
||||
};
|
||||
|
||||
return formatSpec.ValueFormatter(ref value);
|
||||
}
|
||||
|
||||
return PlayReportFormattedValue.Unhandled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class PlayReportGameSpec
|
||||
{
|
||||
public required string[] TitleIds { get; init; }
|
||||
public List<PlayReportValueFormatterSpec> Analyses { get; } = [];
|
||||
|
||||
public PlayReportGameSpec AddValueFormatter(string reportKey, PlayReportValueFormatter valueFormatter)
|
||||
{
|
||||
Analyses.Add(new PlayReportValueFormatterSpec
|
||||
{
|
||||
Priority = Analyses.Count,
|
||||
ReportKey = reportKey,
|
||||
ValueFormatter = valueFormatter
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public PlayReportGameSpec AddValueFormatter(int priority, string reportKey, PlayReportValueFormatter valueFormatter)
|
||||
{
|
||||
Analyses.Add(new PlayReportValueFormatterSpec
|
||||
{
|
||||
Priority = priority,
|
||||
ReportKey = reportKey,
|
||||
ValueFormatter = valueFormatter
|
||||
});
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PlayReportValue
|
||||
{
|
||||
public ApplicationMetadata Application { get; init; }
|
||||
public object BoxedValue { get; init; }
|
||||
}
|
||||
|
||||
public struct PlayReportFormattedValue
|
||||
{
|
||||
public bool Handled { get; private init; }
|
||||
|
||||
public bool Reset { get; private init; }
|
||||
|
||||
public string FormattedString { get; private init; }
|
||||
|
||||
public static implicit operator PlayReportFormattedValue(string formattedValue)
|
||||
=> new() { Handled = true, FormattedString = formattedValue };
|
||||
|
||||
public static PlayReportFormattedValue Unhandled => default;
|
||||
public static PlayReportFormattedValue ForceReset => new() { Handled = true, Reset = true };
|
||||
|
||||
public static PlayReportValueFormatter AlwaysResets = AlwaysResetsImpl;
|
||||
|
||||
private static PlayReportFormattedValue AlwaysResetsImpl(ref PlayReportValue _) => ForceReset;
|
||||
}
|
||||
|
||||
public struct PlayReportValueFormatterSpec
|
||||
{
|
||||
public required int Priority { get; init; }
|
||||
public required string ReportKey { get; init; }
|
||||
public PlayReportValueFormatter ValueFormatter { get; init; }
|
||||
}
|
||||
|
||||
public delegate PlayReportFormattedValue PlayReportValueFormatter(ref PlayReportValue value);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
public static class ShortcutHelper
|
||||
{
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath, string args = "")
|
||||
private static void CreateShortcutWindows(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string cleanedAppName, string desktopPath)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName + ".exe");
|
||||
iconPath += ".ico";
|
||||
@@ -22,13 +22,13 @@ namespace Ryujinx.Ava.Utilities
|
||||
image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
|
||||
SaveBitmapAsIcon(image, iconPath);
|
||||
|
||||
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId, args), iconPath, 0);
|
||||
Shortcut shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
|
||||
shortcut.StringData.NameString = cleanedAppName;
|
||||
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName, string args = "")
|
||||
private static void CreateShortcutLinux(string applicationFilePath, string applicationId, byte[] iconData, string iconPath, string desktopPath, string cleanedAppName)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.sh");
|
||||
string desktopFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.desktop");
|
||||
@@ -40,11 +40,11 @@ namespace Ryujinx.Ava.Utilities
|
||||
data.SaveTo(file);
|
||||
|
||||
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
|
||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId, args)}");
|
||||
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName, string args = "")
|
||||
private static void CreateShortcutMacos(string appFilePath, string applicationId, byte[] iconData, string desktopPath, string cleanedAppName)
|
||||
{
|
||||
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
|
||||
string plistFile = EmbeddedResources.ReadAllText("Ryujinx/Assets/ShortcutFiles/shortcut-template.plist");
|
||||
@@ -63,7 +63,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
string scriptPath = Path.Combine(scriptFolderPath, ScriptName);
|
||||
using StreamWriter scriptFile = new(scriptPath);
|
||||
|
||||
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId, args));
|
||||
scriptFile.Write(shortcutScript, basePath, GetArgsString(appFilePath, applicationId));
|
||||
|
||||
// Set execute permission
|
||||
FileInfo fileInfo = new(scriptPath);
|
||||
@@ -87,7 +87,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
outputFile.Write(plistFile, ScriptName, cleanedAppName, IconName);
|
||||
}
|
||||
|
||||
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData, string args = "")
|
||||
public static void CreateAppShortcut(string applicationFilePath, string applicationName, string applicationId, byte[] iconData)
|
||||
{
|
||||
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
|
||||
string cleanedAppName = string.Join("_", applicationName.Split(Path.GetInvalidFileNameChars()));
|
||||
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
{
|
||||
string iconPath = Path.Combine(AppDataManager.BaseDirPath, "games", applicationId, "app");
|
||||
|
||||
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath, args);
|
||||
CreateShortcutWindows(applicationFilePath, applicationId, iconData, iconPath, cleanedAppName, desktopPath);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -106,14 +106,14 @@ namespace Ryujinx.Ava.Utilities
|
||||
string iconPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "icons", "Ryujinx");
|
||||
|
||||
Directory.CreateDirectory(iconPath);
|
||||
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName, args);
|
||||
CreateShortcutLinux(applicationFilePath, applicationId, iconData, Path.Combine(iconPath, applicationId), desktopPath, cleanedAppName);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName, args);
|
||||
CreateShortcutMacos(applicationFilePath, applicationId, iconData, desktopPath, cleanedAppName);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ namespace Ryujinx.Ava.Utilities
|
||||
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
|
||||
}
|
||||
|
||||
private static string GetArgsString(string appFilePath, string applicationId, string config = "")
|
||||
private static string GetArgsString(string appFilePath, string applicationId)
|
||||
{
|
||||
// args are first defined as a list, for easier adjustments in the future
|
||||
List<string> argsList = [];
|
||||
@@ -132,11 +132,6 @@ namespace Ryujinx.Ava.Utilities
|
||||
argsList.Add($"\"{CommandLineState.BaseDirPathArg}\"");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(config))
|
||||
{
|
||||
argsList.Add(config);
|
||||
}
|
||||
|
||||
if (appFilePath.ToLower().EndsWith(".xci"))
|
||||
{
|
||||
argsList.Add("--application-id");
|
||||
|
||||
Reference in New Issue
Block a user