Compare commits

..

2 Commits

Author SHA1 Message Date
Evan Husted
4de53ada43 Merge branch 'master' into master 2024-12-30 19:25:00 -06:00
jozz024
a18c101f0a add a keyboard shortcut for opening bin files 2024-12-28 11:40:46 -06:00
49 changed files with 2029 additions and 1064 deletions

View File

@@ -37,7 +37,7 @@ jobs:
if (!artifacts.length) { if (!artifacts.length) {
return core.error(`No artifacts found`); return core.error(`No artifacts found`);
} }
let body = `*You need to be logged into GitHub to download these files.*\n\nDownload the artifacts for this pull request:\n`; let body = `Download the artifacts for this pull request:\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`; let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) { for (const art of artifacts) {
const url = `https://github.com/Ryubing/Ryujinx/actions/runs/${run_id}/artifacts/${art.id}`; const url = `https://github.com/Ryubing/Ryujinx/actions/runs/${run_id}/artifacts/${art.id}`;

View File

@@ -17,7 +17,6 @@
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/> <PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/>
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/> <PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
<PackageVersion Include="Concentus" Version="2.2.0" /> <PackageVersion Include="Concentus" Version="2.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="9.0.4" /> <PackageVersion Include="DynamicData" Version="9.0.4" />
@@ -42,7 +41,7 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.0.1" /> <PackageVersion Include="Gommon" Version="2.6.8" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" /> <PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />

View File

@@ -249,12 +249,13 @@ Global
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -1,7 +0,0 @@
namespace Ryujinx.BuildValidationTasks
{
public interface IValidationTask
{
public bool Execute(string projectPath, bool isGitRunner);
}
}

View File

@@ -0,0 +1,73 @@
using System;
using Microsoft.Build.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
using Microsoft.Build.Framework;
namespace Ryujinx.BuildValidationTasks
{
public class LocaleValidationTask : Task
{
public override bool Execute()
{
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
if (path.Split(["src"], StringSplitOptions.None).Length == 1)
{
//i assume that we are in a build directory in the solution dir
path = new FileInfo(path).Directory!.Parent!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
else
{
path = path.Split(["src"], StringSplitOptions.None)[0];
path = new FileInfo(path).Directory!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
string data;
using (StreamReader sr = new(path))
{
data = sr.ReadToEnd();
}
LocalesJson json = JsonConvert.DeserializeObject<LocalesJson>(data);
for (int i = 0; i < json.Locales.Count; i++)
{
LocalesEntry locale = json.Locales[i];
foreach (string langCode in json.Languages.Where(it => !locale.Translations.ContainsKey(it)))
{
locale.Translations.Add(langCode, string.Empty);
Log.LogMessage(MessageImportance.High, $"Added '{langCode}' to Locale '{locale.ID}'");
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
json.Locales[i] = locale;
}
string jsonString = JsonConvert.SerializeObject(json, Formatting.Indented);
using (StreamWriter sw = new(path))
{
sw.Write(jsonString);
}
return true;
}
struct LocalesJson
{
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}
struct LocalesEntry
{
public string ID { get; set; }
public Dictionary<string, string> Translations { get; set; }
}
}
}

View File

@@ -1,117 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.Json;
using System.Text.Encodings.Web;
namespace Ryujinx.BuildValidationTasks
{
public class LocalesValidationTask : IValidationTask
{
public LocalesValidationTask() { }
public bool Execute(string projectPath, bool isGitRunner)
{
Console.WriteLine("Running Locale Validation Task...");
string path = projectPath + "src/Ryujinx/Assets/locales.json";
string data;
using (StreamReader sr = new(path))
{
data = sr.ReadToEnd();
}
LocalesJson json;
if (isGitRunner && data.Contains("\r\n"))
throw new FormatException("locales.json is using CRLF line endings! It should be using LF line endings, build locally to fix...");
try
{
json = JsonSerializer.Deserialize<LocalesJson>(data);
}
catch (JsonException e)
{
throw new JsonException(e.Message); //shorter and easier stacktrace
}
bool encounteredIssue = false;
for (int i = 0; i < json.Locales.Count; i++)
{
LocalesEntry locale = json.Locales[i];
foreach (string langCode in json.Languages.Where(lang => !locale.Translations.ContainsKey(lang)))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations.Add(langCode, string.Empty);
Console.WriteLine($"Added '{langCode}' to Locale '{locale.ID}'");
}
else
{
Console.WriteLine($"Missing '{langCode}' in Locale '{locale.ID}'!");
}
}
foreach (string langCode in json.Languages.Where(lang => locale.Translations.ContainsKey(lang) && lang != "en_US" && locale.Translations[lang] == locale.Translations["en_US"]))
{
encounteredIssue = true;
if (!isGitRunner)
{
locale.Translations[langCode] = string.Empty;
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
}
else
{
Console.WriteLine($"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
}
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
json.Locales[i] = locale;
}
if (isGitRunner && encounteredIssue)
throw new JsonException("1 or more locales are invalid!");
JsonSerializerOptions jsonOptions = new JsonSerializerOptions()
{
WriteIndented = true,
NewLine = "\n",
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
string jsonString = JsonSerializer.Serialize(json, jsonOptions);
using (StreamWriter sw = new(path))
{
sw.Write(jsonString);
}
Console.WriteLine("Finished Locale Validation Task!");
return true;
}
struct LocalesJson
{
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}
struct LocalesEntry
{
public string ID { get; set; }
public Dictionary<string, string> Translations { get; set; }
}
}
}

View File

@@ -1,37 +0,0 @@
using System;
using System.IO;
using System.Linq;
namespace Ryujinx.BuildValidationTasks
{
public class Program
{
static void Main(string[] args)
{
// Display the number of command line arguments.
if (args.Length == 0)
throw new ArgumentException("Error: too few arguments!");
string path = args[0];
if (string.IsNullOrEmpty(path))
throw new ArgumentException("Error: path is null or empty!");
if (!Path.Exists(path))
throw new FileLoadException($"path {{{path}}} does not exist!");
path = Path.GetFullPath(path);
if (!Directory.GetDirectories(path).Contains($"{path}src"))
throw new FileLoadException($"path {{{path}}} is not a valid ryujinx project!");
bool isGitRunner = path.Contains("runner") || path.Contains("D:\\a\\Ryujinx\\Ryujinx");
if (isGitRunner)
Console.WriteLine("Is Git Runner!");
// Run tasks
// Pass extra info needed in the task constructors
new LocalesValidationTask().Execute(path, isGitRunner);
}
}
}

View File

@@ -1,17 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<Target Name="PostBuildTarget" AfterTargets="AfterBuild"> <ItemGroup>
<Message Text="Running Validation Project" Importance="high" /> <PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\" <UsingTask TaskName="Ryujinx.BuildValidationTasks.LocaleValidationTask" TaskFactory="TaskHostFactory" AssemblyFile="$(OutDir)Ryujinx.BuildValidationTasks.dll" />
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true" <Target Name="LocalesJsonValidation" AfterTargets="AfterRebuild">
Condition="'$(RuntimeIdentifier)' == ''" <LocaleValidationTask />
/>
</Target> </Target>
</Project> </Project>

View File

@@ -0,0 +1,35 @@
namespace Ryujinx.Common
{
public class BitTricks
{
// Never actually written bit packing logic before, so I looked it up.
// This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195
public static ulong PackBitFields(uint[] values, byte[] bitFields)
{
ulong retVal = values[0]; //we set the first value right away
for (int f = 1; f < values.Length; f++)
{
retVal <<= bitFields[f]; // we shift the previous value
retVal += values[f];// and add our current value
}
return retVal;
}
public static uint[] UnpackBitFields(ulong packed, byte[] bitFields)
{
int fields = bitFields.Length - 1; // number of fields to unpack
uint[] retArr = new uint[fields + 1]; // init return array
int curPos = 0; // current field bit position (start)
int lastEnd; // position where last field ended
for (int f = fields; f >= 0; f--) // loop from last
{
lastEnd = curPos; // we store where the last value ended
curPos += bitFields[f]; // we get where the current value starts
int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic
}
return retArr;
}
}
}

View File

@@ -1,60 +0,0 @@
using Gommon;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Common.Configuration
{
[Flags]
public enum DirtyHack : byte
{
Xc2MenuSoftlockFix = 1,
ShaderTranslationDelay = 2
}
public readonly struct EnabledDirtyHack(DirtyHack hack, int value)
{
public DirtyHack Hack => hack;
public int Value => value;
public ulong Pack() => Raw.PackBitFields(PackedFormat);
public static EnabledDirtyHack Unpack(ulong packedHack)
{
var unpackedFields = packedHack.UnpackBitFields(PackedFormat);
if (unpackedFields is not [var hack, var value])
throw new ArgumentException(nameof(packedHack));
return new EnabledDirtyHack((DirtyHack)hack, (int)value);
}
private uint[] Raw => [(uint)Hack, (uint)Value.CoerceAtLeast(0)];
public static readonly byte[] PackedFormat = [8, 32];
}
public class DirtyHacks : Dictionary<DirtyHack, int>
{
public DirtyHacks(IEnumerable<EnabledDirtyHack> hacks)
=> hacks.ForEach(edh => Add(edh.Hack, edh.Value));
public DirtyHacks(ulong[] packedHacks) : this(packedHacks.Select(EnabledDirtyHack.Unpack)) {}
public ulong[] PackEntries()
=> Entries.Select(it => it.Pack()).ToArray();
public EnabledDirtyHack[] Entries
=> this
.Select(it => new EnabledDirtyHack(it.Key, it.Value))
.ToArray();
public static implicit operator DirtyHacks(EnabledDirtyHack[] hacks) => new(hacks);
public static implicit operator DirtyHacks(ulong[] packedHacks) => new(packedHacks);
public new int this[DirtyHack hack] => TryGetValue(hack, out var value) ? value : -1;
public bool IsEnabled(DirtyHack hack) => ContainsKey(hack);
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Common.Configuration
{
[Flags]
public enum DirtyHacks : byte
{
Xc2MenuSoftlockFix = 1,
ShaderCompilationThreadSleep = 2
}
public record EnabledDirtyHack(DirtyHacks Hack, int Value)
{
public static readonly byte[] PackedFormat = [8, 32];
public ulong Pack() => BitTricks.PackBitFields([(uint)Hack, (uint)Value], PackedFormat);
public static EnabledDirtyHack Unpack(ulong packedHack)
{
var unpackedFields = BitTricks.UnpackBitFields(packedHack, PackedFormat);
if (unpackedFields is not [var hack, var value])
throw new ArgumentException(nameof(packedHack));
return new EnabledDirtyHack((DirtyHacks)hack, (int)value);
}
}
public class DirtyHackCollection : Dictionary<DirtyHacks, int>
{
public DirtyHackCollection(EnabledDirtyHack[] hacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in hacks)
{
Add(dirtyHacks, value);
}
}
public DirtyHackCollection(ulong[] packedHacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in packedHacks.Select(EnabledDirtyHack.Unpack))
{
Add(dirtyHacks, value);
}
}
public ulong[] PackEntries() =>
this
.Select(it =>
BitTricks.PackBitFields([(uint)it.Key, (uint)it.Value], EnabledDirtyHack.PackedFormat))
.ToArray();
public static implicit operator DirtyHackCollection(EnabledDirtyHack[] hacks) => new(hacks);
public static implicit operator DirtyHackCollection(ulong[] packedHacks) => new(packedHacks);
public new int this[DirtyHacks hack] => TryGetValue(hack, out var value) ? value : -1;
public bool IsEnabled(DirtyHacks hack) => ContainsKey(hack);
}
}

View File

@@ -53,17 +53,6 @@ namespace Ryujinx.Common
{ {
public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName) public static void LogValueChange<T>(LogClass logClass, ReactiveEventArgs<T> eventArgs, string valueName)
{ {
if ((eventArgs.NewValue == null || eventArgs.OldValue == null))
{
if (!(eventArgs.NewValue == null && eventArgs.OldValue == null))
goto Log;
}
else if (!eventArgs.NewValue!.Equals(eventArgs.OldValue))
goto Log;
return;
Log:
string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}"); string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}");
Logger.Info?.Print(logClass, message); Logger.Info?.Print(logClass, message);

View File

@@ -35,8 +35,7 @@ namespace Ryujinx.Common
"010028600EBDA000", // Mario 3D World "010028600EBDA000", // Mario 3D World
"0100152000022000", // Mario Kart 8 Deluxe "0100152000022000", // Mario Kart 8 Deluxe
"01005CA01580E000", // Persona 5 "01005CA01580E000", // Persona 5
"0100187003A36000", // Pokémon: Let's Go, Evoli! "01001f5010dfa000", // Pokemon Legends Arceus
"010003f003a34000", // Pokémon: Let's Go, Pikachu!
"01008C0016544000", // Sea of Stars "01008C0016544000", // Sea of Stars
"01006A800016E000", // Smash Ultimate "01006A800016E000", // Smash Ultimate
"0100000000010000", // Super Mario Odyessy "0100000000010000", // Super Mario Odyessy

View File

@@ -40,35 +40,5 @@ namespace Ryujinx.Common
return (value >> 32) | (value << 32); return (value >> 32) | (value << 32);
} }
// Never actually written bit packing logic before, so I looked it up.
// This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195
public static ulong PackBitFields(this uint[] values, byte[] bitFields)
{
ulong retVal = values[0]; //we set the first value right away
for (int f = 1; f < values.Length; f++)
{
retVal <<= bitFields[f]; // we shift the previous value
retVal += values[f];// and add our current value
}
return retVal;
}
public static uint[] UnpackBitFields(this ulong packed, byte[] bitFields)
{
int fields = bitFields.Length - 1; // number of fields to unpack
uint[] retArr = new uint[fields + 1]; // init return array
int curPos = 0; // current field bit position (start)
int lastEnd; // position where last field ended
for (int f = fields; f >= 0; f--) // loop from last
{
lastEnd = curPos; // we store where the last value ended
curPos += bitFields[f]; // we get where the current value starts
int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic
}
return retArr;
}
} }
} }

View File

@@ -92,11 +92,7 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
internal SupportBufferUpdater SupportBufferUpdater { get; } internal SupportBufferUpdater SupportBufferUpdater { get; }
/// <summary> internal DirtyHackCollection DirtyHacks { get; }
/// Enabled dirty hacks.
/// Used for workarounds to emulator bugs we can't fix/don't know how to fix yet.
/// </summary>
internal DirtyHacks DirtyHacks { get; }
/// <summary> /// <summary>
@@ -121,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
/// </summary> /// </summary>
/// <param name="renderer">Host renderer</param> /// <param name="renderer">Host renderer</param>
public GpuContext(IRenderer renderer, DirtyHacks hacks) public GpuContext(IRenderer renderer, DirtyHackCollection hackCollection)
{ {
Renderer = renderer; Renderer = renderer;
@@ -144,7 +140,7 @@ namespace Ryujinx.Graphics.Gpu
SupportBufferUpdater = new SupportBufferUpdater(renderer); SupportBufferUpdater = new SupportBufferUpdater(renderer);
DirtyHacks = hacks; DirtyHacks = hackCollection;
_firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); _firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
} }

