Compare commits

..

1 Commits

Author SHA1 Message Date
Evan Husted
8b3a945b5f misc: Dirty Hacks
Enable this settings screen via a boolean in Config.json
First one is the xb2 menu softlock fix
2024-12-28 22:04:21 -06:00
28 changed files with 462 additions and 359 deletions

View File

@@ -255,12 +255,13 @@ Global
{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.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.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.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.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU

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 : ValidationTask
{
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,40 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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,16 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<Target Name="PostBuildTarget" AfterTargets="AfterBuild">
<Message Text="Running Validation Project" Importance="high" />
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
<Exec WorkingDirectory="$(ProjectDir)bin\Debug\$(TargetFramework)\"
Command="dotnet Ryujinx.BuildValidationTasks.dll &quot;$(ProjectDir)..\..\\&quot;"
ConsoleToMsBuild="true"
/>
<UsingTask TaskName="Ryujinx.BuildValidationTasks.LocaleValidationTask" TaskFactory="TaskHostFactory" AssemblyFile="$(OutDir)Ryujinx.BuildValidationTasks.dll" />
<Target Name="LocalesJsonValidation" AfterTargets="AfterRebuild">
<LocaleValidationTask />
</Target>
</Project>
</Project>

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ryujinx.BuildValidationTasks
{
public interface ValidationTask
{
public bool Execute(string projectPath, bool isGitRunner);
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace Ryujinx.Common.Configuration
{
[Flags]
public enum DirtyHacks
{
None = 0,
Xc2MenuSoftlockFix = 1 << 10
}
}

View File

@@ -8,6 +8,8 @@ namespace Ryujinx.Common
{
public static class TitleIDs
{
public static Optional<string> CurrentApplication;
public static GraphicsBackend SelectGraphicsBackend(string titleId, GraphicsBackend currentBackend)
{
switch (currentBackend)

View File

@@ -47,12 +47,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public static bool EnableMacroHLE = true;
/// <summary>
/// Title id of the current running game.
/// Used by the shader cache.
/// </summary>
public static string TitleId;
/// <summary>
/// Enables or disables the shader cache.
/// </summary>

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
@@ -116,8 +117,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
return GraphicsConfig.EnableShaderCache && TitleIDs.CurrentApplication.HasValue
? Path.Combine(AppDataManager.GamesDirPath, TitleIDs.CurrentApplication, "cache", "shader")
: null;
}

View File

@@ -188,6 +188,11 @@ namespace Ryujinx.HLE
/// An action called when HLE force a refresh of output after docked mode changed.
/// </summary>
public Action RefreshInputConfig { internal get; set; }
/**
* The desired hacky workarounds.
*/
public DirtyHacks Hacks { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager,
@@ -218,7 +223,8 @@ namespace Ryujinx.HLE
bool multiplayerDisableP2p,
string multiplayerLdnPassphrase,
string multiplayerLdnServer,
int customVSyncInterval)
int customVSyncInterval,
DirtyHacks dirtyHacks = DirtyHacks.None)
{
VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager;
@@ -250,6 +256,7 @@ namespace Ryujinx.HLE
MultiplayerDisableP2p = multiplayerDisableP2p;
MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks;
}
}
}

View File

@@ -1,6 +1,9 @@
using LibHac;
using LibHac.Common;
using LibHac.Sf;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
{
@@ -13,6 +16,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
_baseStorage = SharedRef<LibHac.FsSrv.Sf.IStorage>.CreateMove(ref baseStorage);
}
private const string Xc2TitleId = "0100e95004038000";
[CommandCmif(0)]
// Read(u64 offset, u64 length) -> buffer<u8, 0x46, 0> buffer
public ResultCode Read(ServiceCtx context)
@@ -33,6 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
if (context.Device.DirtyHacks.HasFlag(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication == Xc2TitleId)
{
// Add a load-bearing sleep to avoid XC2 softlock
// https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357
Thread.Sleep(2);
}
return (ResultCode)result.Value;
}

View File

@@ -4,6 +4,7 @@ using LibHac.Fs.Fsa;
using LibHac.Loader;
using LibHac.Ns;
using LibHac.Tools.FsSystem;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Loaders.Executables;
@@ -102,7 +103,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
}
// Initialize GPU.
Graphics.Gpu.GraphicsConfig.TitleId = programId.ToString("X16");
TitleIDs.CurrentApplication = programId.ToString("X16");
device.Gpu.HostInitalized.Set();
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))

