Compare commits
10 Commits
Canary-1.2
...
e0b6a01e9d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0b6a01e9d | ||
|
|
e509ffa716 | ||
|
|
412d4065b8 | ||
|
|
e6644626fc | ||
|
|
714c68b548 | ||
|
|
fec197d9ec | ||
|
|
a4b2feef79 | ||
|
|
ad7d9d1ce0 | ||
|
|
86f9544910 | ||
|
|
e9ecbd44fc |
@@ -11,6 +11,7 @@ namespace Ryujinx.Input.SDL2
|
||||
private readonly Dictionary<int, string> _gamepadsInstanceIdsMapping;
|
||||
private readonly List<string> _gamepadsIds;
|
||||
private readonly Lock _lock = new();
|
||||
private readonly SDL2JoyConPair joyConPair;
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds
|
||||
{
|
||||
@@ -36,7 +37,7 @@ namespace Ryujinx.Input.SDL2
|
||||
SDL2Driver.Instance.Initialize();
|
||||
SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||
SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
|
||||
|
||||
joyConPair = new SDL2JoyConPair();
|
||||
// Add already connected gamepads
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
|
||||
@@ -89,6 +90,10 @@ namespace Ryujinx.Input.SDL2
|
||||
lock (_lock)
|
||||
{
|
||||
_gamepadsIds.Remove(id);
|
||||
if (joyConPair.GetGamepad(_gamepadsIds) == null)
|
||||
{
|
||||
_gamepadsIds.Remove(joyConPair.Id);
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadDisconnected?.Invoke(id);
|
||||
@@ -120,6 +125,11 @@ namespace Ryujinx.Input.SDL2
|
||||
_gamepadsIds.Insert(joystickDeviceId, id);
|
||||
else
|
||||
_gamepadsIds.Add(id);
|
||||
if (joyConPair.GetGamepad(_gamepadsIds) != null)
|
||||
{
|
||||
_gamepadsIds.Remove(joyConPair.Id);
|
||||
_gamepadsIds.Add(joyConPair.Id);
|
||||
}
|
||||
}
|
||||
|
||||
OnGamepadConnected?.Invoke(id);
|
||||
@@ -157,6 +167,14 @@ namespace Ryujinx.Input.SDL2
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
if (id == joyConPair.Id)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return joyConPair.GetGamepad(_gamepadsIds);
|
||||
}
|
||||
}
|
||||
|
||||
int joystickIndex = GetJoystickIndexByGamepadId(id);
|
||||
|
||||
if (joystickIndex == -1)
|
||||
@@ -171,6 +189,7 @@ namespace Ryujinx.Input.SDL2
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine("Game controller opened" + SDL_GameControllerName(gamepadHandle));
|
||||
return new SDL2Gamepad(gamepadHandle, id);
|
||||
}
|
||||
}
|
||||
|
||||
281
src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs
Normal file
281
src/Ryujinx.Input.SDL2/SDL2JoyConPair.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using static SDL2.SDL;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace Ryujinx.Input.SDL2
|
||||
{
|
||||
internal class SDL2JoyConPair : IGamepad
|
||||
{
|
||||
private IGamepad _left;
|
||||
private IGamepad _right;
|
||||
private Timer timer;
|
||||
private StandardControllerInputConfig _configuration;
|
||||
|
||||
private readonly StickInputId[] _stickUserMapping =
|
||||
[
|
||||
StickInputId.Unbound,
|
||||
StickInputId.Left,
|
||||
StickInputId.Right
|
||||
];
|
||||
|
||||
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From)
|
||||
{
|
||||
public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound;
|
||||
}
|
||||
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping = new(20);
|
||||
|
||||
private readonly Lock _userMappingLock = new();
|
||||
|
||||
public GamepadFeaturesFlag Features => (_left?.Features ?? GamepadFeaturesFlag.None) |
|
||||
(_right?.Features ?? GamepadFeaturesFlag.None);
|
||||
|
||||
public string Id => "JoyConPair";
|
||||
|
||||
public string Name => "Nintendo Switch Joy-Con (L/R)";
|
||||
private const string LeftName = "Nintendo Switch Joy-Con (L)";
|
||||
private const string RightName = "Nintendo Switch Joy-Con (R)";
|
||||
public bool IsConnected => _left is { IsConnected: true } && _right is { IsConnected: true };
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_left?.Dispose();
|
||||
_right?.Dispose();
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetMappedStateSnapshot()
|
||||
{
|
||||
GamepadStateSnapshot rawState = GetStateSnapshot();
|
||||
GamepadStateSnapshot result = default;
|
||||
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
if (_buttonsUserMapping.Count == 0)
|
||||
return rawState;
|
||||
|
||||
|
||||
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
|
||||
foreach (ButtonMappingEntry entry in _buttonsUserMapping)
|
||||
{
|
||||
if (!entry.IsValid)
|
||||
continue;
|
||||
|
||||
// Do not touch state of button already pressed
|
||||
if (!result.IsPressed(entry.To))
|
||||
{
|
||||
result.SetPressed(entry.To, rawState.IsPressed(entry.From));
|
||||
}
|
||||
}
|
||||
|
||||
(float leftStickX, float leftStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Left]);
|
||||
(float rightStickX, float rightStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Right]);
|
||||
|
||||
result.SetStick(StickInputId.Left, leftStickX, leftStickY);
|
||||
result.SetStick(StickInputId.Right, rightStickX, rightStickY);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Vector3 GetMotionData(MotionInputId inputId)
|
||||
{
|
||||
return inputId switch
|
||||
{
|
||||
MotionInputId.SecondAccelerometer => _right.GetMotionData(MotionInputId.Accelerometer),
|
||||
MotionInputId.SecondGyroscope => _right.GetMotionData(MotionInputId.Gyroscope),
|
||||
_ => _left.GetMotionData(inputId)
|
||||
};
|
||||
}
|
||||
|
||||
public GamepadStateSnapshot GetStateSnapshot()
|
||||
{
|
||||
return IGamepad.GetStateSnapshot(this);
|
||||
}
|
||||
|
||||
public (float, float) GetStick(StickInputId inputId)
|
||||
{
|
||||
switch (inputId)
|
||||
{
|
||||
case StickInputId.Left:
|
||||
{
|
||||
(float x, float y) = _left.GetStick(StickInputId.Left);
|
||||
return (y, -x);
|
||||
}
|
||||
case StickInputId.Right:
|
||||
{
|
||||
(float x, float y) = _right.GetStick(StickInputId.Left);
|
||||
return (-y, x);
|
||||
}
|
||||
case StickInputId.Unbound:
|
||||
case StickInputId.Count:
|
||||
default:
|
||||
return (0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPressed(GamepadButtonInputId inputId)
|
||||
{
|
||||
return inputId switch
|
||||
{
|
||||
GamepadButtonInputId.LeftStick => _left.IsPressed(GamepadButtonInputId.LeftStick),
|
||||
GamepadButtonInputId.DpadUp => _left.IsPressed(GamepadButtonInputId.Y),
|
||||
GamepadButtonInputId.DpadDown => _left.IsPressed(GamepadButtonInputId.A),
|
||||
GamepadButtonInputId.DpadLeft => _left.IsPressed(GamepadButtonInputId.B),
|
||||
GamepadButtonInputId.DpadRight => _left.IsPressed(GamepadButtonInputId.X),
|
||||
GamepadButtonInputId.Minus => _left.IsPressed(GamepadButtonInputId.Start),
|
||||
GamepadButtonInputId.LeftShoulder => _left.IsPressed(GamepadButtonInputId.Paddle2),
|
||||
GamepadButtonInputId.LeftTrigger => _left.IsPressed(GamepadButtonInputId.Paddle4),
|
||||
GamepadButtonInputId.SingleRightTrigger0 => _left.IsPressed(GamepadButtonInputId.LeftShoulder),
|
||||
GamepadButtonInputId.SingleLeftTrigger0 => _left.IsPressed(GamepadButtonInputId.RightShoulder),
|
||||
|
||||
GamepadButtonInputId.RightStick => _right.IsPressed(GamepadButtonInputId.LeftStick),
|
||||
GamepadButtonInputId.A => _right.IsPressed(GamepadButtonInputId.B),
|
||||
GamepadButtonInputId.B => _right.IsPressed(GamepadButtonInputId.Y),
|
||||
GamepadButtonInputId.X => _right.IsPressed(GamepadButtonInputId.A),
|
||||
GamepadButtonInputId.Y => _right.IsPressed(GamepadButtonInputId.X),
|
||||
GamepadButtonInputId.Plus => _right.IsPressed(GamepadButtonInputId.Start),
|
||||
GamepadButtonInputId.RightShoulder => _right.IsPressed(GamepadButtonInputId.Paddle1),
|
||||
GamepadButtonInputId.RightTrigger => _right.IsPressed(GamepadButtonInputId.Paddle3),
|
||||
GamepadButtonInputId.SingleRightTrigger1 => _right.IsPressed(GamepadButtonInputId.LeftShoulder),
|
||||
GamepadButtonInputId.SingleLeftTrigger1 => _right.IsPressed(GamepadButtonInputId.RightShoulder),
|
||||
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||
{
|
||||
if (lowFrequency != 0)
|
||||
{
|
||||
_right.Rumble(lowFrequency, lowFrequency, durationMs);
|
||||
}
|
||||
|
||||
if (highFrequency != 0)
|
||||
{
|
||||
_left.Rumble(highFrequency, highFrequency, durationMs);
|
||||
}
|
||||
|
||||
if (lowFrequency == 0 && highFrequency == 0)
|
||||
{
|
||||
_left.Rumble(0, 0, durationMs);
|
||||
_right.Rumble(0, 0, durationMs);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetConfiguration(InputConfig configuration)
|
||||
{
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
_configuration = (StandardControllerInputConfig)configuration;
|
||||
_left.SetConfiguration(configuration);
|
||||
_right.SetConfiguration(configuration);
|
||||
|
||||
_buttonsUserMapping.Clear();
|
||||
|
||||
// First update sticks
|
||||
_stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick;
|
||||
_stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick;
|
||||
|
||||
// Then left joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick,
|
||||
(GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0,
|
||||
(GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
|
||||
|
||||
// Finally right joycon
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick,
|
||||
(GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
|
||||
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1,
|
||||
(GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
|
||||
|
||||
SetTriggerThreshold(_configuration.TriggerThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTriggerThreshold(float triggerThreshold)
|
||||
{
|
||||
_left.SetTriggerThreshold(triggerThreshold);
|
||||
_right.SetTriggerThreshold(triggerThreshold);
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(List<string> gamepadsIds)
|
||||
{
|
||||
this.Dispose();
|
||||
var gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id)
|
||||
.Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList();
|
||||
int leftIndex = gamepadNames.IndexOf(LeftName);
|
||||
int rightIndex = gamepadNames.IndexOf(RightName);
|
||||
|
||||
if (leftIndex == -1 || rightIndex == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex);
|
||||
nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex);
|
||||
|
||||
if (leftGamepadHandle == nint.Zero || rightGamepadHandle == nint.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_left = new SDL2Gamepad(leftGamepadHandle, gamepadsIds[leftIndex]);
|
||||
_right = new SDL2Gamepad(rightGamepadHandle, gamepadsIds[rightIndex]);
|
||||
ShowPowerLevel(leftGamepadHandle, rightGamepadHandle);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void ShowPowerLevel(nint leftGamepadHandle, nint rightGamepadHandle)
|
||||
{
|
||||
timer?.Stop();
|
||||
timer = new Timer(2000);
|
||||
timer.Elapsed += (_, _) =>
|
||||
{
|
||||
timer.Stop();
|
||||
var leftLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(leftGamepadHandle));
|
||||
var rightLevel = SDL_JoystickCurrentPowerLevel(SDL_GameControllerGetJoystick(rightGamepadHandle));
|
||||
Logger.Info?.Print(LogClass.Hid, $"Left power level: {leftLevel}, Right power level: {rightLevel}");
|
||||
};
|
||||
timer.AutoReset = false;
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,6 +266,7 @@ namespace Ryujinx.Input.HLE
|
||||
if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook)
|
||||
{
|
||||
_leftMotionInput = new MotionInput();
|
||||
_rightMotionInput = new MotionInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -298,7 +299,20 @@ namespace Ryujinx.Input.HLE
|
||||
|
||||
if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
|
||||
{
|
||||
_rightMotionInput = _leftMotionInput;
|
||||
if (gamepad.Id== "JoyConPair")
|
||||
{
|
||||
Vector3 rightAccelerometer = gamepad.GetMotionData(MotionInputId.SecondAccelerometer);
|
||||
Vector3 rightGyroscope = gamepad.GetMotionData(MotionInputId.SecondGyroscope);
|
||||
|
||||
rightAccelerometer = new Vector3(rightAccelerometer.X, -rightAccelerometer.Z, rightAccelerometer.Y);
|
||||
rightGyroscope = new Vector3(rightGyroscope.X, -rightGyroscope.Z, rightGyroscope.Y);
|
||||
|
||||
_rightMotionInput.Update(rightAccelerometer, rightGyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rightMotionInput = _leftMotionInput;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,6 +347,7 @@ namespace Ryujinx.Input.HLE
|
||||
// Reset states
|
||||
State = default;
|
||||
_leftMotionInput = null;
|
||||
_rightMotionInput = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,5 +21,17 @@ namespace Ryujinx.Input
|
||||
/// </summary>
|
||||
/// <remarks>Values are in degrees</remarks>
|
||||
Gyroscope,
|
||||
|
||||
/// <summary>
|
||||
/// Second accelerometer.
|
||||
/// </summary>
|
||||
/// <remarks>Values are in m/s^2</remarks>
|
||||
SecondAccelerometer,
|
||||
|
||||
/// <summary>
|
||||
/// Second gyroscope.
|
||||
/// </summary>
|
||||
/// <remarks>Values are in degrees</remarks>
|
||||
SecondGyroscope
|
||||
}
|
||||
}
|
||||
|
||||
64
src/Ryujinx.UI.Common/Helper/AppletMetadata.cs
Normal file
64
src/Ryujinx.UI.Common/Helper/AppletMetadata.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Ns;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.UI.App.Common;
|
||||
|
||||
namespace Ryujinx.UI.Common.Helper
|
||||
{
|
||||
public readonly struct AppletMetadata
|
||||
{
|
||||
private readonly ContentManager _contentManager;
|
||||
|
||||
public string Name { get; }
|
||||
public ulong ProgramId { get; }
|
||||
|
||||
public string Version { get; }
|
||||
|
||||
public AppletMetadata(ContentManager contentManager, string name, ulong programId, string version = "1.0.0")
|
||||
: this(name, programId, version)
|
||||
{
|
||||
_contentManager = contentManager;
|
||||
}
|
||||
|
||||
public AppletMetadata(string name, ulong programId, string version = "1.0.0")
|
||||
{
|
||||
Name = name;
|
||||
ProgramId = programId;
|
||||
Version = version;
|
||||
}
|
||||
|
||||
public string GetContentPath(ContentManager contentManager)
|
||||
=> (contentManager ?? _contentManager)
|
||||
.GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
public bool CanStart(ContentManager contentManager, out ApplicationData appData, out BlitStruct<ApplicationControlProperty> appControl)
|
||||
{
|
||||
contentManager ??= _contentManager;
|
||||
if (contentManager == null)
|
||||
{
|
||||
appData = null;
|
||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
appData = new()
|
||||
{
|
||||
Name = Name,
|
||||
Id = ProgramId,
|
||||
Path = GetContentPath(contentManager)
|
||||
};
|
||||
|
||||
if (string.IsNullOrEmpty(appData.Path))
|
||||
{
|
||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
appControl = StructHelpers.CreateCustomNacpData(Name, Version);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
@@ -17,7 +16,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
private const string DefaultLanguageCode = "en_US";
|
||||
|
||||
private readonly Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
|
||||
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
private string _localeLanguageCode;
|
||||
|
||||
@@ -27,7 +25,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
public LocaleManager()
|
||||
{
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
|
||||
Load();
|
||||
@@ -37,9 +34,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
var localeLanguageCode = !string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value) ?
|
||||
ConfigurationState.Instance.UI.LanguageCode.Value : CultureInfo.CurrentCulture.Name.Replace('-', '_');
|
||||
|
||||
// Load en_US as default, if the target language translation is missing or incomplete.
|
||||
LoadDefaultLanguage();
|
||||
|
||||
LoadLanguage(localeLanguageCode);
|
||||
|
||||
// Save whatever we ended up with.
|
||||
@@ -66,26 +61,14 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If formatting failed use the default text instead.
|
||||
if (_localeDefaultStrings.TryGetValue(key, out value))
|
||||
try
|
||||
{
|
||||
return string.Format(value, dynamicValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If formatting the default text failed return the key.
|
||||
return key.ToString();
|
||||
}
|
||||
// If formatting the text failed,
|
||||
// continue to the below line & return the text without formatting.
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// If the locale doesn't contain the key return the default one.
|
||||
return _localeDefaultStrings.TryGetValue(key, out string defaultValue)
|
||||
? defaultValue
|
||||
: key.ToString(); // If the locale text doesn't exist return the key.
|
||||
|
||||
return key.ToString(); // If the locale text doesn't exist return the key.
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -114,11 +97,6 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
return this[key];
|
||||
}
|
||||
|
||||
private void LoadDefaultLanguage()
|
||||
{
|
||||
_localeDefaultStrings = LoadJsonLanguage(DefaultLanguageCode);
|
||||
}
|
||||
|
||||
public void LoadLanguage(string languageCode)
|
||||
{
|
||||
var locale = LoadJsonLanguage(languageCode);
|
||||
@@ -126,7 +104,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
if (locale == null)
|
||||
{
|
||||
_localeLanguageCode = DefaultLanguageCode;
|
||||
locale = _localeDefaultStrings;
|
||||
locale = LoadJsonLanguage(_localeLanguageCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -167,15 +145,16 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
if (!Enum.TryParse<LocaleKeys>(locale.ID, out var localeKey))
|
||||
continue;
|
||||
|
||||
localeStrings[localeKey] =
|
||||
locale.Translations.TryGetValue(languageCode, out string val) && !string.IsNullOrEmpty(val)
|
||||
? val
|
||||
: locale.Translations[DefaultLanguageCode];
|
||||
|
||||
if (string.IsNullOrEmpty(localeStrings[localeKey]))
|
||||
var str = locale.Translations.TryGetValue(languageCode, out string val) && !string.IsNullOrEmpty(val)
|
||||
? val
|
||||
: locale.Translations[DefaultLanguageCode];
|
||||
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
throw new Exception($"Locale key '{locale.ID}' has no valid translations for desired language {languageCode}! {DefaultLanguageCode} is an empty string or null");
|
||||
}
|
||||
|
||||
localeStrings[localeKey] = str;
|
||||
}
|
||||
|
||||
return localeStrings;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Data.Converters;
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System;
|
||||
@@ -32,11 +33,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
if (app.CurrentSavingsB < app.PotentialSavingsB)
|
||||
{
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCICanSaveLabel, (app.PotentialSavingsB - app.CurrentSavingsB) / _bytesPerMB);
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCICanSaveLabel, ((app.PotentialSavingsB - app.CurrentSavingsB) / _bytesPerMB).CoerceAtLeast(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCISavingLabel, app.CurrentSavingsB / _bytesPerMB);
|
||||
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCISavingLabel, (app.CurrentSavingsB / _bytesPerMB).CoerceAtLeast(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
value = _processingApplication.Value with { PercentageProgress = null };
|
||||
|
||||
if (value.HasValue)
|
||||
_displayedXCIFiles.ReplaceWith(value.Value);
|
||||
_displayedXCIFiles.ReplaceWith(value);
|
||||
|
||||
_processingApplication = value;
|
||||
OnPropertyChanged();
|
||||
|
||||
@@ -3,8 +3,6 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Threading;
|
||||
using Gommon;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
@@ -12,8 +10,6 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
@@ -124,26 +120,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
ViewModel.LoadConfigurableHotKeys();
|
||||
}
|
||||
|
||||
public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009);
|
||||
|
||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
||||
{
|
||||
const string AppletName = "miiEdit";
|
||||
const ulong AppletProgramId = 0x0100000000001009;
|
||||
const string AppletVersion = "1.0.0";
|
||||
|
||||
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(AppletProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (!string.IsNullOrEmpty(contentPath))
|
||||
if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
|
||||
{
|
||||
ApplicationData applicationData = new()
|
||||
{
|
||||
Name = AppletName,
|
||||
Id = AppletProgramId,
|
||||
Path = contentPath
|
||||
};
|
||||
|
||||
var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion);
|
||||
|
||||
await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
await ViewModel.LoadApplication(appData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user