View File

@@ -367,8 +367,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{ {
try try
{ {
if (_context.DirtyHacks.IsEnabled(DirtyHack.ShaderTranslationDelay)) if (_context.DirtyHacks.IsEnabled(DirtyHacks.ShaderCompilationThreadSleep))
Thread.Sleep(_context.DirtyHacks[DirtyHack.ShaderTranslationDelay]); Thread.Sleep(_context.DirtyHacks[DirtyHacks.ShaderCompilationThreadSleep]);
AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute); AsyncProgramTranslation asyncTranslation = new(guestShaders, specState, programIndex, isCompute);
_asyncTranslationQueue.Add(asyncTranslation, _cancellationToken); _asyncTranslationQueue.Add(asyncTranslation, _cancellationToken);

View File

@@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
if (context.Device.DirtyHacks.IsEnabled(DirtyHack.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId) if (context.Device.DirtyHacks.IsEnabled(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId)
{ {
// Add a load-bearing sleep to avoid XC2 softlock // Add a load-bearing sleep to avoid XC2 softlock
// https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357 // https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.HLE
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
public DirtyHacks DirtyHacks { get; } public DirtyHackCollection DirtyHacks { get; }
public Switch(HLEConfiguration configuration) public Switch(HLEConfiguration configuration)
{ {
@@ -57,7 +57,7 @@ namespace Ryujinx.HLE
: MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable; : MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Mirrorable;
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting
DirtyHacks = new DirtyHacks(Configuration.Hacks); DirtyHacks = new DirtyHackCollection(Configuration.Hacks);
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks);

View File

@@ -314,7 +314,7 @@ namespace Ryujinx.Ava
_renderer.Window?.ChangeVSyncMode(e.NewValue); _renderer.Window?.ChangeVSyncMode(e.NewValue);
_viewModel.UpdateVSyncIntervalPicker(); _viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
} }
public void VSyncModeToggle() public void VSyncModeToggle()

View File

@@ -1234,7 +1234,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "자주 묻는 질문(FAQ) 및 안내", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -1564,7 +1564,7 @@
"pl_PL": "Wersja", "pl_PL": "Wersja",
"pt_BR": "Versão", "pt_BR": "Versão",
"ru_RU": "Версия", "ru_RU": "Версия",
"sv_SE": "", "sv_SE": "Version",
"th_TH": "เวอร์ชั่น", "th_TH": "เวอร์ชั่น",
"tr_TR": "Sürüm", "tr_TR": "Sürüm",
"uk_UA": "Версія", "uk_UA": "Версія",
@@ -2213,8 +2213,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "ExeFS",
"sv_SE": "", "sv_SE": "ExeFS",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -2263,8 +2263,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "RomFS",
"sv_SE": "", "sv_SE": "RomFS",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -2310,7 +2310,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "ロゴ", "ja_JP": "ロゴ",
"ko_KR": "로고", "ko_KR": "로고",
"no_NO": "", "no_NO": "Logo",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Лого", "ru_RU": "Лого",
@@ -2609,7 +2609,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "펌웨어 버전 : {0}", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Versão do firmware: {0}", "pt_BR": "Versão do firmware: {0}",
@@ -3260,11 +3260,11 @@
"it_IT": "Sistema", "it_IT": "Sistema",
"ja_JP": "システム", "ja_JP": "システム",
"ko_KR": "시스템", "ko_KR": "시스템",
"no_NO": "", "no_NO": "System",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Sistema", "pt_BR": "Sistema",
"ru_RU": "Система", "ru_RU": "Система",
"sv_SE": "", "sv_SE": "System",
"th_TH": "ระบบ", "th_TH": "ระบบ",
"tr_TR": "Sistem", "tr_TR": "Sistem",
"uk_UA": "Система", "uk_UA": "Система",
@@ -3335,11 +3335,11 @@
"it_IT": "Giappone", "it_IT": "Giappone",
"ja_JP": "日本", "ja_JP": "日本",
"ko_KR": "일본", "ko_KR": "일본",
"no_NO": "", "no_NO": "Japan",
"pl_PL": "Japonia", "pl_PL": "Japonia",
"pt_BR": "Japão", "pt_BR": "Japão",
"ru_RU": "Япония", "ru_RU": "Япония",
"sv_SE": "", "sv_SE": "Japan",
"th_TH": "ญี่ปุ่น", "th_TH": "ญี่ปุ่น",
"tr_TR": "Japonya", "tr_TR": "Japonya",
"uk_UA": "Японія", "uk_UA": "Японія",
@@ -3360,11 +3360,11 @@
"it_IT": "Stati Uniti d'America", "it_IT": "Stati Uniti d'America",
"ja_JP": "アメリカ", "ja_JP": "アメリカ",
"ko_KR": "미국", "ko_KR": "미국",
"no_NO": "", "no_NO": "USA",
"pl_PL": "Stany Zjednoczone", "pl_PL": "Stany Zjednoczone",
"pt_BR": "EUA", "pt_BR": "EUA",
"ru_RU": "США", "ru_RU": "США",
"sv_SE": "", "sv_SE": "USA",
"th_TH": "สหรัฐอเมริกา", "th_TH": "สหรัฐอเมริกา",
"tr_TR": "ABD", "tr_TR": "ABD",
"uk_UA": "США", "uk_UA": "США",
@@ -3410,7 +3410,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "オーストラリア", "ja_JP": "オーストラリア",
"ko_KR": "호주", "ko_KR": "호주",
"no_NO": "", "no_NO": "Australia",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Austrália", "pt_BR": "Austrália",
"ru_RU": "Австралия", "ru_RU": "Австралия",
@@ -3460,11 +3460,11 @@
"it_IT": "Corea", "it_IT": "Corea",
"ja_JP": "韓国", "ja_JP": "韓国",
"ko_KR": "한국", "ko_KR": "한국",
"no_NO": "", "no_NO": "Korea",
"pl_PL": "", "pl_PL": "",
"pt_BR": "Coreia", "pt_BR": "Coreia",
"ru_RU": "Корея", "ru_RU": "Корея",
"sv_SE": "", "sv_SE": "Korea",
"th_TH": "เกาหลี", "th_TH": "เกาหลี",
"tr_TR": "Kore", "tr_TR": "Kore",
"uk_UA": "Корея", "uk_UA": "Корея",
@@ -3485,11 +3485,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "台湾", "ja_JP": "台湾",
"ko_KR": "대만", "ko_KR": "대만",
"no_NO": "", "no_NO": "Taiwan",
"pl_PL": "Tajwan", "pl_PL": "Tajwan",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Тайвань", "ru_RU": "Тайвань",
"sv_SE": "", "sv_SE": "Taiwan",
"th_TH": "ไต้หวัน", "th_TH": "ไต้หวัน",
"tr_TR": "Tayvan", "tr_TR": "Tayvan",
"uk_UA": "Тайвань", "uk_UA": "Тайвань",
@@ -3955,7 +3955,7 @@
"el_GR": "Ζώνη Ώρας Συστήματος:", "el_GR": "Ζώνη Ώρας Συστήματος:",
"en_US": "System Time Zone:", "en_US": "System Time Zone:",
"es_ES": "Zona horaria del sistema:", "es_ES": "Zona horaria del sistema:",
"fr_FR": "Fuseau horaire du système\u00A0:", "fr_FR": "Fuseau horaire du système :",
"he_IL": "אזור זמן מערכת:", "he_IL": "אזור זמן מערכת:",
"it_IT": "Fuso orario del sistema:", "it_IT": "Fuso orario del sistema:",
"ja_JP": "タイムゾーン:", "ja_JP": "タイムゾーン:",
@@ -4009,7 +4009,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "PC 날짜와 시간에 동기화", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -4135,11 +4135,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "ダミー", "ja_JP": "ダミー",
"ko_KR": "더미", "ko_KR": "더미",
"no_NO": "", "no_NO": "Dummy",
"pl_PL": "Atrapa", "pl_PL": "Atrapa",
"pt_BR": "Nenhuma", "pt_BR": "Nenhuma",
"ru_RU": "Без звука", "ru_RU": "Без звука",
"sv_SE": "", "sv_SE": "Dummy",
"th_TH": "", "th_TH": "",
"tr_TR": "Yapay", "tr_TR": "Yapay",
"uk_UA": "", "uk_UA": "",
@@ -4163,8 +4163,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "OpenAL",
"sv_SE": "", "sv_SE": "OpenAL",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4188,8 +4188,8 @@
"no_NO": "Lyd Inn/Ut", "no_NO": "Lyd Inn/Ut",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "SoundIO",
"sv_SE": "", "sv_SE": "SoundIO",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4213,8 +4213,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "SDL2",
"sv_SE": "", "sv_SE": "SDL2",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4230,12 +4230,12 @@
"el_GR": "Μικροδιορθώσεις", "el_GR": "Μικροδιορθώσεις",
"en_US": "Hacks", "en_US": "Hacks",
"es_ES": "", "es_ES": "",
"fr_FR": "", "fr_FR": "Hacks",
"he_IL": "האצות", "he_IL": "האצות",
"it_IT": "Espedienti", "it_IT": "Espedienti",
"ja_JP": "ハック", "ja_JP": "ハック",
"ko_KR": "핵", "ko_KR": "핵",
"no_NO": "", "no_NO": "Hacks",
"pl_PL": "Hacki", "pl_PL": "Hacki",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Хаки", "ru_RU": "Хаки",
@@ -4314,7 +4314,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "4ГиБ", "ru_RU": "4ГиБ",
"sv_SE": "", "sv_SE": "4GiB",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "4Гб", "uk_UA": "4Гб",
@@ -4339,7 +4339,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "6ГиБ", "ru_RU": "6ГиБ",
"sv_SE": "", "sv_SE": "6GiB",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "6Гб", "uk_UA": "6Гб",
@@ -4364,7 +4364,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "8ГиБ", "ru_RU": "8ГиБ",
"sv_SE": "", "sv_SE": "8GiB",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "8Гб", "uk_UA": "8Гб",
@@ -4389,7 +4389,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "12ГиБ", "ru_RU": "12ГиБ",
"sv_SE": "", "sv_SE": "12GiB",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "12Гб", "uk_UA": "12Гб",
@@ -4585,11 +4585,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "2배", "ko_KR": "2배",
"no_NO": "", "no_NO": "2x",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "2x",
"sv_SE": "", "sv_SE": "2x",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4610,11 +4610,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "4배", "ko_KR": "4배",
"no_NO": "", "no_NO": "4x",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "4x",
"sv_SE": "", "sv_SE": "4x",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4635,11 +4635,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "8배", "ko_KR": "8배",
"no_NO": "", "no_NO": "8x",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "8x",
"sv_SE": "", "sv_SE": "8x",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4660,11 +4660,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "16배", "ko_KR": "16배",
"no_NO": "", "no_NO": "16x",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "16x",
"sv_SE": "", "sv_SE": "16x",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4760,11 +4760,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "2배(1440p/2160p)", "ko_KR": "2배(1440p/2160p)",
"no_NO": "", "no_NO": "2x (1440p/2160p)",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "2x (1440p/2160p)",
"sv_SE": "", "sv_SE": "2x (1440p/2160p)",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4785,11 +4785,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "3배(2160p/3240p)", "ko_KR": "3배(2160p/3240p)",
"no_NO": "", "no_NO": "3x (2160p/3240p)",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "3x (2160p/3240p)",
"sv_SE": "", "sv_SE": "3x (2160p/3240p)",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4863,8 +4863,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "4:3",
"sv_SE": "", "sv_SE": "4:3",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4888,8 +4888,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "16:9",
"sv_SE": "", "sv_SE": "16:9",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4913,8 +4913,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "16:10",
"sv_SE": "", "sv_SE": "16:10",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4938,8 +4938,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "21:9",
"sv_SE": "", "sv_SE": "21:9",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -4960,11 +4960,11 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "", "ko_KR": "",
"no_NO": "", "no_NO": "32:9",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "32:9",
"sv_SE": "", "sv_SE": "32:9",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -5060,7 +5060,7 @@
"it_IT": "Log", "it_IT": "Log",
"ja_JP": "ロギング", "ja_JP": "ロギング",
"ko_KR": "로그 기록", "ko_KR": "로그 기록",
"no_NO": "", "no_NO": "Logging",
"pl_PL": "Dziennik zdarzeń", "pl_PL": "Dziennik zdarzeń",
"pt_BR": "Log", "pt_BR": "Log",
"ru_RU": "Журналирование", "ru_RU": "Журналирование",
@@ -5085,7 +5085,7 @@
"it_IT": "Log", "it_IT": "Log",
"ja_JP": "ロギング", "ja_JP": "ロギング",
"ko_KR": "로그 기록", "ko_KR": "로그 기록",
"no_NO": "", "no_NO": "Logging",
"pl_PL": "Dziennik zdarzeń", "pl_PL": "Dziennik zdarzeń",
"pt_BR": "Log", "pt_BR": "Log",
"ru_RU": "Журналирование", "ru_RU": "Журналирование",
@@ -6113,8 +6113,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "Pro Kontroler", "pl_PL": "Pro Kontroler",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Pro Controller",
"sv_SE": "", "sv_SE": "Pro Controller",
"th_TH": "โปรคอนโทรลเลอร์", "th_TH": "โปรคอนโทรลเลอร์",
"tr_TR": "Profesyonel Kumanda", "tr_TR": "Profesyonel Kumanda",
"uk_UA": "Контролер Pro", "uk_UA": "Контролер Pro",
@@ -8088,8 +8088,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Enter",
"sv_SE": "", "sv_SE": "Enter",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8114,7 +8114,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Esc", "ru_RU": "Esc",
"sv_SE": "", "sv_SE": "Escape",
"th_TH": "", "th_TH": "",
"tr_TR": "Esc", "tr_TR": "Esc",
"uk_UA": "", "uk_UA": "",
@@ -8163,8 +8163,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Tab",
"sv_SE": "", "sv_SE": "Tab",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8188,8 +8188,8 @@
"no_NO": "Tilbaketast", "no_NO": "Tilbaketast",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Backspace",
"sv_SE": "", "sv_SE": "Backspace",
"th_TH": "", "th_TH": "",
"tr_TR": "Geri tuşu", "tr_TR": "Geri tuşu",
"uk_UA": "", "uk_UA": "",
@@ -8213,8 +8213,8 @@
"no_NO": "Sett inn", "no_NO": "Sett inn",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Insert",
"sv_SE": "", "sv_SE": "Insert",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8238,8 +8238,8 @@
"no_NO": "Slett", "no_NO": "Slett",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Delete",
"sv_SE": "", "sv_SE": "Delete",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8263,8 +8263,8 @@
"no_NO": "Side opp", "no_NO": "Side opp",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Page Up",
"sv_SE": "", "sv_SE": "Page Up",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8288,8 +8288,8 @@
"no_NO": "Side ned", "no_NO": "Side ned",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Page Down",
"sv_SE": "", "sv_SE": "Page Down",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8313,8 +8313,8 @@
"no_NO": "Hjem", "no_NO": "Hjem",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Home",
"sv_SE": "", "sv_SE": "Home",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8338,8 +8338,8 @@
"no_NO": "Avslutt", "no_NO": "Avslutt",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "End",
"sv_SE": "", "sv_SE": "End",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8363,8 +8363,8 @@
"no_NO": "Skiftelås", "no_NO": "Skiftelås",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Caps Lock",
"sv_SE": "", "sv_SE": "Caps Lock",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8388,8 +8388,8 @@
"no_NO": "Rullelås", "no_NO": "Rullelås",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Scroll Lock",
"sv_SE": "", "sv_SE": "Scroll Lock",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8413,8 +8413,8 @@
"no_NO": "Skjermbilde", "no_NO": "Skjermbilde",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Print Screen",
"sv_SE": "", "sv_SE": "Print Screen",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8438,8 +8438,8 @@
"no_NO": "Stans midlertidig", "no_NO": "Stans midlertidig",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Pause",
"sv_SE": "", "sv_SE": "Pause",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8463,8 +8463,8 @@
"no_NO": "Numerisk Lås", "no_NO": "Numerisk Lås",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Num Lock",
"sv_SE": "", "sv_SE": "Num Lock",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8514,7 +8514,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 0", "ru_RU": "Блок цифр 0",
"sv_SE": "", "sv_SE": "Keypad 0",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8539,7 +8539,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 1", "ru_RU": "Блок цифр 1",
"sv_SE": "", "sv_SE": "Keypad 1",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8564,7 +8564,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 2", "ru_RU": "Блок цифр 2",
"sv_SE": "", "sv_SE": "Keypad 2",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8589,7 +8589,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 3", "ru_RU": "Блок цифр 3",
"sv_SE": "", "sv_SE": "Keypad 3",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8614,7 +8614,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 4", "ru_RU": "Блок цифр 4",
"sv_SE": "", "sv_SE": "Keypad 4",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8639,7 +8639,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 5", "ru_RU": "Блок цифр 5",
"sv_SE": "", "sv_SE": "Keypad 5",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8664,7 +8664,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 6", "ru_RU": "Блок цифр 6",
"sv_SE": "", "sv_SE": "Keypad 6",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8689,7 +8689,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 7", "ru_RU": "Блок цифр 7",
"sv_SE": "", "sv_SE": "Keypad 7",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8714,7 +8714,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 8", "ru_RU": "Блок цифр 8",
"sv_SE": "", "sv_SE": "Keypad 8",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8739,7 +8739,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Блок цифр 9", "ru_RU": "Блок цифр 9",
"sv_SE": "", "sv_SE": "Keypad 9",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8889,7 +8889,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Enter (блок цифр)", "ru_RU": "Enter (блок цифр)",
"sv_SE": "", "sv_SE": "Keypad Enter",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -8913,7 +8913,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "0",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -8938,7 +8938,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "1",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -8963,7 +8963,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "2",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -8988,7 +8988,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "3",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9013,7 +9013,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "4",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9038,7 +9038,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "5",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9063,7 +9063,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "6",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9088,7 +9088,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "7",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9113,7 +9113,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "8",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9138,7 +9138,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "9",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9163,7 +9163,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "~",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9188,7 +9188,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "`",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9213,7 +9213,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "-",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9238,7 +9238,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "+",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9263,7 +9263,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "[",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9288,7 +9288,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "]",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9313,7 +9313,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": ";",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9363,7 +9363,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": ",",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9388,7 +9388,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": ".",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9413,7 +9413,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "/",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9738,7 +9738,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "-",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -9763,7 +9763,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "+",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "4", "tr_TR": "4",
@@ -9789,7 +9789,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Кнопка меню", "ru_RU": "Кнопка меню",
"sv_SE": "", "sv_SE": "Guide",
"th_TH": "", "th_TH": "",
"tr_TR": "Rehber", "tr_TR": "Rehber",
"uk_UA": "", "uk_UA": "",
@@ -12438,7 +12438,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "{0}: {1}",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -14289,7 +14289,7 @@
"pl_PL": "Seria Amiibo", "pl_PL": "Seria Amiibo",
"pt_BR": "Franquia Amiibo", "pt_BR": "Franquia Amiibo",
"ru_RU": "Серия Amiibo", "ru_RU": "Серия Amiibo",
"sv_SE": "", "sv_SE": "Amiibo Series",
"th_TH": "", "th_TH": "",
"tr_TR": "Amiibo Serisi", "tr_TR": "Amiibo Serisi",
"uk_UA": "Серія Amiibo", "uk_UA": "Серія Amiibo",
@@ -15259,7 +15259,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.\n\n이 설정은 활성 설정이 아니므로 여전히 동기화되지 않을 수 있으며, 이 경우 이 버튼을 다시 클릭하면 됩니다.", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -15755,7 +15755,7 @@
"el_GR": "", "el_GR": "",
"en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", "en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
"es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.", "es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.",
"fr_FR": "Format\u00A0d'affichage appliqué à la fenêtre du moteur de rendu.\n\nChangez cela uniquement si vous utilisez un mod changeant le format\u00A0d'affichage pour votre jeu, sinon les graphismes seront étirés.\n\nLaissez sur 16:9 si vous n'êtes pas sûr.", "fr_FR": "Format d'affichage appliqué à la fenêtre du moteur de rendu.\n\nChangez cela uniquement si vous utilisez un mod changeant le format d'affichage pour votre jeu, sinon les graphismes seront étirés.\n\nLaissez sur 16:9 si vous n'êtes pas sûr.",
"he_IL": "", "he_IL": "",
"it_IT": "Proporzioni dello schermo applicate alla finestra di renderizzazione.\n\nCambialo solo se stai usando una mod di proporzioni per il tuo gioco, altrimenti la grafica verrà allungata.\n\nLasciare il 16:9 se incerto.", "it_IT": "Proporzioni dello schermo applicate alla finestra di renderizzazione.\n\nCambialo solo se stai usando una mod di proporzioni per il tuo gioco, altrimenti la grafica verrà allungata.\n\nLasciare il 16:9 se incerto.",
"ja_JP": "レンダリングウインドウに適用するアスペクト比です.\n\nゲームにアスペクト比を変更する mod を使用している場合のみ変更してください.\n\nわからない場合は16:9のままにしておいてください.\n", "ja_JP": "レンダリングウインドウに適用するアスペクト比です.\n\nゲームにアスペクト比を変更する mod を使用している場合のみ変更してください.\n\nわからない場合は16:9のままにしておいてください.\n",
@@ -17439,7 +17439,7 @@
"pl_PL": "Wersja {0}", "pl_PL": "Wersja {0}",
"pt_BR": "Versão {0}", "pt_BR": "Versão {0}",
"ru_RU": "Версия {0}", "ru_RU": "Версия {0}",
"sv_SE": "", "sv_SE": "Version {0}",
"th_TH": "เวอร์ชั่น {0}", "th_TH": "เวอร์ชั่น {0}",
"tr_TR": "Sürüm {0}", "tr_TR": "Sürüm {0}",
"uk_UA": "Версія {0}", "uk_UA": "Версія {0}",
@@ -17664,7 +17664,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "Ryujinx - Informação", "pt_BR": "Ryujinx - Informação",
"ru_RU": "Ryujinx - Информация", "ru_RU": "Ryujinx - Информация",
"sv_SE": "", "sv_SE": "Ryujinx - Info",
"th_TH": "Ryujinx ข้อมูล", "th_TH": "Ryujinx ข้อมูล",
"tr_TR": "Ryujinx - Bilgi", "tr_TR": "Ryujinx - Bilgi",
"uk_UA": "Ryujin x - Інформація", "uk_UA": "Ryujin x - Інформація",
@@ -18813,8 +18813,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Amiibo",
"sv_SE": "", "sv_SE": "Amiibo",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -19435,7 +19435,7 @@
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "XCI 파일 트리머", "ko_KR": "XCI 파일 트리머",
"no_NO": "", "no_NO": "XCI File Trimmer",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Уменьшение размера XCI файлов", "ru_RU": "Уменьшение размера XCI файлов",
@@ -19639,7 +19639,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "{0:n0} Мб", "ru_RU": "{0:n0} Мб",
"sv_SE": "", "sv_SE": "{0:n0} Mb",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "{0:n0} Мб", "uk_UA": "{0:n0} Мб",
@@ -20509,7 +20509,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": "",
@@ -20534,7 +20534,7 @@
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "Vulkan을 사용합니다.\nARM 맥에서 해당 플랫폼에서 잘 실행되는 게임을 플레이하는 경우 Metal 후단부를 사용합니다.", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -20914,7 +20914,7 @@
"pl_PL": "Głoś", "pl_PL": "Głoś",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Громкость", "ru_RU": "Громкость",
"sv_SE": "", "sv_SE": "Vol",
"th_TH": "ระดับเสียง", "th_TH": "ระดับเสียง",
"tr_TR": "Ses", "tr_TR": "Ses",
"uk_UA": "Гуч.", "uk_UA": "Гуч.",
@@ -21055,7 +21055,7 @@
"el_GR": "Όνομα", "el_GR": "Όνομα",
"en_US": "Name", "en_US": "Name",
"es_ES": "Nombre", "es_ES": "Nombre",
"fr_FR": "Nom\u00A0", "fr_FR": "Nom ",
"he_IL": "שם", "he_IL": "שם",
"it_IT": "Nome", "it_IT": "Nome",
"ja_JP": "名称", "ja_JP": "名称",
@@ -21388,8 +21388,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "FSR",
"sv_SE": "", "sv_SE": "FSR",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -21888,8 +21888,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "ldn_mitm",
"sv_SE": "", "sv_SE": "ldn_mitm",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -21913,8 +21913,8 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "RyuLDN",
"sv_SE": "", "sv_SE": "RyuLDN",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
@@ -22214,7 +22214,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Вертикальная синхронизация:", "ru_RU": "Вертикальная синхронизация:",
"sv_SE": "", "sv_SE": "VSync:",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "Вертикальна синхронізація (VSync):", "uk_UA": "Вертикальна синхронізація (VSync):",
@@ -22255,7 +22255,7 @@
"el_GR": "", "el_GR": "",
"en_US": "Switch", "en_US": "Switch",
"es_ES": "", "es_ES": "",
"fr_FR": "", "fr_FR": "Switch",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
@@ -22264,7 +22264,7 @@
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "Консоль", "ru_RU": "Консоль",
"sv_SE": "", "sv_SE": "Switch",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",

View File

@@ -13,10 +13,9 @@
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>
<Target Name="BuildValidationProj" BeforeTargets="BeforeBuild"> <Target Name="BuildValidationProj" BeforeTargets="BeforeRebuild">
<Message Text="Building Validation Project for $(TargetFramework)" Importance="high" Condition="'$(RuntimeIdentifier)' == ''" /> <MSBuild Projects="..\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj" Targets="Rebuild">
<Exec WorkingDirectory="..\Ryujinx.BuildValidationTasks\" Command="dotnet build -c Debug /clp:NoSummary" /> </MSBuild>
</Target> </Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))"> <Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
@@ -49,7 +48,6 @@
<PackageReference Include="Avalonia.Svg" /> <PackageReference Include="Avalonia.Svg" />
<PackageReference Include="Avalonia.Svg.Skia" /> <PackageReference Include="Avalonia.Svg.Skia" />
<PackageReference Include="CommandLineParser" /> <PackageReference Include="CommandLineParser" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="DiscordRichPresence" /> <PackageReference Include="DiscordRichPresence" />
<PackageReference Include="DynamicData" /> <PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" /> <PackageReference Include="FluentAvaloniaUI" />

View File

@@ -15,6 +15,7 @@
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -25,10 +26,10 @@
Padding="8" Padding="8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
SelectedItem="{Binding GridSelectedApplication}" ContextFlyout="{StaticResource ApplicationContextMenu}"
ContextFlyout="{Binding GridAppContextMenu}"
DoubleTapped="GameList_DoubleTapped" DoubleTapped="GameList_DoubleTapped"
ItemsSource="{Binding AppsObservableList}"> ItemsSource="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel> <ListBox.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<WrapPanel <WrapPanel

View File

@@ -26,5 +26,11 @@ namespace Ryujinx.Ava.UI.Controls
if (sender is ListBox { SelectedItem: ApplicationData selected }) if (sender is ListBox { SelectedItem: ApplicationData selected })
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent)); RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
} }
public void GameList_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (DataContext is MainWindowViewModel viewModel && sender is ListBox { SelectedItem: ApplicationData selected })
viewModel.GridSelectedApplication = selected;
}
} }
} }