View File

@@ -6,6 +6,7 @@ using LibHac.Ns;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions;
@@ -204,7 +205,7 @@ namespace Ryujinx.HLE.Loaders.Processes
}
// Explicitly null TitleId to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null;
TitleIDs.CurrentApplication = default;
_device.Gpu.HostInitalized.Set();
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,

View File

@@ -37,6 +37,8 @@ namespace Ryujinx.HLE
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
public DirtyHacks DirtyHacks { get; }
public Switch(HLEConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration.GpuRenderer);
@@ -72,6 +74,7 @@ namespace Ryujinx.HLE
System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
DirtyHacks = Configuration.Hacks;
UpdateVSyncInterval();
#pragma warning restore IDE0055
}

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.UI.Common.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
public const int CurrentVersion = 57;
public const int CurrentVersion = 58;
/// <summary>
/// Version of the configuration file format
@@ -429,7 +429,17 @@ namespace Ryujinx.UI.Common.Configuration
/// Uses Hypervisor over JIT if available
/// </summary>
public bool UseHypervisor { get; set; }
/**
* Show toggles for dirty hacks in the UI.
*/
public bool ShowDirtyHacks { get; set; }
/**
* The packed value of the enabled dirty hacks.
*/
public int EnabledDirtyHacks { get; set; }
/// <summary>
/// Loads a configuration file from disk
/// </summary>

View File

@@ -735,6 +735,9 @@ namespace Ryujinx.UI.Common.Configuration
Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p;
Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase;
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks;
Hacks.Xc2MenuSoftlockFix.Value = ((DirtyHacks)configurationFileFormat.EnabledDirtyHacks).HasFlag(DirtyHacks.Xc2MenuSoftlockFix);
if (configurationFileUpdated)
{

View File

@@ -1,4 +1,5 @@
using ARMeilleure;
using Gommon;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Configuration.Hid;
@@ -617,6 +618,49 @@ namespace Ryujinx.UI.Common.Configuration
}
}
public class HacksSection
{
/// <summary>
/// Show toggles for dirty hacks in the UI.
/// </summary>
public ReactiveObject<bool> ShowDirtyHacks { get; private set; }
public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; }
public HacksSection()
{
ShowDirtyHacks = new ReactiveObject<bool>();
Xc2MenuSoftlockFix = new ReactiveObject<bool>();
Xc2MenuSoftlockFix.Event += HackChanged;
}
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
{
Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: {EnabledHacks}", "LogValueChange");
}
public DirtyHacks EnabledHacks
{
get
{
DirtyHacks dirtyHacks = DirtyHacks.None;
if (Xc2MenuSoftlockFix)
Apply(DirtyHacks.Xc2MenuSoftlockFix);
return dirtyHacks;
void Apply(DirtyHacks hack)
{
if (dirtyHacks is not DirtyHacks.None)
dirtyHacks |= hack;
else
dirtyHacks = hack;
}
}
}
}
/// <summary>
/// The default configuration instance
/// </summary>
@@ -651,6 +695,11 @@ namespace Ryujinx.UI.Common.Configuration
/// The Multiplayer section
/// </summary>
public MultiplayerSection Multiplayer { get; private set; }
/**
* The Dirty Hacks section
*/
public HacksSection Hacks { get; private set; }
/// <summary>
/// Enables or disables Discord Rich Presence
@@ -700,6 +749,7 @@ namespace Ryujinx.UI.Common.Configuration
Graphics = new GraphicsSection();
Hid = new HidSection();
Multiplayer = new MultiplayerSection();
Hacks = new HacksSection();
EnableDiscordIntegration = new ReactiveObject<bool>();
CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>();