View File

@@ -7,6 +7,7 @@
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:converters="clr-namespace:Avalonia.Data.Converters;assembly=Avalonia.Base"
d:DesignHeight="450" d:DesignHeight="450"
d:DesignWidth="800" d:DesignWidth="800"
Focusable="True" Focusable="True"
@@ -15,6 +16,7 @@
x:DataType="viewModels:MainWindowViewModel"> x:DataType="viewModels:MainWindowViewModel">
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<controls:ApplicationContextMenu x:Key="ApplicationContextMenu" />
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -26,10 +28,10 @@
Padding="8" Padding="8"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
SelectedItem="{Binding ListSelectedApplication}" ContextFlyout="{StaticResource ApplicationContextMenu}"
ContextFlyout="{Binding ListAppContextMenu}"
DoubleTapped="GameList_DoubleTapped" DoubleTapped="GameList_DoubleTapped"
ItemsSource="{Binding AppsObservableList}"> ItemsSource="{Binding AppsObservableList}"
SelectionChanged="GameList_SelectionChanged">
<ListBox.ItemsPanel> <ListBox.ItemsPanel>
<ItemsPanelTemplate> <ItemsPanelTemplate>
<StackPanel <StackPanel

View File

@@ -30,6 +30,12 @@ namespace Ryujinx.Ava.UI.Controls
RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent)); RaiseEvent(new ApplicationOpenedEventArgs(selected, ApplicationOpenedEvent));
} }
public void GameList_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (DataContext is MainWindowViewModel viewModel && sender is ListBox { SelectedItem: ApplicationData selected })
viewModel.ListSelectedApplication = selected;
}
private async void IdString_OnClick(object sender, RoutedEventArgs e) private async void IdString_OnClick(object sender, RoutedEventArgs e)
{ {
if (DataContext is not MainWindowViewModel mwvm) if (DataContext is not MainWindowViewModel mwvm)

View File

@@ -1,7 +1,6 @@
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
@@ -9,11 +8,42 @@ using System;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class AboutWindowViewModel : BaseModel, IDisposable public class AboutWindowViewModel : BaseModel, IDisposable
{ {
[ObservableProperty] private Bitmap _githubLogo; private Bitmap _githubLogo;
[ObservableProperty] private Bitmap _discordLogo; private Bitmap _discordLogo;
[ObservableProperty] private string _version;
private string _version;
public Bitmap GithubLogo
{
get => _githubLogo;
set
{
_githubLogo = value;
OnPropertyChanged();
}
}
public Bitmap DiscordLogo
{
get => _discordLogo;
set
{
_discordLogo = value;
OnPropertyChanged();
}
}
public string Version
{
get => _version;
set
{
_version = value;
OnPropertyChanged();
}
}
public string Developers => "GreemDev"; public string Developers => "GreemDev";

View File

@@ -1,10 +1,18 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System; using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public class BaseModel : ObservableObject public class BaseModel : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames) protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames)
{ {
OnPropertyChanged(firstPropertyName); OnPropertyChanged(firstPropertyName);

View File

@@ -2,7 +2,6 @@ using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -18,13 +17,13 @@ using Application = Avalonia.Application;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class DownloadableContentManagerViewModel : BaseModel public class DownloadableContentManagerViewModel : BaseModel
{ {
private readonly ApplicationLibrary _applicationLibrary; private readonly ApplicationLibrary _applicationLibrary;
private AvaloniaList<DownloadableContentModel> _downloadableContents = new(); private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new(); private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
[ObservableProperty] private AvaloniaList<DownloadableContentModel> _views = new(); private AvaloniaList<DownloadableContentModel> _views = new();
[ObservableProperty] private bool _showBundledContentNotice = false; private bool _showBundledContentNotice = false;
private string _search; private string _search;
private readonly ApplicationData _applicationData; private readonly ApplicationData _applicationData;
@@ -42,6 +41,26 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public AvaloniaList<DownloadableContentModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public AvaloniaList<DownloadableContentModel> SelectedDownloadableContents
{
get => _selectedDownloadableContents;
set
{
_selectedDownloadableContents = value;
OnPropertyChanged();
}
}
public string Search public string Search
{ {
get => _search; get => _search;
@@ -58,13 +77,26 @@ namespace Ryujinx.Ava.UI.ViewModels
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
} }
public bool ShowBundledContentNotice
{
get => _showBundledContentNotice;
set
{
_showBundledContentNotice = value;
OnPropertyChanged();
}
}
public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) public DownloadableContentManagerViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
{ {
_applicationLibrary = applicationLibrary; _applicationLibrary = applicationLibrary;
_applicationData = applicationData; _applicationData = applicationData;
_storageProvider = RyujinxApp.MainWindow.StorageProvider; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
_storageProvider = desktop.MainWindow.StorageProvider;
}
LoadDownloadableContents(); LoadDownloadableContents();
} }

View File

@@ -245,7 +245,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
_mainWindow = RyujinxApp.MainWindow; _mainWindow =
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
.ApplicationLifetime).MainWindow;
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner); AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);

View File

@@ -6,7 +6,6 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
@@ -55,112 +54,77 @@ using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class MainWindowViewModel : BaseModel public class MainWindowViewModel : BaseModel
{ {
private const int HotKeyPressDelayMs = 500; private const int HotKeyPressDelayMs = 500;
private delegate int LoadContentFromFolderDelegate(List<string> dirs, out int numRemoved); private delegate int LoadContentFromFolderDelegate(List<string> dirs, out int numRemoved);
[ObservableProperty] private ObservableCollectionExtended<ApplicationData> _applications; private ObservableCollectionExtended<ApplicationData> _applications;
[ObservableProperty] private string _aspectRatioStatusText; private string _aspectStatusText;
[ObservableProperty] private string _loadHeading;
[ObservableProperty] private string _cacheLoadStatus;
[ObservableProperty] private string _dockedStatusText;
[ObservableProperty] private string _fifoStatusText;
[ObservableProperty] private string _gameStatusText;
[ObservableProperty] private string _volumeStatusText;
[ObservableProperty] private string _gpuNameText;
[ObservableProperty] private string _backendText;
[ObservableProperty] private string _shaderCountText;
[ObservableProperty] private bool _showShaderCompilationHint;
[ObservableProperty] private bool _isFullScreen;
[ObservableProperty] private int _progressMaximum;
[ObservableProperty] private int _progressValue;
[ObservableProperty] private bool _showMenuAndStatusBar = true;
[ObservableProperty] private bool _showStatusSeparator;
[ObservableProperty] private Brush _progressBarForegroundColor;
[ObservableProperty] private Brush _progressBarBackgroundColor;
[ObservableProperty] private Brush _vSyncModeColor;
[ObservableProperty] private byte[] _selectedIcon;
[ObservableProperty] private int _statusBarProgressMaximum;
[ObservableProperty] private int _statusBarProgressValue;
[ObservableProperty] private string _statusBarProgressStatusText;
[ObservableProperty] private bool _statusBarProgressStatusVisible;
[ObservableProperty] private bool _isPaused;
[ObservableProperty] private bool _isLoadingIndeterminate = true;
[ObservableProperty] private bool _showAll;
[ObservableProperty] private string _lastScannedAmiiboId;
[ObservableProperty] private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
[ObservableProperty] private long _lastFullscreenToggle = Environment.TickCount64;
[ObservableProperty] private bool _showContent = true;
[ObservableProperty] private float _volumeBeforeMute;
[ObservableProperty] private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
[ObservableProperty] private Cursor _cursor;
[ObservableProperty] private string _title;
[ObservableProperty] private WindowState _windowState;
[ObservableProperty] private double _windowWidth;
[ObservableProperty] private double _windowHeight;
[ObservableProperty] private bool _isActive;
[ObservableProperty] private bool _isSubMenuOpen;
[ObservableProperty] private ApplicationContextMenu _listAppContextMenu;
[ObservableProperty] private ApplicationContextMenu _gridAppContextMenu;
private bool _showLoadProgress; private string _loadHeading;
private bool _isGameRunning; private string _cacheLoadStatus;
private bool _isAmiiboRequested;
private bool _isAmiiboBinRequested;
private string _searchText; private string _searchText;
private Timer _searchTimer; private Timer _searchTimer;
private string _dockedStatusText;
private string _vSyncModeText; private string _vSyncModeText;
private string _fifoStatusText;
private string _gameStatusText;
private string _volumeStatusText;
private string _gpuStatusText;
private string _shaderCountText;
private bool _isAmiiboRequested;
private bool _isAmiiboBinRequested;
private bool _showShaderCompilationHint;
private bool _isGameRunning;
private bool _isFullScreen;
private int _progressMaximum;
private int _progressValue;
private long _lastFullscreenToggle = Environment.TickCount64;
private bool _showLoadProgress;
private bool _showMenuAndStatusBar = true;
private bool _showStatusSeparator;
private Brush _progressBarForegroundColor;
private Brush _progressBarBackgroundColor;
private Brush _vSyncModeColor;
private byte[] _selectedIcon;
private bool _isAppletMenuActive;
private int _statusBarProgressMaximum;
private int _statusBarProgressValue;
private string _statusBarProgressStatusText;
private bool _statusBarProgressStatusVisible;
private bool _isPaused;
private bool _showContent = true;
private bool _isLoadingIndeterminate = true;
private bool _showAll;
private string _lastScannedAmiiboId;
private bool _statusBarVisible;
private ReadOnlyObservableCollection<ApplicationData> _appsObservableList;
private string _showUiKey = "F4"; private string _showUiKey = "F4";
private string _pauseKey = "F5"; private string _pauseKey = "F5";
private string _screenshotKey = "F8"; private string _screenshotKey = "F8";
private float _volume; private float _volume;
private bool _isAppletMenuActive; private float _volumeBeforeMute;
private bool _statusBarVisible; private string _backendText;
private bool _areMimeTypesRegistered = FileAssociationHelper.AreMimeTypesRegistered;
private bool _canUpdate = true; private bool _canUpdate = true;
private Cursor _cursor;
private string _title;
private ApplicationData _currentApplicationData; private ApplicationData _currentApplicationData;
private readonly AutoResetEvent _rendererWaitEvent; private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState;
private double _windowWidth;
private double _windowHeight;
private int _customVSyncInterval; private int _customVSyncInterval;
private int _customVSyncIntervalPercentageProxy; private int _customVSyncIntervalPercentageProxy;
private ApplicationData _listSelectedApplication;
private ApplicationData _gridSelectedApplication;
public ApplicationData ListSelectedApplication private bool _isActive;
{ private bool _isSubMenuOpen;
get => _listSelectedApplication;
set
{
_listSelectedApplication = value;
#pragma warning disable MVVMTK0034 public ApplicationData ListSelectedApplication;
if (_listSelectedApplication != null && _listAppContextMenu == null) public ApplicationData GridSelectedApplication;
ListAppContextMenu = new ApplicationContextMenu();
else if (_listSelectedApplication == null && _listAppContextMenu != null)
ListAppContextMenu = null!;
#pragma warning restore MVVMTK0034
OnPropertyChanged();
}
}
public ApplicationData GridSelectedApplication
{
get => _gridSelectedApplication;
set
{
_gridSelectedApplication = value;
#pragma warning disable MVVMTK0034
if (_gridSelectedApplication != null && _gridAppContextMenu == null)
GridAppContextMenu = new ApplicationContextMenu();
else if (_gridSelectedApplication == null && _gridAppContextMenu != null)
GridAppContextMenu = null!;
#pragma warning restore MVVMTK0034
OnPropertyChanged();
}
}
// Key is Title ID // Key is Title ID
public SafeDictionary<string, LdnGameData.Array> LdnData = []; public SafeDictionary<string, LdnGameData.Array> LdnData = [];
@@ -254,7 +218,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool CanUpdate public bool CanUpdate
{ {
get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(); get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false);
set set
{ {
_canUpdate = value; _canUpdate = value;
@@ -262,6 +226,49 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public Cursor Cursor
{
get => _cursor;
set
{
_cursor = value;
OnPropertyChanged();
}
}
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
{
get => _appsObservableList;
set
{
_appsObservableList = value;
OnPropertyChanged();
}
}
public bool IsPaused
{
get => _isPaused;
set
{
_isPaused = value;
OnPropertyChanged();
}
}
public long LastFullscreenToggle
{
get => _lastFullscreenToggle;
set
{
_lastFullscreenToggle = value;
OnPropertyChanged();
}
}
public bool StatusBarVisible public bool StatusBarVisible
{ {
get => _statusBarVisible && EnableNonGameRunningControls; get => _statusBarVisible && EnableNonGameRunningControls;
@@ -277,6 +284,17 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowFirmwareStatus => !ShowLoadProgress; public bool ShowFirmwareStatus => !ShowLoadProgress;
public bool ShowShaderCompilationHint
{
get => _showShaderCompilationHint;
set
{
_showShaderCompilationHint = value;
OnPropertyChanged();
}
}
public bool IsGameRunning public bool IsGameRunning
{ {
get => _isGameRunning; get => _isGameRunning;
@@ -332,6 +350,61 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string GameStatusText
{
get => _gameStatusText;
set
{
_gameStatusText = value;
OnPropertyChanged();
}
}
public bool IsFullScreen
{
get => _isFullScreen;
set
{
_isFullScreen = value;
OnPropertyChanged();
}
}
public bool IsSubMenuOpen
{
get => _isSubMenuOpen;
set
{
_isSubMenuOpen = value;
OnPropertyChanged();
}
}
public bool ShowAll
{
get => _showAll;
set
{
_showAll = value;
OnPropertyChanged();
}
}
public string LastScannedAmiiboId
{
get => _lastScannedAmiiboId;
set
{
_lastScannedAmiiboId = value;
OnPropertyChanged();
}
}
public ApplicationData SelectedApplication public ApplicationData SelectedApplication
{ {
get get
@@ -345,20 +418,87 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool OpenUserSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; public bool OpenUserSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool OpenDeviceSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool TrimXCIEnabled => XCIFileTrimmer.CanTrim(SelectedApplication.Path, new XCITrimmerLog.MainWindow(this)); public bool TrimXCIEnabled => XCIFileTrimmer.CanTrim(SelectedApplication.Path, new XCITrimmerLog.MainWindow(this));
public bool OpenBcatSaveDirectoryEnabled => SelectedApplication.HasControlHolder && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading
{
get => _loadHeading;
set
{
_loadHeading = value;
OnPropertyChanged();
}
}
public string CacheLoadStatus
{
get => _cacheLoadStatus;
set
{
_cacheLoadStatus = value;
OnPropertyChanged();
}
}
public Brush ProgressBarBackgroundColor
{
get => _progressBarBackgroundColor;
set
{
_progressBarBackgroundColor = value;
OnPropertyChanged();
}
}
public Brush ProgressBarForegroundColor
{
get => _progressBarForegroundColor;
set
{
_progressBarForegroundColor = value;
OnPropertyChanged();
}
}
public Brush VSyncModeColor
{
get => _vSyncModeColor;
set
{
_vSyncModeColor = value;
OnPropertyChanged();
}
}
public bool ShowCustomVSyncIntervalPicker public bool ShowCustomVSyncIntervalPicker
=> _isGameRunning && AppHost.Device.VSyncMode == VSyncMode.Custom;
public void UpdateVSyncIntervalPicker()
{ {
OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker)); get
{
if (_isGameRunning)
{
return AppHost.Device.VSyncMode ==
VSyncMode.Custom;
}
else
{
return false;
}
}
set
{
OnPropertyChanged();
}
} }
public int CustomVSyncIntervalPercentageProxy public int CustomVSyncIntervalPercentageProxy
@@ -411,6 +551,126 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public byte[] SelectedIcon
{
get => _selectedIcon;
set
{
_selectedIcon = value;
OnPropertyChanged();
}
}
public int ProgressMaximum
{
get => _progressMaximum;
set
{
_progressMaximum = value;
OnPropertyChanged();
}
}
public int ProgressValue
{
get => _progressValue;
set
{
_progressValue = value;
OnPropertyChanged();
}
}
public int StatusBarProgressMaximum
{
get => _statusBarProgressMaximum;
set
{
_statusBarProgressMaximum = value;
OnPropertyChanged();
}
}
public int StatusBarProgressValue
{
get => _statusBarProgressValue;
set
{
_statusBarProgressValue = value;
OnPropertyChanged();
}
}
public bool StatusBarProgressStatusVisible
{
get => _statusBarProgressStatusVisible;
set
{
_statusBarProgressStatusVisible = value;
OnPropertyChanged();
}
}
public string StatusBarProgressStatusText
{
get => _statusBarProgressStatusText;
set
{
_statusBarProgressStatusText = value;
OnPropertyChanged();
}
}
public string FifoStatusText
{
get => _fifoStatusText;
set
{
_fifoStatusText = value;
OnPropertyChanged();
}
}
public string GpuNameText
{
get => _gpuStatusText;
set
{
_gpuStatusText = value;
OnPropertyChanged();
}
}
public string ShaderCountText
{
get => _shaderCountText;
set
{
_shaderCountText = value;
OnPropertyChanged();
}
}
public string BackendText
{
get => _backendText;
set
{
_backendText = value;
OnPropertyChanged();
}
}
public string VSyncModeText public string VSyncModeText
{ {
get => _vSyncModeText; get => _vSyncModeText;
@@ -419,7 +679,39 @@ namespace Ryujinx.Ava.UI.ViewModels
_vSyncModeText = value; _vSyncModeText = value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker)); }
}
public string DockedStatusText
{
get => _dockedStatusText;
set
{
_dockedStatusText = value;
OnPropertyChanged();
}
}
public string AspectRatioStatusText
{
get => _aspectStatusText;
set
{
_aspectStatusText = value;
OnPropertyChanged();
}
}
public string VolumeStatusText
{
get => _volumeStatusText;
set
{
_volumeStatusText = value;
OnPropertyChanged();
} }
} }
@@ -443,6 +735,73 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public float VolumeBeforeMute
{
get => _volumeBeforeMute;
set
{
_volumeBeforeMute = value;
OnPropertyChanged();
}
}
public bool ShowStatusSeparator
{
get => _showStatusSeparator;
set
{
_showStatusSeparator = value;
OnPropertyChanged();
}
}
public bool ShowMenuAndStatusBar
{
get => _showMenuAndStatusBar;
set
{
_showMenuAndStatusBar = value;
OnPropertyChanged();
}
}
public bool IsLoadingIndeterminate
{
get => _isLoadingIndeterminate;
set
{
_isLoadingIndeterminate = value;
OnPropertyChanged();
}
}
public bool IsActive
{
get => _isActive;
set
{
_isActive = value;
OnPropertyChanged();
}
}
public bool ShowContent
{
get => _showContent;
set
{
_showContent = value;
OnPropertyChanged();
}
}
public bool IsAppletMenuActive public bool IsAppletMenuActive
{ {
get => _isAppletMenuActive && EnableNonGameRunningControls; get => _isAppletMenuActive && EnableNonGameRunningControls;
@@ -454,6 +813,39 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public WindowState WindowState
{
get => _windowState;
internal set
{
_windowState = value;
OnPropertyChanged();
}
}
public double WindowWidth
{
get => _windowWidth;
set
{
_windowWidth = value;
OnPropertyChanged();
}
}
public double WindowHeight
{
get => _windowHeight;
set
{
_windowHeight = value;
OnPropertyChanged();
}
}
public bool IsGrid => Glyph == Glyph.Grid; public bool IsGrid => Glyph == Glyph.Grid;
public bool IsList => Glyph == Glyph.List; public bool IsList => Glyph == Glyph.List;
@@ -497,6 +889,17 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string Title
{
get => _title;
set
{
_title = value;
OnPropertyChanged();
}
}
public bool ShowConsoleVisible public bool ShowConsoleVisible
{ {
get => ConsoleHelper.SetConsoleWindowStateSupported; get => ConsoleHelper.SetConsoleWindowStateSupported;
@@ -507,6 +910,27 @@ namespace Ryujinx.Ava.UI.ViewModels
get => FileAssociationHelper.IsTypeAssociationSupported; get => FileAssociationHelper.IsTypeAssociationSupported;
} }
public bool AreMimeTypesRegistered
{
get => _areMimeTypesRegistered;
set {
_areMimeTypesRegistered = value;
OnPropertyChanged();
}
}
public ObservableCollectionExtended<ApplicationData> Applications
{
get => _applications;
set
{
_applications = value;
OnPropertyChanged();
}
}
public Glyph Glyph public Glyph Glyph
{ {
get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value; get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value;
@@ -524,8 +948,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowNames public bool ShowNames
{ {
get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1; get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1; set
set
{ {
ConfigurationState.Instance.UI.ShowNames.Value = value; ConfigurationState.Instance.UI.ShowNames.Value = value;
@@ -1085,6 +1508,8 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync"; VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync";
ShowCustomVSyncIntervalPicker =
args.VSyncMode == VSyncMode.Custom.ToString();
DockedStatusText = args.DockedMode; DockedStatusText = args.DockedMode;
AspectRatioStatusText = args.AspectRatio; AspectRatioStatusText = args.AspectRatio;
GameStatusText = args.GameStatus; GameStatusText = args.GameStatus;

View File

@@ -1,7 +1,8 @@
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using Gommon; using Gommon;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -17,13 +18,13 @@ using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class ModManagerViewModel : BaseModel public class ModManagerViewModel : BaseModel
{ {
private readonly string _modJsonPath; private readonly string _modJsonPath;
private AvaloniaList<ModModel> _mods = new(); private AvaloniaList<ModModel> _mods = new();
[ObservableProperty] private AvaloniaList<ModModel> _views = new(); private AvaloniaList<ModModel> _views = new();
[ObservableProperty] private AvaloniaList<ModModel> _selectedMods = new(); private AvaloniaList<ModModel> _selectedMods = new();
private string _search; private string _search;
private readonly ulong _applicationId; private readonly ulong _applicationId;
@@ -43,6 +44,26 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public AvaloniaList<ModModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public AvaloniaList<ModModel> SelectedMods
{
get => _selectedMods;
set
{
_selectedMods = value;
OnPropertyChanged();
}
}
public string Search public string Search
{ {
get => _search; get => _search;
@@ -65,7 +86,10 @@ namespace Ryujinx.Ava.UI.ViewModels
_modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json"); _modJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationId.ToString("x16"), "mods.json");
_storageProvider = RyujinxApp.MainWindow.StorageProvider; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
_storageProvider = desktop.MainWindow.StorageProvider;
}
LoadMods(applicationId); LoadMods(applicationId);
} }
@@ -122,10 +146,8 @@ namespace Ryujinx.Ava.UI.ViewModels
.Filter(Filter) .Filter(Filter)
.Bind(out var view).AsObservableList(); .Bind(out var view).AsObservableList();
#pragma warning disable MVVMTK0034 // Event to update is fired below
_views.Clear(); _views.Clear();
_views.AddRange(view); _views.AddRange(view);
#pragma warning restore MVVMTK0034
SelectedMods = new(Views.Where(x => x.Enabled)); SelectedMods = new(Views.Where(x => x.Enabled));

View File

@@ -1,10 +1,9 @@
using CommunityToolkit.Mvvm.ComponentModel; using Gommon;
using Gommon;
using Ryujinx.Ava.Utilities.Configuration; using Ryujinx.Ava.Utilities.Configuration;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class SettingsHacksViewModel : BaseModel public class SettingsHacksViewModel : BaseModel
{ {
private readonly SettingsViewModel _baseViewModel; private readonly SettingsViewModel _baseViewModel;
@@ -15,11 +14,33 @@ namespace Ryujinx.Ava.UI.ViewModels
_baseViewModel = settingsVm; _baseViewModel = settingsVm;
} }
[ObservableProperty] private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
[ObservableProperty] private bool _shaderTranslationDelayEnabled = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay; private bool _shaderTranslationThreadSleep = ConfigurationState.Instance.Hacks.EnableShaderTranslationDelay;
private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay; private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderTranslationDelay;
public string ShaderTranslationDelayValueText => $"{ShaderTranslationDelay}ms"; public bool Xc2MenuSoftlockFixEnabled
{
get => _xc2MenuSoftlockFix;
set
{
_xc2MenuSoftlockFix = value;
OnPropertyChanged();
}
}
public bool ShaderTranslationDelayEnabled
{
get => _shaderTranslationThreadSleep;
set
{
_shaderTranslationThreadSleep = value;
OnPropertyChanged();
}
}
public string ShaderTranslationDelayTooltipText => $"Current value: {ShaderTranslationDelay}";
public int ShaderTranslationDelay public int ShaderTranslationDelay
{ {
@@ -28,7 +49,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_shaderTranslationSleepDelay = value; _shaderTranslationSleepDelay = value;
OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayValueText)); OnPropertiesChanged(nameof(ShaderTranslationDelay), nameof(ShaderTranslationDelayTooltipText));
} }
} }

View File

@@ -1,7 +1,6 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Gommon; using Gommon;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
@@ -47,9 +46,9 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _resolutionScale; private int _resolutionScale;
private int _graphicsBackendMultithreadingIndex; private int _graphicsBackendMultithreadingIndex;
private float _volume; private float _volume;
[ObservableProperty] private bool _isVulkanAvailable = true; private bool _isVulkanAvailable = true;
[ObservableProperty] private bool _gameDirectoryChanged; private bool _gameDirectoryChanged;
[ObservableProperty] private bool _autoloadDirectoryChanged; private bool _autoloadDirectoryChanged;
private readonly List<string> _gpuIds = new(); private readonly List<string> _gpuIds = new();
private int _graphicsBackendIndex; private int _graphicsBackendIndex;
private int _scalingFilter; private int _scalingFilter;
@@ -64,7 +63,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _networkInterfaceIndex; private int _networkInterfaceIndex;
private int _multiplayerModeIndex; private int _multiplayerModeIndex;
private string _ldnPassphrase; private string _ldnPassphrase;
[ObservableProperty] private string _ldnServer; private string _ldnServer;
public SettingsHacksViewModel DirtyHacks { get; } public SettingsHacksViewModel DirtyHacks { get; }
@@ -112,10 +111,43 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool IsVulkanAvailable
{
get => _isVulkanAvailable;
set
{
_isVulkanAvailable = value;
OnPropertyChanged();
}
}
public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS(); public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; public bool IsAppleSiliconMac => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
public bool GameDirectoryChanged
{
get => _gameDirectoryChanged;
set
{
_gameDirectoryChanged = value;
OnPropertyChanged();
}
}
public bool AutoloadDirectoryChanged
{
get => _autoloadDirectoryChanged;
set
{
_autoloadDirectoryChanged = value;
OnPropertyChanged();
}
}
public bool IsMacOS => OperatingSystem.IsMacOS(); public bool IsMacOS => OperatingSystem.IsMacOS();
public bool EnableDiscordIntegration { get; set; } public bool EnableDiscordIntegration { get; set; }
@@ -155,7 +187,14 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string CustomVSyncIntervalPercentageText => CustomVSyncIntervalPercentageProxy + "%"; public string CustomVSyncIntervalPercentageText
{
get
{
string text = CustomVSyncIntervalPercentageProxy + "%";
return text;
}
}
public bool EnableCustomVSyncInterval public bool EnableCustomVSyncInterval
{ {
@@ -317,6 +356,7 @@ namespace Ryujinx.Ava.UI.ViewModels
set set
{ {
_networkInterfaceIndex = value != -1 ? value : 0; _networkInterfaceIndex = value != -1 ? value : 0;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
} }
} }
@@ -326,6 +366,7 @@ namespace Ryujinx.Ava.UI.ViewModels
set set
{ {
_multiplayerModeIndex = value; _multiplayerModeIndex = value;
ConfigurationState.Instance.Multiplayer.Mode.Value = (MultiplayerMode)_multiplayerModeIndex;
} }
} }
@@ -334,6 +375,16 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsInvalidLdnPassphraseVisible { get; set; } public bool IsInvalidLdnPassphraseVisible { get; set; }
public string LdnServer
{
get => _ldnServer;
set
{
_ldnServer = value;
OnPropertyChanged();
}
}
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
{ {
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
@@ -415,10 +466,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public void MatchSystemTime() public void MatchSystemTime()
{ {
(DateTimeOffset dto, TimeSpan timeOfDay) = DateTimeOffset.Now.Extract(); var dto = DateTimeOffset.Now;
CurrentDate = dto; CurrentDate = new DateTimeOffset(dto.Year, dto.Month, dto.Day, 0, 0, 0, dto.Offset);
CurrentTime = timeOfDay;
CurrentTime = dto.TimeOfDay;
OnPropertyChanged(nameof(CurrentDate)); OnPropertyChanged(nameof(CurrentDate));
OnPropertyChanged(nameof(CurrentTime)); OnPropertyChanged(nameof(CurrentTime));
@@ -596,14 +648,16 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ShowTitleBar.Value = ShowTitleBar; config.ShowTitleBar.Value = ShowTitleBar;
config.HideCursor.Value = (HideCursorMode)HideCursor; config.HideCursor.Value = (HideCursorMode)HideCursor;
if (GameDirectoryChanged) if (_gameDirectoryChanged)
{ {
config.UI.GameDirs.Value = [..GameDirectories]; List<string> gameDirs = new(GameDirectories);
config.UI.GameDirs.Value = gameDirs;
} }
if (AutoloadDirectoryChanged) if (_autoloadDirectoryChanged)
{ {
config.UI.AutoloadDirs.Value = [..AutoloadDirectories]; List<string> autoloadDirs = new(AutoloadDirectories);
config.UI.AutoloadDirs.Value = autoloadDirs;
} }
config.UI.BaseStyle.Value = BaseStyleIndex switch config.UI.BaseStyle.Value = BaseStyleIndex switch
@@ -702,7 +756,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Multiplayer.LdnServer.Value = LdnServer; config.Multiplayer.LdnServer.Value = LdnServer;
// Dirty Hacks // Dirty Hacks
config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFix; config.Hacks.Xc2MenuSoftlockFix.Value = DirtyHacks.Xc2MenuSoftlockFixEnabled;
config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled; config.Hacks.EnableShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelayEnabled;
config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay; config.Hacks.ShaderTranslationDelay.Value = DirtyHacks.ShaderTranslationDelay;
@@ -713,8 +767,8 @@ namespace Ryujinx.Ava.UI.ViewModels
SaveSettingsEvent?.Invoke(); SaveSettingsEvent?.Invoke();
GameDirectoryChanged = false; _gameDirectoryChanged = false;
AutoloadDirectoryChanged = false; _autoloadDirectoryChanged = false;
} }
private static void RevertIfNotSaved() private static void RevertIfNotSaved()

View File

@@ -1,32 +1,74 @@
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Common.Models; using Ryujinx.Ava.Common.Models;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.Utilities.AppLibrary; using Ryujinx.Ava.Utilities.AppLibrary;
using Ryujinx.HLE.FileSystem;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Application = Avalonia.Application;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public record TitleUpdateViewModelNoUpdate; public record TitleUpdateViewNoUpdateSentinal();
public partial class TitleUpdateViewModel : BaseModel public class TitleUpdateViewModel : BaseModel
{ {
private ApplicationLibrary ApplicationLibrary { get; } private ApplicationLibrary ApplicationLibrary { get; }
private ApplicationData ApplicationData { get; } private ApplicationData ApplicationData { get; }
[ObservableProperty] private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
[ObservableProperty] private AvaloniaList<object> _views = new(); private AvaloniaList<object> _views = new();
[ObservableProperty] private object _selectedUpdate = new TitleUpdateViewModelNoUpdate(); private object _selectedUpdate = new TitleUpdateViewNoUpdateSentinal();
[ObservableProperty] private bool _showBundledContentNotice; private bool _showBundledContentNotice = false;
private readonly IStorageProvider _storageProvider; public AvaloniaList<TitleUpdateModel> TitleUpdates
{
get => _titleUpdates;
set
{
_titleUpdates = value;
OnPropertyChanged();
}
}
public AvaloniaList<object> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public object SelectedUpdate
{
get => _selectedUpdate;
set
{
_selectedUpdate = value;
OnPropertyChanged();
}
}
public bool ShowBundledContentNotice
{
get => _showBundledContentNotice;
set
{
_showBundledContentNotice = value;
OnPropertyChanged();
}
}
public IStorageProvider StorageProvider;
public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData) public TitleUpdateViewModel(ApplicationLibrary applicationLibrary, ApplicationData applicationData)
{ {
@@ -34,7 +76,10 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationData = applicationData; ApplicationData = applicationData;
_storageProvider = RyujinxApp.MainWindow.StorageProvider; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
StorageProvider = desktop.MainWindow.StorageProvider;
}
LoadUpdates(); LoadUpdates();
} }
@@ -45,7 +90,7 @@ namespace Ryujinx.Ava.UI.ViewModels
.Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase); .Where(it => it.TitleUpdate.TitleIdBase == ApplicationData.IdBase);
bool hasBundledContent = false; bool hasBundledContent = false;
SelectedUpdate = new TitleUpdateViewModelNoUpdate(); SelectedUpdate = new TitleUpdateViewNoUpdateSentinal();
foreach ((TitleUpdateModel update, bool isSelected) in updates) foreach ((TitleUpdateModel update, bool isSelected) in updates)
{ {
TitleUpdates.Add(update); TitleUpdates.Add(update);
@@ -71,12 +116,12 @@ namespace Ryujinx.Ava.UI.ViewModels
var selected = SelectedUpdate; var selected = SelectedUpdate;
Views.Clear(); Views.Clear();
Views.Add(new TitleUpdateViewModelNoUpdate()); Views.Add(new TitleUpdateViewNoUpdateSentinal());
Views.AddRange(sortedUpdates); Views.AddRange(sortedUpdates);
SelectedUpdate = selected; SelectedUpdate = selected;
if (SelectedUpdate is TitleUpdateViewModelNoUpdate) if (SelectedUpdate is TitleUpdateViewNoUpdateSentinal)
{ {
SelectedUpdate = Views[0]; SelectedUpdate = Views[0];
} }
@@ -134,7 +179,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
else if (update == SelectedUpdate as TitleUpdateModel) else if (update == SelectedUpdate as TitleUpdateModel)
{ {
SelectedUpdate = new TitleUpdateViewModelNoUpdate(); SelectedUpdate = new TitleUpdateViewNoUpdateSentinal();
} }
SortUpdates(); SortUpdates();
@@ -142,7 +187,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async Task Add() public async Task Add()
{ {
var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions var result = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{ {
AllowMultiple = true, AllowMultiple = true,
FileTypeFilter = new List<FilePickerFileType> FileTypeFilter = new List<FilePickerFileType>

View File

@@ -1,5 +1,4 @@
using Avalonia.Media; using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
@@ -21,12 +20,12 @@ using Image = SkiaSharp.SKImage;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
internal partial class UserFirmwareAvatarSelectorViewModel : BaseModel internal class UserFirmwareAvatarSelectorViewModel : BaseModel
{ {
private static readonly Dictionary<string, byte[]> _avatarStore = new(); private static readonly Dictionary<string, byte[]> _avatarStore = new();
[ObservableProperty] private ObservableCollection<ProfileImageModel> _images; private ObservableCollection<ProfileImageModel> _images;
[ObservableProperty] private Color _backgroundColor = Colors.White; private Color _backgroundColor = Colors.White;
private int _selectedIndex; private int _selectedIndex;
@@ -35,11 +34,27 @@ namespace Ryujinx.Ava.UI.ViewModels
_images = new ObservableCollection<ProfileImageModel>(); _images = new ObservableCollection<ProfileImageModel>();
LoadImagesFromStore(); LoadImagesFromStore();
PropertyChanged += (_, args) => }
public Color BackgroundColor
{
get => _backgroundColor;
set
{ {
if (args.PropertyName == nameof(BackgroundColor)) _backgroundColor = value;
ChangeImageBackground(); OnPropertyChanged();
}; ChangeImageBackground();
}
}
public ObservableCollection<ProfileImageModel> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
} }
public int SelectedIndex public int SelectedIndex
@@ -55,7 +70,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
else else
{ {
SelectedImage = Images[_selectedIndex].Data; SelectedImage = _images[_selectedIndex].Data;
} }
OnPropertyChanged(); OnPropertyChanged();

View File

@@ -1,9 +1,18 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
internal partial class UserProfileImageSelectorViewModel : BaseModel internal class UserProfileImageSelectorViewModel : BaseModel
{ {
[ObservableProperty] private bool _firmwareFound; private bool _firmwareFound;
public bool FirmwareFound
{
get => _firmwareFound;
set
{
_firmwareFound = value;
OnPropertyChanged();
}
}
} }
} }

View File

@@ -1,4 +1,3 @@
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@@ -9,31 +8,74 @@ using System.Collections.ObjectModel;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
public partial class UserSaveManagerViewModel : BaseModel public class UserSaveManagerViewModel : BaseModel
{ {
[ObservableProperty] private int _sortIndex; private int _sortIndex;
[ObservableProperty] private int _orderIndex; private int _orderIndex;
[ObservableProperty] private string _search; private string _search;
[ObservableProperty] private ObservableCollection<SaveModel> _saves = new(); private ObservableCollection<SaveModel> _saves = new();
[ObservableProperty] private ObservableCollection<SaveModel> _views = new(); private ObservableCollection<SaveModel> _views = new();
private readonly AccountManager _accountManager; private readonly AccountManager _accountManager;
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
public int SortIndex
{
get => _sortIndex;
set
{
_sortIndex = value;
OnPropertyChanged();
Sort();
}
}
public int OrderIndex
{
get => _orderIndex;
set
{
_orderIndex = value;
OnPropertyChanged();
Sort();
}
}
public string Search
{
get => _search;
set
{
_search = value;
OnPropertyChanged();
Sort();
}
}
public ObservableCollection<SaveModel> Saves
{
get => _saves;
set
{
_saves = value;
OnPropertyChanged();
Sort();
}
}
public ObservableCollection<SaveModel> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public UserSaveManagerViewModel(AccountManager accountManager) public UserSaveManagerViewModel(AccountManager accountManager)
{ {
_accountManager = accountManager; _accountManager = accountManager;
PropertyChanged += (_, evt) =>
{
if (evt.PropertyName is
nameof(SortIndex) or
nameof(OrderIndex) or
nameof(Search) or
nameof(Saves))
{
Sort();
}
};
} }
public void Sort() public void Sort()
@@ -43,10 +85,8 @@ namespace Ryujinx.Ava.UI.ViewModels
.Sort(GetComparer()) .Sort(GetComparer())
.Bind(out var view).AsObservableList(); .Bind(out var view).AsObservableList();
#pragma warning disable MVVMTK0034
_views.Clear(); _views.Clear();
_views.AddRange(view); _views.AddRange(view);
#pragma warning restore MVVMTK0034
OnPropertyChanged(nameof(Views)); OnPropertyChanged(nameof(Views));
} }
@@ -54,7 +94,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
if (arg is SaveModel save) if (arg is SaveModel save)
{ {
return string.IsNullOrWhiteSpace(Search) || save.Title.ToLower().Contains(Search.ToLower()); return string.IsNullOrWhiteSpace(_search) || save.Title.ToLower().Contains(_search.ToLower());
} }
return false; return false;

View File

@@ -247,6 +247,7 @@
Header="{ext:Locale MenuBarActionsScanAmiiboBin}" Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
Icon="{ext:Icon mdi-cube-scan}" Icon="{ext:Icon mdi-cube-scan}"
IsVisible="{Binding CanScanAmiiboBinaries}" IsVisible="{Binding CanScanAmiiboBinaries}"
InputGesture="Ctrl + B"
IsEnabled="{Binding IsAmiiboBinRequested}" /> IsEnabled="{Binding IsAmiiboBinRequested}" />
<MenuItem <MenuItem
Command="{Binding TakeScreenshot}" Command="{Binding TakeScreenshot}"

View File

@@ -41,7 +41,7 @@ namespace Ryujinx.Ava.UI.Views.Main
private void VSyncMode_PointerReleased(object sender, PointerReleasedEventArgs e) private void VSyncMode_PointerReleased(object sender, PointerReleasedEventArgs e)
{ {
Window.ViewModel.ToggleVSyncMode(); Window.ViewModel.ToggleVSyncMode();
Logger.Info?.PrintMsg(LogClass.Application, $"VSync Mode toggled to: {Window.ViewModel.AppHost.Device.VSyncMode}"); Logger.Info?.Print(LogClass.Application, $"VSync Mode toggled to: {Window.ViewModel.AppHost.Device.VSyncMode}");
} }
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e) private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)

View File

@@ -26,6 +26,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
DataContext = ViewModel = window.ViewModel; DataContext = ViewModel = window.ViewModel;
} }
} }
public void Sort_Checked(object sender, RoutedEventArgs args) public void Sort_Checked(object sender, RoutedEventArgs args)

View File

@@ -37,7 +37,7 @@
ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}"> ToolTip.Tip="{Binding DirtyHacks.Xc2MenuFixTooltip}">
<CheckBox <CheckBox
Margin="0" Margin="0"
IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFix}"/> IsChecked="{Binding DirtyHacks.Xc2MenuSoftlockFixEnabled}"/>
<TextBlock <TextBlock
VerticalAlignment="Center" VerticalAlignment="Center"
Text="Xenoblade Chronicles 2 Menu Softlock Fix" /> Text="Xenoblade Chronicles 2 Menu Softlock Fix" />
@@ -54,27 +54,21 @@
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="Arbitrary Delay on Shader Translation"/> Text="Arbitrary Delay on Shader Translation"/>
</StackPanel> </StackPanel>
<StackPanel <Slider IsVisible="{Binding DirtyHacks.ShaderTranslationDelayEnabled}"
IsVisible="{Binding DirtyHacks.ShaderTranslationDelayEnabled}" HorizontalAlignment="Center"
Margin="0,10,0,0" Value="{Binding DirtyHacks.ShaderTranslationDelay}"
Orientation="Horizontal" ToolTip.Tip="{Binding DirtyHacks.ShaderTranslationDelayTooltipText}"
HorizontalAlignment="Center"> Width="175"
<Slider HorizontalAlignment="Center" Margin="0,-3,0,0"
Value="{Binding DirtyHacks.ShaderTranslationDelay}" Height="32"
Width="175" Padding="0,-5"
Margin="0,-3,0,0" TickFrequency="1"
Height="32" IsSnapToTickEnabled="True"
Padding="0,-5" LargeChange="10"
TickFrequency="1" SmallChange="1"
IsSnapToTickEnabled="True" VerticalAlignment="Center"
LargeChange="10" Minimum="1"
SmallChange="1" Maximum="1000" />
VerticalAlignment="Center"
Minimum="1"
Maximum="1000" />
<TextBlock Margin="5,0"
Text="{Binding DirtyHacks.ShaderTranslationDelayValueText}"/>
</StackPanel>
<Separator/> <Separator/>
</StackPanel> </StackPanel>
</Border> </Border>

View File

@@ -40,6 +40,7 @@
<KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" /> <KeyBinding Gesture="F9" Command="{Binding ToggleDockMode}" />
<KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" /> <KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
<KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" /> <KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" />
<KeyBinding Gesture="Ctrl+B" Command="{Binding OpenBinFile}" />
</Window.KeyBindings> </Window.KeyBindings>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RowDefinitions="*">
<helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" /> <helpers:OffscreenTextBox IsEnabled="False" Opacity="0" Name="HiddenTextBox" IsHitTestVisible="False" IsTabStop="False" />

View File

@@ -93,7 +93,6 @@
Padding="10" Padding="10"
MinWidth="0" MinWidth="0"
MinHeight="0" MinHeight="0"
ToolTip.Tip="{Binding Path}"
Click="OpenLocation"> Click="OpenLocation">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="OpenFolder" Symbol="OpenFolder"

View File

@@ -95,7 +95,7 @@
</Panel> </Panel>
</DataTemplate> </DataTemplate>
<DataTemplate <DataTemplate
DataType="viewModels:TitleUpdateViewModelNoUpdate"> DataType="viewModels:TitleUpdateViewNoUpdateSentinal">
<Panel <Panel
Height="33" Height="33"
Margin="10"> Margin="10">

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
public string Path { get; set; } public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
public bool HasControlHolder => ControlHolder.ByteSpan.Length > 0 && !ControlHolder.ByteSpan.IsZeros(); public bool HasControlHolder => ControlHolder.ByteSpan.Length > 0;
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);

View File

@@ -1,4 +1,3 @@
using Gommon;
using LibHac.Ns; using LibHac.Ns;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -23,7 +22,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId; LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
return new Array(receivedData.Where(game => return new Array(receivedData.Where(game =>
communicationId.Items.Contains(game.TitleId.ToULong()) communicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16))
)); ));
} }