View File

@@ -138,6 +138,8 @@ namespace Ryujinx.UI.Common.Configuration
MultiplayerDisableP2p = Multiplayer.DisableP2p,
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
LdnServer = Multiplayer.LdnServer,
ShowDirtyHacks = Hacks.ShowDirtyHacks,
EnabledDirtyHacks = (int)Hacks.EnabledHacks,
};
return configurationFile;

View File

@@ -311,6 +311,7 @@ namespace Ryujinx.Ava
Device.VSyncMode = e.NewValue;
Device.UpdateVSyncInterval();
}
_renderer.Window?.ChangeVSyncMode(e.NewValue);
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
@@ -923,7 +924,7 @@ namespace Ryujinx.Ava
// Initialize Configuration.
var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value;
Device = new HLE.Switch(new HLEConfiguration(
Device = new Switch(new HLEConfiguration(
VirtualFileSystem,
_viewModel.LibHacHorizonManager,
ContentManager,
@@ -953,7 +954,8 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.DisableP2p,
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
ConfigurationState.Instance.Multiplayer.LdnServer,
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value));
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : DirtyHacks.None));
}
private static IHardwareDeviceDriver InitializeAudio()

View File

@@ -1564,7 +1564,7 @@
"pl_PL": "Wersja",
"pt_BR": "Versão",
"ru_RU": "Версия",
"sv_SE": "",
"sv_SE": "Version",
"th_TH": "เวอร์ชั่น",
"tr_TR": "Sürüm",
"uk_UA": "Версія",
@@ -2213,8 +2213,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "ExeFS",
"sv_SE": "ExeFS",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -2263,8 +2263,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "RomFS",
"sv_SE": "RomFS",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -2310,7 +2310,7 @@
"it_IT": "",
"ja_JP": "ロゴ",
"ko_KR": "로고",
"no_NO": "",
"no_NO": "Logo",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Лого",
@@ -3260,11 +3260,11 @@
"it_IT": "Sistema",
"ja_JP": "システム",
"ko_KR": "시스템",
"no_NO": "",
"no_NO": "System",
"pl_PL": "",
"pt_BR": "Sistema",
"ru_RU": "Система",
"sv_SE": "",
"sv_SE": "System",
"th_TH": "ระบบ",
"tr_TR": "Sistem",
"uk_UA": "Система",
@@ -3335,11 +3335,11 @@
"it_IT": "Giappone",
"ja_JP": "日本",
"ko_KR": "일본",
"no_NO": "",
"no_NO": "Japan",
"pl_PL": "Japonia",
"pt_BR": "Japão",
"ru_RU": "Япония",
"sv_SE": "",
"sv_SE": "Japan",
"th_TH": "ญี่ปุ่น",
"tr_TR": "Japonya",
"uk_UA": "Японія",
@@ -3360,11 +3360,11 @@
"it_IT": "Stati Uniti d'America",
"ja_JP": "アメリカ",
"ko_KR": "미국",
"no_NO": "",
"no_NO": "USA",
"pl_PL": "Stany Zjednoczone",
"pt_BR": "EUA",
"ru_RU": "США",
"sv_SE": "",
"sv_SE": "USA",
"th_TH": "สหรัฐอเมริกา",
"tr_TR": "ABD",
"uk_UA": "США",
@@ -3410,7 +3410,7 @@
"it_IT": "",
"ja_JP": "オーストラリア",
"ko_KR": "호주",
"no_NO": "",
"no_NO": "Australia",
"pl_PL": "",
"pt_BR": "Austrália",
"ru_RU": "Австралия",
@@ -3460,11 +3460,11 @@
"it_IT": "Corea",
"ja_JP": "韓国",
"ko_KR": "한국",
"no_NO": "",
"no_NO": "Korea",
"pl_PL": "",
"pt_BR": "Coreia",
"ru_RU": "Корея",
"sv_SE": "",
"sv_SE": "Korea",
"th_TH": "เกาหลี",
"tr_TR": "Kore",
"uk_UA": "Корея",
@@ -3485,11 +3485,11 @@
"it_IT": "",
"ja_JP": "台湾",
"ko_KR": "대만",
"no_NO": "",
"no_NO": "Taiwan",
"pl_PL": "Tajwan",
"pt_BR": "",
"ru_RU": "Тайвань",
"sv_SE": "",
"sv_SE": "Taiwan",
"th_TH": "ไต้หวัน",
"tr_TR": "Tayvan",
"uk_UA": "Тайвань",
@@ -3955,7 +3955,7 @@
"el_GR": "Ζώνη Ώρας Συστήματος:",
"en_US": "System Time Zone:",
"es_ES": "Zona horaria del sistema:",
"fr_FR": "Fuseau horaire du système\u00A0:",
"fr_FR": "Fuseau horaire du système :",
"he_IL": "אזור זמן מערכת:",
"it_IT": "Fuso orario del sistema:",
"ja_JP": "タイムゾーン:",
@@ -4135,11 +4135,11 @@
"it_IT": "",
"ja_JP": "ダミー",
"ko_KR": "더미",
"no_NO": "",
"no_NO": "Dummy",
"pl_PL": "Atrapa",
"pt_BR": "Nenhuma",
"ru_RU": "Без звука",
"sv_SE": "",
"sv_SE": "Dummy",
"th_TH": "",
"tr_TR": "Yapay",
"uk_UA": "",
@@ -4163,8 +4163,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "OpenAL",
"sv_SE": "OpenAL",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4188,8 +4188,8 @@
"no_NO": "Lyd Inn/Ut",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "SoundIO",
"sv_SE": "SoundIO",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4213,8 +4213,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "SDL2",
"sv_SE": "SDL2",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4230,12 +4230,12 @@
"el_GR": "Μικροδιορθώσεις",
"en_US": "Hacks",
"es_ES": "",
"fr_FR": "",
"fr_FR": "Hacks",
"he_IL": "האצות",
"it_IT": "Espedienti",
"ja_JP": "ハック",
"ko_KR": "핵",
"no_NO": "",
"no_NO": "Hacks",
"pl_PL": "Hacki",
"pt_BR": "",
"ru_RU": "Хаки",
@@ -4314,7 +4314,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "4ГиБ",
"sv_SE": "",
"sv_SE": "4GiB",
"th_TH": "",
"tr_TR": "",
"uk_UA": "4Гб",
@@ -4339,7 +4339,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "6ГиБ",
"sv_SE": "",
"sv_SE": "6GiB",
"th_TH": "",
"tr_TR": "",
"uk_UA": "6Гб",
@@ -4364,7 +4364,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "8ГиБ",
"sv_SE": "",
"sv_SE": "8GiB",
"th_TH": "",
"tr_TR": "",
"uk_UA": "8Гб",
@@ -4389,7 +4389,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "12ГиБ",
"sv_SE": "",
"sv_SE": "12GiB",
"th_TH": "",
"tr_TR": "",
"uk_UA": "12Гб",
@@ -4585,11 +4585,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "2배",
"no_NO": "",
"no_NO": "2x",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "2x",
"sv_SE": "2x",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4610,11 +4610,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "4배",
"no_NO": "",
"no_NO": "4x",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "4x",
"sv_SE": "4x",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4635,11 +4635,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "8배",
"no_NO": "",
"no_NO": "8x",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "8x",
"sv_SE": "8x",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4660,11 +4660,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "16배",
"no_NO": "",
"no_NO": "16x",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "16x",
"sv_SE": "16x",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4760,11 +4760,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "2배(1440p/2160p)",
"no_NO": "",
"no_NO": "2x (1440p/2160p)",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "2x (1440p/2160p)",
"sv_SE": "2x (1440p/2160p)",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4785,11 +4785,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "3배(2160p/3240p)",
"no_NO": "",
"no_NO": "3x (2160p/3240p)",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "3x (2160p/3240p)",
"sv_SE": "3x (2160p/3240p)",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4863,8 +4863,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "4:3",
"sv_SE": "4:3",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4888,8 +4888,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "16:9",
"sv_SE": "16:9",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4913,8 +4913,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "16:10",
"sv_SE": "16:10",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4938,8 +4938,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "21:9",
"sv_SE": "21:9",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -4960,11 +4960,11 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"no_NO": "32:9",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "32:9",
"sv_SE": "32:9",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -5060,7 +5060,7 @@
"it_IT": "Log",
"ja_JP": "ロギング",
"ko_KR": "로그 기록",
"no_NO": "",
"no_NO": "Logging",
"pl_PL": "Dziennik zdarzeń",
"pt_BR": "Log",
"ru_RU": "Журналирование",
@@ -5085,7 +5085,7 @@
"it_IT": "Log",
"ja_JP": "ロギング",
"ko_KR": "로그 기록",
"no_NO": "",
"no_NO": "Logging",
"pl_PL": "Dziennik zdarzeń",
"pt_BR": "Log",
"ru_RU": "Журналирование",
@@ -6113,8 +6113,8 @@
"no_NO": "",
"pl_PL": "Pro Kontroler",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Pro Controller",
"sv_SE": "Pro Controller",
"th_TH": "โปรคอนโทรลเลอร์",
"tr_TR": "Profesyonel Kumanda",
"uk_UA": "Контролер Pro",
@@ -8088,8 +8088,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Enter",
"sv_SE": "Enter",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8114,7 +8114,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Esc",
"sv_SE": "",
"sv_SE": "Escape",
"th_TH": "",
"tr_TR": "Esc",
"uk_UA": "",
@@ -8163,8 +8163,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Tab",
"sv_SE": "Tab",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8188,8 +8188,8 @@
"no_NO": "Tilbaketast",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Backspace",
"sv_SE": "Backspace",
"th_TH": "",
"tr_TR": "Geri tuşu",
"uk_UA": "",
@@ -8213,8 +8213,8 @@
"no_NO": "Sett inn",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Insert",
"sv_SE": "Insert",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8238,8 +8238,8 @@
"no_NO": "Slett",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Delete",
"sv_SE": "Delete",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8263,8 +8263,8 @@
"no_NO": "Side opp",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Page Up",
"sv_SE": "Page Up",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8288,8 +8288,8 @@
"no_NO": "Side ned",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Page Down",
"sv_SE": "Page Down",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8313,8 +8313,8 @@
"no_NO": "Hjem",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Home",
"sv_SE": "Home",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8338,8 +8338,8 @@
"no_NO": "Avslutt",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "End",
"sv_SE": "End",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8363,8 +8363,8 @@
"no_NO": "Skiftelås",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Caps Lock",
"sv_SE": "Caps Lock",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8388,8 +8388,8 @@
"no_NO": "Rullelås",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Scroll Lock",
"sv_SE": "Scroll Lock",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8413,8 +8413,8 @@
"no_NO": "Skjermbilde",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Print Screen",
"sv_SE": "Print Screen",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8438,8 +8438,8 @@
"no_NO": "Stans midlertidig",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Pause",
"sv_SE": "Pause",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8463,8 +8463,8 @@
"no_NO": "Numerisk Lås",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Num Lock",
"sv_SE": "Num Lock",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8514,7 +8514,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 0",
"sv_SE": "",
"sv_SE": "Keypad 0",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8539,7 +8539,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 1",
"sv_SE": "",
"sv_SE": "Keypad 1",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8564,7 +8564,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 2",
"sv_SE": "",
"sv_SE": "Keypad 2",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8589,7 +8589,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 3",
"sv_SE": "",
"sv_SE": "Keypad 3",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8614,7 +8614,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 4",
"sv_SE": "",
"sv_SE": "Keypad 4",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8639,7 +8639,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 5",
"sv_SE": "",
"sv_SE": "Keypad 5",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8664,7 +8664,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 6",
"sv_SE": "",
"sv_SE": "Keypad 6",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8689,7 +8689,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 7",
"sv_SE": "",
"sv_SE": "Keypad 7",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8714,7 +8714,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 8",
"sv_SE": "",
"sv_SE": "Keypad 8",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8739,7 +8739,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Блок цифр 9",
"sv_SE": "",
"sv_SE": "Keypad 9",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8889,7 +8889,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Enter (блок цифр)",
"sv_SE": "",
"sv_SE": "Keypad Enter",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -8913,7 +8913,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "0",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -8938,7 +8938,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "1",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -8963,7 +8963,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "2",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -8988,7 +8988,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "3",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9013,7 +9013,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "4",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9038,7 +9038,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "5",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9063,7 +9063,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "6",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9088,7 +9088,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "7",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9113,7 +9113,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "8",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9138,7 +9138,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "9",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9163,7 +9163,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "~",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9188,7 +9188,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "`",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9213,7 +9213,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "-",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9238,7 +9238,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "+",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9263,7 +9263,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "[",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9288,7 +9288,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "]",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9313,7 +9313,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": ";",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9363,7 +9363,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": ",",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9388,7 +9388,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": ".",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9413,7 +9413,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "/",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9738,7 +9738,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "-",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -9763,7 +9763,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "+",
"sv_SE": "",
"th_TH": "",
"tr_TR": "4",
@@ -9789,7 +9789,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Кнопка меню",
"sv_SE": "",
"sv_SE": "Guide",
"th_TH": "",
"tr_TR": "Rehber",
"uk_UA": "",
@@ -12438,7 +12438,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "{0}: {1}",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -14289,7 +14289,7 @@
"pl_PL": "Seria Amiibo",
"pt_BR": "Franquia Amiibo",
"ru_RU": "Серия Amiibo",
"sv_SE": "",
"sv_SE": "Amiibo Series",
"th_TH": "",
"tr_TR": "Amiibo Serisi",
"uk_UA": "Серія Amiibo",
@@ -15755,7 +15755,7 @@
"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.",
"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": "",
"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",
@@ -17426,8 +17426,8 @@
"ID": "TitleUpdateVersionLabel",
"Translations": {
"ar_SA": "الإصدار: {0}",
"de_DE": "",
"el_GR": "",
"de_DE": "Version {0} - {1}",
"el_GR": "Version {0} - {1}",
"en_US": "Version {0} - {1}",
"es_ES": "Versión {0} - {1}",
"fr_FR": "",
@@ -17439,7 +17439,7 @@
"pl_PL": "Wersja {0} - {1}",
"pt_BR": "Versão {0} - {1}",
"ru_RU": "Версия {0} - {1}",
"sv_SE": "",
"sv_SE": "Version {0} - {1}",
"th_TH": "เวอร์ชั่น {0} - {1}",
"tr_TR": "Sürüm {0} - {1}",
"uk_UA": "Версія {0} - {1}",
@@ -17664,7 +17664,7 @@
"pl_PL": "",
"pt_BR": "Ryujinx - Informação",
"ru_RU": "Ryujinx - Информация",
"sv_SE": "",
"sv_SE": "Ryujinx - Info",
"th_TH": "Ryujinx ข้อมูล",
"tr_TR": "Ryujinx - Bilgi",
"uk_UA": "Ryujin x - Інформація",
@@ -18813,8 +18813,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "Amiibo",
"sv_SE": "Amiibo",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -19435,7 +19435,7 @@
"it_IT": "",
"ja_JP": "",
"ko_KR": "XCI 파일 트리머",
"no_NO": "",
"no_NO": "XCI File Trimmer",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Уменьшение размера XCI файлов",
@@ -19639,7 +19639,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "{0:n0} Мб",
"sv_SE": "",
"sv_SE": "{0:n0} Mb",
"th_TH": "",
"tr_TR": "",
"uk_UA": "{0:n0} Мб",
@@ -20914,7 +20914,7 @@
"pl_PL": "Głoś",
"pt_BR": "",
"ru_RU": "Громкость",
"sv_SE": "",
"sv_SE": "Vol",
"th_TH": "ระดับเสียง",
"tr_TR": "Ses",
"uk_UA": "Гуч.",
@@ -21055,7 +21055,7 @@
"el_GR": "Όνομα",
"en_US": "Name",
"es_ES": "Nombre",
"fr_FR": "Nom\u00A0",
"fr_FR": "Nom ",
"he_IL": "שם",
"it_IT": "Nome",
"ja_JP": "名称",
@@ -21388,8 +21388,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "FSR",
"sv_SE": "FSR",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -21888,8 +21888,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "ldn_mitm",
"sv_SE": "ldn_mitm",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -21913,8 +21913,8 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"ru_RU": "RyuLDN",
"sv_SE": "RyuLDN",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
@@ -22214,7 +22214,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Вертикальная синхронизация:",
"sv_SE": "",
"sv_SE": "VSync:",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Вертикальна синхронізація (VSync):",
@@ -22255,7 +22255,7 @@
"el_GR": "",
"en_US": "Switch",
"es_ES": "",
"fr_FR": "",
"fr_FR": "Switch",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
@@ -22264,7 +22264,7 @@
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Консоль",
"sv_SE": "",
"sv_SE": "Switch",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",

View File

@@ -13,13 +13,9 @@
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup>
<Target Name="BuildValidationProj" BeforeTargets="BeforeBuild">
<Message Text="Building Validation Project for $(TargetFramework)" Importance="high" Condition="'$(RuntimeIdentifier)' == ''" />
<Message Text="Building Validation Project for $(TargetFramework) with runtime $(RuntimeIdentifier)" Importance="high" Condition="'$(RuntimeIdentifier)' != ''" />
<Exec WorkingDirectory="..\Ryujinx.BuildValidationTasks\" Command="dotnet restore Ryujinx.BuildValidationTasks.csproj --force --ucr true" Condition="'$(RuntimeIdentifier)' == ''" />
<Exec WorkingDirectory="..\Ryujinx.BuildValidationTasks\" Command="dotnet restore Ryujinx.BuildValidationTasks.csproj --force --runtime $(RuntimeIdentifier)" Condition="'$(RuntimeIdentifier)' != ''" />
<MSBuild Projects="..\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj" Properties="Configuration=Debug" />
<Target Name="BuildValidationProj" BeforeTargets="BeforeRebuild">
<MSBuild Projects="..\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj" Targets="Rebuild">
</MSBuild>
</Target>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
@@ -143,4 +139,10 @@
<ItemGroup>
<AdditionalFiles Include="Assets\locales.json" />
</ItemGroup>
<ItemGroup>
<Compile Update="UI\Views\Settings\SettingsHacksView.axaml.cs">
<DependentUpon>SettingsHacksView.axaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@@ -13,8 +13,9 @@ namespace Ryujinx.Ava.UI.ViewModels
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertiesChanged(params ReadOnlySpan<string> propertyNames)
protected void OnPropertiesChanged(string firstPropertyName, params ReadOnlySpan<string> propertyNames)
{
OnPropertyChanged(firstPropertyName);
foreach (var propertyName in propertyNames)
{
OnPropertyChanged(propertyName);

View File

@@ -62,7 +62,9 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _networkInterfaceIndex;
private int _multiplayerModeIndex;
private string _ldnPassphrase;
private string _LdnServer;
private string _ldnServer;
private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
public int ResolutionScale
{
@@ -162,9 +164,7 @@ namespace Ryujinx.Ava.UI.ViewModels
get => _vSyncMode;
set
{
if (value == VSyncMode.Custom ||
value == VSyncMode.Switch ||
value == VSyncMode.Unbounded)
if (value is VSyncMode.Custom or VSyncMode.Switch or VSyncMode.Unbounded)
{
_vSyncMode = value;
OnPropertyChanged();
@@ -258,6 +258,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool UseHypervisor { get; set; }
public bool DisableP2P { get; set; }
public bool ShowDirtyHacks => ConfigurationState.Instance.Hacks.ShowDirtyHacks;
public string TimeZone { get; set; }
public string ShaderDumpPath { get; set; }
@@ -274,6 +276,17 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
public bool Xc2MenuSoftlockFixEnabled
{
get => _xc2MenuSoftlockFix;
set
{
_xc2MenuSoftlockFix = value;
OnPropertyChanged();
}
}
public int Language { get; set; }
public int Region { get; set; }
public int FsGlobalAccessLogMode { get; set; }
@@ -374,10 +387,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public string LdnServer
{
get => _LdnServer;
get => _ldnServer;
set
{
_LdnServer = value;
_ldnServer = value;
OnPropertyChanged();
}
}
@@ -746,6 +759,9 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Multiplayer.DisableP2p.Value = DisableP2P;
config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
config.Multiplayer.LdnServer.Value = LdnServer;
// Dirty Hacks
config.Hacks.Xc2MenuSoftlockFix.Value = Xc2MenuSoftlockFixEnabled;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@@ -779,5 +795,8 @@ namespace Ryujinx.Ava.UI.ViewModels
RevertIfNotSaved();
CloseWindow?.Invoke();
}
public static string Xc2MenuFixTooltip =>
"From the issue on GitHub:\n\nWhen clicking very fast from game main menu to 2nd submenu, there is a low chance that the game will softlock, the submenu won't show up, while background music is still there.";
}
}

View File

@@ -0,0 +1,48 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHacksView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="HacksPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Center"
Orientation="Vertical"
Spacing="5">
<TextBlock
HorizontalAlignment="Center"
Classes="h1"
Text="Dirty Hacks" />
<TextBlock
Foreground="{DynamicResource SecondaryTextColor}"
TextDecorations="Underline"
Text="Game-specific hacks &amp; tricks to alleviate performance issues or crashing. May cause issues." />
<StackPanel
Margin="0,10,0,0"
Orientation="Horizontal"
HorizontalAlignment="Center"
ToolTip.Tip="{Binding Xc2MenuFixTooltip}">
<CheckBox
Margin="0"
IsChecked="{Binding Xc2MenuSoftlockFixEnabled}"/>
<TextBlock
VerticalAlignment="Center"
Text="Xenoblade Chronicles 2 Menu Softlock Fix" />
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,17 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.UI.Common.Configuration;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsHacksView : UserControl
{
public SettingsViewModel ViewModel;
public SettingsHacksView()
{
InitializeComponent();
}
}
}

View File

@@ -37,6 +37,7 @@
<settings:SettingsAudioView Name="AudioPage" />
<settings:SettingsNetworkView Name="NetworkPage" />
<settings:SettingsLoggingView Name="LoggingPage" />
<settings:SettingsHacksView Name="HacksPage" />
</Grid>
<ui:NavigationView
Grid.Row="1"
@@ -91,6 +92,11 @@
Content="{ext:Locale SettingsTabLogging}"
Tag="LoggingPage"
IconSource="Document" />
<ui:NavigationViewItem
IsVisible="{Binding ShowDirtyHacks}"
Content="Dirty Hacks"
Tag="HacksPage"
IconSource="Code" />
</ui:NavigationView.MenuItems>
<ui:NavigationView.Styles>
<Style Selector="Grid#PlaceholderGrid">

View File

@@ -86,6 +86,10 @@ namespace Ryujinx.Ava.UI.Windows
case nameof(LoggingPage):
NavPanel.Content = LoggingPage;
break;
case nameof(HacksPage):
HacksPage.ViewModel = ViewModel;
NavPanel.Content = HacksPage;
break;
default:
throw new NotImplementedException();
}