View File

@@ -272,7 +272,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
public MemoryManagerMode MemoryManagerMode { get; set; } public MemoryManagerMode MemoryManagerMode { get; set; }
/// <summary> /// <summary>
/// Expands the RAM amount on the emulated system /// Expands the RAM amount on the emulated system from 4GiB to 8GiB
/// </summary> /// </summary>
public MemoryConfiguration DramSize { get; set; } public MemoryConfiguration DramSize { get; set; }

View File

@@ -645,9 +645,6 @@ namespace Ryujinx.Ava.Utilities.Configuration
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe) private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
{ {
if (!ShowDirtyHacks)
return;
var newHacks = EnabledHacks.Select(x => x.Hack) var newHacks = EnabledHacks.Select(x => x.Hack)
.JoinToString(", "); .JoinToString(", ");
@@ -669,14 +666,14 @@ namespace Ryujinx.Ava.Utilities.Configuration
List<EnabledDirtyHack> enabledHacks = []; List<EnabledDirtyHack> enabledHacks = [];
if (Xc2MenuSoftlockFix) if (Xc2MenuSoftlockFix)
Apply(DirtyHack.Xc2MenuSoftlockFix); Apply(DirtyHacks.Xc2MenuSoftlockFix);
if (EnableShaderTranslationDelay) if (EnableShaderTranslationDelay)
Apply(DirtyHack.ShaderTranslationDelay, ShaderTranslationDelay); Apply(DirtyHacks.ShaderCompilationThreadSleep, ShaderTranslationDelay);
return enabledHacks.ToArray(); return enabledHacks.ToArray();
void Apply(DirtyHack hack, int value = 0) void Apply(DirtyHacks hack, int value = 0)
{ {
enabledHacks.Add(new EnabledDirtyHack(hack, value)); enabledHacks.Add(new EnabledDirtyHack(hack, value));
} }