diff --git a/Directory.Packages.props b/Directory.Packages.props index ffb5f2ead..7059af0e0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,7 +38,7 @@ - + diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index 53929f95e..2602f9905 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -40,11 +40,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleSignature ???? CFBundleVersion - 1.1.0 + 1.2.0 NSHighResolutionCapable CSResourcesFileMapped diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 12e4c3aa1..0465d7c91 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -17,7 +17,7 @@ error_handler() { set the button_pressed to the button returned of the result if the button_pressed is \"Open Download Page\" then - open location \"https://ryujinx.org/download\" + open location \"https://ryujinx.app/download\" end if """ @@ -54,4 +54,4 @@ if [ "$#" -le 3 ]; then open -a "$INSTALL_DIRECTORY" else open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}" -fi \ No newline at end of file +fi diff --git a/src/ARMeilleure/Instructions/InstEmitSystem.cs b/src/ARMeilleure/Instructions/InstEmitSystem.cs index fbf3b4a70..11c1d0328 100644 --- a/src/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/src/ARMeilleure/Instructions/InstEmitSystem.cs @@ -88,7 +88,7 @@ namespace ARMeilleure.Instructions EmitSetTpidrEl0(context); return; case 0b11_011_1101_0000_101: - EmitGetTpidr2El0(context); + EmitSetTpidr2El0(context); return; default: @@ -291,5 +291,16 @@ namespace ARMeilleure.Instructions context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value); } + + private static void EmitSetTpidr2El0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand value = GetIntOrZR(context, op.Rt); + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset())), value); + } } } diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs index 608681551..076530744 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs @@ -1,6 +1,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller { - public class JoyconConfigControllerStick where TButton : unmanaged where TStick : unmanaged + public class JoyconConfigControllerStick + where TButton : unmanaged + where TStick : unmanaged { public TStick Joystick { get; set; } public bool InvertStickX { get; set; } diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs index da4d2e51b..5895c67bb 100644 --- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs +++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Applets.Browser; +using Ryujinx.HLE.HOS.Applets.Cabinet; using Ryujinx.HLE.HOS.Applets.Dummy; using Ryujinx.HLE.HOS.Applets.Error; using Ryujinx.HLE.HOS.Services.Am.AppletAE; @@ -23,14 +24,14 @@ namespace Ryujinx.HLE.HOS.Applets case AppletId.SoftwareKeyboard: return new SoftwareKeyboardApplet(system); case AppletId.LibAppletWeb: - return new BrowserApplet(system); case AppletId.LibAppletShop: - return new BrowserApplet(system); case AppletId.LibAppletOff: - return new BrowserApplet(system); + return new BrowserApplet(); case AppletId.MiiEdit: Logger.Warning?.Print(LogClass.Application, $"Please use the MiiEdit inside File/Open Applet"); return new DummyApplet(system); + case AppletId.Cabinet: + return new CabinetApplet(system); } Logger.Warning?.Print(LogClass.Application, $"Applet {applet} not implemented!"); diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs index 6afbe4a72..c5f13dab3 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs @@ -18,13 +18,6 @@ namespace Ryujinx.HLE.HOS.Applets.Browser private List _arguments; private ShimKind _shimKind; - public BrowserApplet(Horizon system) { } - - public ResultCode GetResult() - { - return ResultCode.Success; - } - public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { _normalSession = normalSession; diff --git a/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs new file mode 100644 index 000000000..294b8d1f6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs @@ -0,0 +1,182 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using Ryujinx.HLE.HOS.Services.Hid.HidServer; +using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Applets.Cabinet +{ + internal unsafe class CabinetApplet : IApplet + { + private readonly Horizon _system; + private AppletSession _normalSession; + + public event EventHandler AppletStateChanged; + + public CabinetApplet(Horizon system) + { + _system = system; + } + + public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) + { + _normalSession = normalSession; + + byte[] launchParams = _normalSession.Pop(); + byte[] startParamBytes = _normalSession.Pop(); + + StartParamForAmiiboSettings startParam = IApplet.ReadStruct(startParamBytes); + + Logger.Stub?.PrintStub(LogClass.ServiceAm, $"CabinetApplet Start Type: {startParam.Type}"); + + switch (startParam.Type) + { + case 0: + StartNicknameAndOwnerSettings(ref startParam); + break; + case 1: + case 3: + StartFormatter(ref startParam); + break; + default: + Logger.Error?.Print(LogClass.ServiceAm, $"Unknown AmiiboSettings type: {startParam.Type}"); + break; + } + + // Prepare the response + ReturnValueForAmiiboSettings returnValue = new() + { + AmiiboSettingsReturnFlag = (byte)AmiiboSettingsReturnFlag.HasRegisterInfo, + DeviceHandle = new DeviceHandle + { + Handle = 0 // Dummy device handle + }, + RegisterInfo = startParam.RegisterInfo + }; + + // Push the response + _normalSession.Push(BuildResponse(returnValue)); + AppletStateChanged?.Invoke(this, null); + + _system.ReturnFocus(); + + return ResultCode.Success; + } + + public ResultCode GetResult() + { + _system.Device.System.NfpDevices.RemoveAt(0); + return ResultCode.Success; + } + + private void StartFormatter(ref StartParamForAmiiboSettings startParam) + { + // Initialize RegisterInfo + startParam.RegisterInfo = new RegisterInfo(); + } + + private void StartNicknameAndOwnerSettings(ref StartParamForAmiiboSettings startParam) + { + _system.Device.UIHandler.DisplayCabinetDialog(out string newName); + byte[] nameBytes = Encoding.UTF8.GetBytes(newName); + Array41 nickName = new Array41(); + nameBytes.CopyTo(nickName.AsSpan()); + startParam.RegisterInfo.Nickname = nickName; + NfpDevice devicePlayer1 = new() + { + NpadIdType = NpadIdType.Player1, + Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), + State = NfpDeviceState.SearchingForTag, + }; + _system.Device.System.NfpDevices.Add(devicePlayer1); + _system.Device.UIHandler.DisplayCabinetMessageDialog(); + string amiiboId = string.Empty; + bool scanned = false; + while (!scanned) + { + for (int i = 0; i < _system.Device.System.NfpDevices.Count; i++) + { + if (_system.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) + { + amiiboId = _system.Device.System.NfpDevices[i].AmiiboId; + scanned = true; + } + } + } + VirtualAmiibo.UpdateNickName(amiiboId, newName); + } + + private static byte[] BuildResponse(ReturnValueForAmiiboSettings returnValue) + { + int size = Unsafe.SizeOf(); + byte[] bytes = new byte[size]; + + fixed (byte* bytesPtr = bytes) + { + Unsafe.Write(bytesPtr, returnValue); + } + + return bytes; + } + + #region Structs + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct TagInfo + { + public fixed byte Data[0x58]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct StartParamForAmiiboSettings + { + public byte ZeroValue; // Left at zero by sdknso + public byte Type; + public byte Flags; + public byte AmiiboSettingsStartParamOffset28; + public ulong AmiiboSettingsStartParam0; + + public TagInfo TagInfo; // Only enabled when flags bit 1 is set + public RegisterInfo RegisterInfo; // Only enabled when flags bit 2 is set + + public fixed byte StartParamExtraData[0x20]; + + public fixed byte Reserved[0x24]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct ReturnValueForAmiiboSettings + { + public byte AmiiboSettingsReturnFlag; + private byte Padding1; + private byte Padding2; + private byte Padding3; + public DeviceHandle DeviceHandle; + public TagInfo TagInfo; + public RegisterInfo RegisterInfo; + public fixed byte IgnoredBySdknso[0x24]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct DeviceHandle + { + public ulong Handle; + } + + public enum AmiiboSettingsReturnFlag : byte + { + Cancel = 0, + HasTagInfo = 2, + HasRegisterInfo = 4, + HasTagInfoAndRegisterInfo = 6 + } + + #endregion + } +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index 5ec9d4b08..3a7b29ab5 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -117,11 +117,6 @@ namespace Ryujinx.HLE.HOS.Applets return ResultCode.Success; } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private static byte[] BuildResponse(ControllerSupportResultInfo result) { using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); diff --git a/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs index 75df7a373..6b16aee7b 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs @@ -11,11 +11,14 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy { private readonly Horizon _system; private AppletSession _normalSession; + public event EventHandler AppletStateChanged; + public DummyApplet(Horizon system) { _system = system; } + public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { _normalSession = normalSession; @@ -24,10 +27,7 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy _system.ReturnFocus(); return ResultCode.Success; } - private static T ReadStruct(byte[] data) where T : struct - { - return MemoryMarshal.Read(data.AsSpan()); - } + private static byte[] BuildResponse() { using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); @@ -35,9 +35,5 @@ namespace Ryujinx.HLE.HOS.Applets.Dummy writer.Write((ulong)ResultCode.Success); return stream.ToArray(); } - public ResultCode GetResult() - { - return ResultCode.Success; - } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs index 87d88fc65..0e043cc45 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs @@ -203,10 +203,5 @@ namespace Ryujinx.HLE.HOS.Applets.Error _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber} (Details)", "\n" + detailsText, buttons.ToArray()); } } - - public ResultCode GetResult() - { - return ResultCode.Success; - } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/IApplet.cs b/src/Ryujinx.HLE/HOS/Applets/IApplet.cs index bc5353841..4500b2f63 100644 --- a/src/Ryujinx.HLE/HOS/Applets/IApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/IApplet.cs @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Applets ResultCode Start(AppletSession normalSession, AppletSession interactiveSession); - ResultCode GetResult(); + ResultCode GetResult() => ResultCode.Success; bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position) => false; diff --git a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs index ccc761ba1..05bddc76f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -37,11 +37,6 @@ namespace Ryujinx.HLE.HOS.Applets return ResultCode.Success; } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private byte[] BuildResponse() { UserProfile currentUser = _system.AccountManager.LastOpenedUser; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index e04fc64fe..9ec202357 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -144,11 +144,6 @@ namespace Ryujinx.HLE.HOS.Applets } } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private bool IsKeyboardActive() { return _backgroundState >= InlineKeyboardState.Appearing && _backgroundState < InlineKeyboardState.Disappearing; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs index 32d9e68da..51571401f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { /// /// Wraps a type in a class so it gets stored in the GC managed heap. This is used as communication mechanism - /// between classed that need to be disposed and, thus, can't share their references. + /// between classes that need to be disposed and, thus, can't share their references. /// /// The internal type. class TRef diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs index d48a88978..8f9f0e3e4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs @@ -24,6 +24,15 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p return ResultCode.Success; } + [CommandCmif(776)] + // DestroyGroup() + public ResultCode DestroyGroup(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + [CommandCmif(1536)] // SendToOtherGroup(nn::lp2p::MacAddress, nn::lp2p::GroupId, s16, s16, u32, buffer) public ResultCode SendToOtherGroup(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs index 598fb654f..fbce5c10c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs @@ -1,3 +1,5 @@ +using Gommon; +using Humanizer; using NetCoreServer; using Open.Nat; using Ryujinx.Common.Logging; @@ -153,7 +155,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy if (_publicPort != 0) { - _ = Task.Delay(PortLeaseRenew * 1000, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease)); + _ = Executor.ExecuteAfterDelayAsync( + PortLeaseRenew.Seconds(), + _disposedCancellation.Token, + RefreshLease); } _natDevice = device; @@ -257,7 +262,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy } - _ = Task.Delay(PortLeaseRenew, _disposedCancellation.Token).ContinueWith((task) => Task.Run(RefreshLease)); + _ = Executor.ExecuteAfterDelayAsync( + PortLeaseRenew.Milliseconds(), + _disposedCancellation.Token, + RefreshLease); } public bool TryRegisterUser(P2pProxySession session, ExternalProxyConfig config) diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index 7ce749d1a..0c685471c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -93,6 +93,13 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return registerInfo; } + public static void UpdateNickName(string amiiboId, string newNickName) + { + VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); + virtualAmiiboFile.NickName = newNickName; + SaveAmiiboFile(virtualAmiiboFile); + } + public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId) { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 2928ac7fe..361a9159e 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca) + public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca, BlitStruct? customNacpData = null) { // Extract RomFs and ExeFs from NCA. IStorage romFs = nca.GetRomFs(device, patchNca); @@ -55,6 +55,10 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { nacpData = controlNca.GetNacp(device); } + else if (customNacpData != null) // if the Application doesn't provide a nacp file but the Application provides an override, use the provided nacp override + { + nacpData = (BlitStruct)customNacpData; + } /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update. diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index a0e7e0fa1..fe8360f04 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -98,12 +98,12 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - public bool LoadNca(string path) + public bool LoadNca(string path, BlitStruct? customNacpData = null) { FileStream file = new(path, FileMode.Open, FileAccess.Read); Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - ProcessResult processResult = nca.Load(_device, null, null); + ProcessResult processResult = nca.Load(_device, null, null, customNacpData); if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) { diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index e187b2360..3a7042670 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -84,12 +84,19 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } + bool isFirmware = ProgramId is >= 0x0100000000000819 and <= 0x010000000000081C; + bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF; + + string name = !isFirmware + ? (isFirmwareApplication ? "Firmware Application " : "") + (!string.IsNullOrWhiteSpace(Name) ? Name : "") + : "Firmware"; + // TODO: LibHac npdm currently doesn't support version field. - string version = ProgramId > 0x0100000000007FFF - ? DisplayVersion + string version = !isFirmware + ? (!string.IsNullOrWhiteSpace(DisplayVersion) ? DisplayVersion : "") : device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?"; - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); + Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); return true; } diff --git a/src/Ryujinx.HLE/StructHelpers.cs b/src/Ryujinx.HLE/StructHelpers.cs new file mode 100644 index 000000000..6e6af8cea --- /dev/null +++ b/src/Ryujinx.HLE/StructHelpers.cs @@ -0,0 +1,37 @@ +using LibHac.Common; +using LibHac.Ns; +using System; +using System.Text; + +namespace Ryujinx.HLE +{ + public static class StructHelpers + { + public static BlitStruct CreateCustomNacpData(string name, string version) + { + // https://switchbrew.org/wiki/NACP + const int OffsetOfDisplayVersion = 0x3060; + + // https://switchbrew.org/wiki/NACP#ApplicationTitle + const int TotalApplicationTitles = 0x10; + const int SizeOfApplicationTitle = 0x300; + const int OffsetOfApplicationPublisherStrings = 0x200; + + + var nacpData = new BlitStruct(1); + + // name and publisher buffer + // repeat once for each locale (the ApplicationControlProperty has 16 locales) + for (int i = 0; i < TotalApplicationTitles; i++) + { + Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle)..]); + "Ryujinx"u8.CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle + OffsetOfApplicationPublisherStrings)..]); + } + + // version buffer + Encoding.ASCII.GetBytes(version).AsSpan().CopyTo(nacpData.ByteSpan[OffsetOfDisplayVersion..]); + + return nacpData; + } + } +} diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 466352152..d0afdf173 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -1,3 +1,5 @@ +using LibHac.Common; +using LibHac.Ns; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; using Ryujinx.Common.Configuration; @@ -111,7 +113,7 @@ namespace Ryujinx.HLE public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile); public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId); - public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile); + public bool LoadNca(string ncaFile, BlitStruct? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData); public bool LoadNsp(string nspFile, ulong applicationId = 0) => Processes.LoadNsp(nspFile, applicationId); public bool LoadProgram(string fileName) => Processes.LoadNxo(fileName); diff --git a/src/Ryujinx.HLE/UI/IHostUIHandler.cs b/src/Ryujinx.HLE/UI/IHostUIHandler.cs index 8debfcca0..88af83735 100644 --- a/src/Ryujinx.HLE/UI/IHostUIHandler.cs +++ b/src/Ryujinx.HLE/UI/IHostUIHandler.cs @@ -24,6 +24,18 @@ namespace Ryujinx.HLE.UI /// True when OK is pressed, False otherwise. bool DisplayMessageDialog(ControllerAppletUIArgs args); + /// + /// Displays an Input Dialog box to the user so they can enter the Amiibo's new name + /// + /// Text that the user entered. Set to `null` on internal errors + /// True when OK is pressed, False otherwise. Also returns True on internal errors + bool DisplayCabinetDialog(out string userText); + + /// + /// Displays a Message Dialog box to the user to notify them to scan the Amiibo. + /// + void DisplayCabinetMessageDialog(); + /// /// Tell the UI that we need to transition to another program. /// diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index ff87a3845..12158176a 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -1,4 +1,5 @@ using CommandLine; +using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Common; @@ -96,8 +97,13 @@ namespace Ryujinx.Headless.SDL2 } Parser.Default.ParseArguments(args) - .WithParsed(Load) - .WithNotParsed(errors => errors.Output()); + .WithParsed(Load) + .WithNotParsed(errors => + { + Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:"); + + errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}")); + }); } private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) @@ -579,8 +585,8 @@ namespace Ryujinx.Headless.SDL2 options.MultiplayerLanInterfaceId, Common.Configuration.Multiplayer.MultiplayerMode.Disabled, false, - "", - "", + string.Empty, + string.Empty, options.CustomVSyncInterval); return new Switch(configuration); diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 2479ec127..fbe7cb49c 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -1,4 +1,5 @@ using Humanizer; +using LibHac.Tools.Fs; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; @@ -485,6 +486,19 @@ namespace Ryujinx.Headless.SDL2 return true; } + public bool DisplayCabinetDialog(out string userText) + { + // SDL2 doesn't support input dialogs + userText = "Ryujinx"; + + return true; + } + + public void DisplayCabinetMessageDialog() + { + SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION, "Cabinet Dialog", "Please scan your Amiibo now.", WindowHandle); + } + public bool DisplayMessageDialog(ControllerAppletUIArgs args) { if (_ignoreControllerApplet) return false; diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs index 174db51ad..cb6467f5e 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs @@ -1,5 +1,4 @@ using DynamicData; -using DynamicData.Kernel; using Gommon; using LibHac; using LibHac.Common; @@ -37,14 +36,13 @@ using System.Threading.Tasks; using ContentType = LibHac.Ncm.ContentType; using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using Path = System.IO.Path; -using SpanHelpers = LibHac.Common.SpanHelpers; using TimeSpan = System.TimeSpan; namespace Ryujinx.UI.App.Common { public class ApplicationLibrary { - public static string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; + public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; public Language DesiredLanguage { get; set; } public event EventHandler ApplicationCountUpdated; public event EventHandler LdnGameDataReceived; @@ -191,12 +189,9 @@ namespace Ryujinx.UI.App.Common } } - if (isExeFs) - { - return GetApplicationFromExeFs(pfs, filePath); - } - - return null; + return isExeFs + ? GetApplicationFromExeFs(pfs, filePath) + : null; } /// The configured key set is missing a key. @@ -512,10 +507,6 @@ namespace Ryujinx.UI.App.Common case ".xci": case ".nsp": { - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem); foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) @@ -604,7 +595,7 @@ namespace Ryujinx.UI.App.Common controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None) .OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read) .ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), + nacpFile.Get.Read(out _, 0, LibHac.Common.SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); var displayVersion = controlData.DisplayVersionString.ToString(); @@ -827,7 +818,7 @@ namespace Ryujinx.UI.App.Common { _downloadableContents.Edit(it => { - DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase, dlcs); + DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, dlcs); it.Remove(it.Items.Where(item => item.Dlc.TitleIdBase == application.IdBase)); it.AddOrUpdate(dlcs); @@ -839,7 +830,7 @@ namespace Ryujinx.UI.App.Common { _titleUpdates.Edit(it => { - TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, updates); + TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, updates); it.Remove(it.Items.Where(item => item.TitleUpdate.TitleIdBase == application.IdBase)); it.AddOrUpdate(updates); @@ -1088,14 +1079,15 @@ namespace Ryujinx.UI.App.Common private bool AddAndAutoSelectUpdate(TitleUpdateModel update) { - var currentlySelected = TitleUpdates.Items.FirstOrOptional(it => + if (update == null) return false; + + var currentlySelected = TitleUpdates.Items.FindFirst(it => it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected); - var shouldSelect = !currentlySelected.HasValue || - currentlySelected.Value.TitleUpdate.Version < update.Version; + var shouldSelect = currentlySelected.Check(curr => curr.TitleUpdate?.Version < update.Version); _titleUpdates.AddOrUpdate((update, shouldSelect)); - + if (currentlySelected.HasValue && shouldSelect) { _titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false)); @@ -1464,7 +1456,7 @@ namespace Ryujinx.UI.App.Common if (addedNewDlc) { var gameDlcs = it.Items.Where(dlc => dlc.Dlc.TitleIdBase == application.IdBase).ToList(); - DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase, + DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, gameDlcs); } } @@ -1483,11 +1475,11 @@ namespace Ryujinx.UI.App.Common TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase); it.AddOrUpdate(savedUpdates); - var selectedUpdate = savedUpdates.FirstOrOptional(update => update.IsSelected); + var selectedUpdate = savedUpdates.FindFirst(update => update.IsSelected); if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates)) { - var savedUpdateLookup = savedUpdates.Select(update => update.Item1).ToHashSet(); + var savedUpdateLookup = savedUpdates.Select(update => update.Update).ToHashSet(); bool updatesChanged = false; foreach (var update in bundledUpdates.OrderByDescending(bundled => bundled.Version)) @@ -1495,12 +1487,11 @@ namespace Ryujinx.UI.App.Common if (!savedUpdateLookup.Contains(update)) { bool shouldSelect = false; - if (!selectedUpdate.HasValue || selectedUpdate.Value.Item1.Version < update.Version) + if (selectedUpdate.Check(su => su.Update?.Version < update.Version)) { shouldSelect = true; - if (selectedUpdate.HasValue) - _titleUpdates.AddOrUpdate((selectedUpdate.Value.Item1, false)); - selectedUpdate = DynamicData.Kernel.Optional<(TitleUpdateModel, bool IsSelected)>.Create((update, true)); + _titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false)); + selectedUpdate = (update, true); } modifiedVersion = modifiedVersion || shouldSelect; @@ -1513,7 +1504,7 @@ namespace Ryujinx.UI.App.Common if (updatesChanged) { var gameUpdates = it.Items.Where(update => update.TitleUpdate.TitleIdBase == application.IdBase).ToList(); - TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, gameUpdates); + TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, gameUpdates); } } }); @@ -1525,14 +1516,14 @@ namespace Ryujinx.UI.App.Common private void SaveDownloadableContentsForGame(ulong titleIdBase) { var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList(); - DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, titleIdBase, dlcs); + DownloadableContentsHelper.SaveDownloadableContentsJson(titleIdBase, dlcs); } // Save the _currently tracked_ update state for the game private void SaveTitleUpdatesForGame(ulong titleIdBase) { var updates = TitleUpdates.Items.Where(update => update.TitleUpdate.TitleIdBase == titleIdBase).ToList(); - TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, titleIdBase, updates); + TitleUpdatesHelper.SaveTitleUpdatesJson(titleIdBase, updates); } // ApplicationData isnt live-updating (e.g. when an update gets applied) and so this is meant to trigger a refresh diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index badb047df..04ddd442f 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -1,16 +1,12 @@ -using ARMeilleure; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; using Ryujinx.Graphics.Vulkan; using Ryujinx.HLE; using Ryujinx.UI.Common.Configuration.System; using Ryujinx.UI.Common.Configuration.UI; using System; -using System.Collections.Generic; namespace Ryujinx.UI.Common.Configuration { @@ -21,10 +17,10 @@ namespace Ryujinx.UI.Common.Configuration if (Instance != null) { throw new InvalidOperationException("Configuration is already initialized"); - } + } Instance = new ConfigurationState(); - } + } public ConfigurationFileFormat ToFileFormat() { diff --git a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs b/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs index 623952b37..99b209c6e 100644 --- a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs @@ -7,6 +7,24 @@ namespace Ryujinx.UI.Common.Helper { public static partial class ConsoleHelper { + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32")] + private static partial nint GetConsoleWindow(); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool ShowWindow(nint hWnd, int nCmdShow); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + private static partial nint GetForegroundWindow(); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool SetForegroundWindow(nint hWnd); + public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows(); public static void SetConsoleWindowState(bool show) @@ -35,16 +53,11 @@ namespace Ryujinx.UI.Common.Helper return; } + SetForegroundWindow(hWnd); + + hWnd = GetForegroundWindow(); + ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); } - - [SupportedOSPlatform("windows")] - [LibraryImport("kernel32")] - private static partial nint GetConsoleWindow(); - - [SupportedOSPlatform("windows")] - [LibraryImport("user32")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool ShowWindow(nint hWnd, int nCmdShow); } } diff --git a/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs b/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs index 3695c5c5c..020529b55 100644 --- a/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs @@ -42,7 +42,7 @@ namespace Ryujinx.UI.Common.Helper } } - public static void SaveDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs) + public static void SaveDownloadableContentsJson(ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs) { DownloadableContentContainer container = default; List downloadableContentContainerList = new(); diff --git a/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs b/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs index 18fbabd6d..36de8b31a 100644 --- a/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs @@ -28,7 +28,7 @@ namespace Ryujinx.UI.Common.Helper { private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase) + public static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase) { var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase); @@ -49,7 +49,7 @@ namespace Ryujinx.UI.Common.Helper } } - public static void SaveTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates) + public static void SaveTitleUpdatesJson(ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates) { var titleUpdateWindowData = new TitleUpdateMetadata { @@ -77,7 +77,7 @@ namespace Ryujinx.UI.Common.Helper JsonHelper.SerializeToFile(titleUpdatesJsonPath, titleUpdateWindowData, _serializerContext.TitleUpdateMetadata); } - private static List<(TitleUpdateModel, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase) + private static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase) { var result = new List<(TitleUpdateModel, bool IsSelected)>(); diff --git a/src/Ryujinx/App.axaml b/src/Ryujinx/App.axaml index 5a603509c..5c96f97f2 100644 --- a/src/Ryujinx/App.axaml +++ b/src/Ryujinx/App.axaml @@ -11,7 +11,7 @@ - + diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 5789737d6..65c798ac2 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -3,6 +3,8 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Threading; +using LibHac.Common; +using LibHac.Ns; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.OpenAL; @@ -670,7 +672,7 @@ namespace Ryujinx.Ava _cursorState = CursorStates.ForceChangeCursor; } - public async Task LoadGuestApplication() + public async Task LoadGuestApplication(BlitStruct? customNacpData = null) { InitializeSwitchInstance(); MainWindow.UpdateGraphicsConfig(); @@ -740,7 +742,7 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - if (!Device.LoadNca(ApplicationPath)) + if (!Device.LoadNca(ApplicationPath, customNacpData)) { Device.Dispose(); @@ -1137,7 +1139,7 @@ namespace Ryujinx.Ava LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", _displayCount)); } diff --git a/src/Ryujinx/Assets/Locales/ar_SA.json b/src/Ryujinx/Assets/Locales/ar_SA.json index 34b4f7212..bdb6a92ec 100644 --- a/src/Ryujinx/Assets/Locales/ar_SA.json +++ b/src/Ryujinx/Assets/Locales/ar_SA.json @@ -702,6 +702,9 @@ "Never": "مطلقا", "SwkbdMinCharacters": "يجب أن يبلغ طوله {0} حرفا على الأقل", "SwkbdMinRangeCharacters": "يجب أن يتكون من {0}-{1} حرفا", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "لوحة المفاتيح البرمجية", "SoftwareKeyboardModeNumeric": "يجب أن يكون 0-9 أو '.' فقط", "SoftwareKeyboardModeAlphabet": "يجب أن تكون الأحرف غير CJK فقط", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "إضافة ملفات جديدة...", "UpdaterExtracting": "استخراج التحديث...", "UpdaterDownloading": "تحميل التحديث...", - "Game": "لعبة", "Docked": "تركيب بالمنصة", "Handheld": "محمول", "ConnectionError": "خطأ في الاتصال", @@ -786,7 +788,7 @@ "CheatWindowHeading": "الغش متوفر لـ {0} [{1}]", "BuildId": "معرف البناء:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "المحتويات القابلة للتنزيل {0}", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/de_DE.json b/src/Ryujinx/Assets/Locales/de_DE.json index 013120738..9f4b63a95 100644 --- a/src/Ryujinx/Assets/Locales/de_DE.json +++ b/src/Ryujinx/Assets/Locales/de_DE.json @@ -702,6 +702,9 @@ "Never": "Niemals", "SwkbdMinCharacters": "Muss mindestens {0} Zeichen lang sein", "SwkbdMinRangeCharacters": "Muss {0}-{1} Zeichen lang sein", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Software-Tastatur", "SoftwareKeyboardModeNumeric": "Darf nur 0-9 oder \".\" sein", "SoftwareKeyboardModeAlphabet": "Keine CJK-Zeichen", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Neue Dateien hinzufügen...", "UpdaterExtracting": "Update extrahieren...", "UpdaterDownloading": "Update herunterladen...", - "Game": "Spiel", "Docked": "Docked", "Handheld": "Handheld", "ConnectionError": "Verbindungsfehler.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Cheats verfügbar für {0} [{1}]", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", "BuildId": "BuildId:", - "DlcWindowHeading": "DLC verfügbar für {0} [{1}]", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/el_GR.json b/src/Ryujinx/Assets/Locales/el_GR.json index c5d6a60e6..7979b9228 100644 --- a/src/Ryujinx/Assets/Locales/el_GR.json +++ b/src/Ryujinx/Assets/Locales/el_GR.json @@ -702,6 +702,9 @@ "Never": "Ποτέ", "SwkbdMinCharacters": "Πρέπει να έχει μήκος τουλάχιστον {0} χαρακτήρες", "SwkbdMinRangeCharacters": "Πρέπει να έχει μήκος {0}-{1} χαρακτήρες", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Εικονικό Πληκτρολόγιο", "SoftwareKeyboardModeNumeric": "Πρέπει να είναι 0-9 ή '.' μόνο", "SoftwareKeyboardModeAlphabet": "Πρέπει να μην είναι μόνο χαρακτήρες CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Προσθήκη Νέων Αρχείων...", "UpdaterExtracting": "Εξαγωγή Ενημέρωσης...", "UpdaterDownloading": "Λήψη Ενημέρωσης...", - "Game": "Παιχνίδι", "Docked": "Προσκολλημένο", "Handheld": "Χειροκίνητο", "ConnectionError": "Σφάλμα Σύνδεσης.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]", "BuildId": "BuildId:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index b7ab8969b..0598665fd 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -714,6 +714,9 @@ "Never": "Never", "SwkbdMinCharacters": "Must be at least {0} characters long", "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Software Keyboard", "SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only", "SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only", @@ -727,7 +730,6 @@ "UpdaterAddingFiles": "Adding New Files...", "UpdaterExtracting": "Extracting Update...", "UpdaterDownloading": "Downloading Update...", - "Game": "Game", "Docked": "Docked", "Handheld": "Handheld", "ConnectionError": "Connection Error.", @@ -800,7 +802,7 @@ "CheatWindowHeading": "Cheats Available for {0} [{1}]", "BuildId": "BuildId:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/es_ES.json b/src/Ryujinx/Assets/Locales/es_ES.json index 730bd7961..3774605f6 100644 --- a/src/Ryujinx/Assets/Locales/es_ES.json +++ b/src/Ryujinx/Assets/Locales/es_ES.json @@ -1,7 +1,7 @@ { "Language": "Español (ES)", "MenuBarFileOpenApplet": "Abrir applet", - "MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet", + "MenuBarFileOpenAppletOpenMiiApplet": "Applet Editor Mii", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo", "SettingsTabInputDirectMouseAccess": "Acceso directo al ratón", "SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:", @@ -32,12 +32,12 @@ "MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware desde un archivo XCI o ZIP", "MenuBarFileToolsInstallFirmwareFromDirectory": "Instalar firmware desde una carpeta", "MenuBarToolsInstallKeys": "Install Keys", - "MenuBarFileToolsInstallKeysFromFile": "Install keys from KEYS or ZIP", - "MenuBarFileToolsInstallKeysFromFolder": "Install keys from a directory", + "MenuBarFileToolsInstallKeysFromFile": "Instalar keys de KEYS o ZIP", + "MenuBarFileToolsInstallKeysFromFolder": "Instalar keys de un directorio", "MenuBarToolsManageFileTypes": "Administrar tipos de archivo", "MenuBarToolsInstallFileTypes": "Instalar tipos de archivo", "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo", - "MenuBarToolsXCITrimmer": "Trim XCI Files", + "MenuBarToolsXCITrimmer": "Recortar archivos XCI", "MenuBarView": "_View", "MenuBarViewWindow": "Tamaño Ventana", "MenuBarViewWindow720": "720p", @@ -89,11 +89,11 @@ "GameListContextMenuOpenModsDirectoryToolTip": "Abre el directorio que contiene los Mods de la Aplicación.", "GameListContextMenuOpenSdModsDirectory": "Abrir Directorio de Mods de Atmosphere\n\n\n\n\n\n", "GameListContextMenuOpenSdModsDirectoryToolTip": "Abre el directorio alternativo de la tarjeta SD de Atmosphere que contiene los Mods de la Aplicación. Útil para los mods que están empaquetados para el hardware real.", - "GameListContextMenuTrimXCI": "Check and Trim XCI File", - "GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space", + "GameListContextMenuTrimXCI": "Verificar y recortar archivo XCI", + "GameListContextMenuTrimXCIToolTip": "Verificar y recortar archivo XCI para ahorrar espacio en disco", "StatusBarGamesLoaded": "{0}/{1} juegos cargados", "StatusBarSystemVersion": "Versión del sistema: {0}", - "StatusBarXCIFileTrimming": "Trimming XCI File '{0}'", + "StatusBarXCIFileTrimming": "Recortando el siguiente archivo XCI: '{0}'", "LinuxVmMaxMapCountDialogTitle": "Límite inferior para mapeos de memoria detectado", "LinuxVmMaxMapCountDialogTextPrimary": "¿Quieres aumentar el valor de vm.max_map_count a {0}?", "LinuxVmMaxMapCountDialogTextSecondary": "Algunos juegos podrían intentar crear más mapeos de memoria de los permitidos. Ryujinx se bloqueará tan pronto como se supere este límite.", @@ -480,7 +480,7 @@ "DialogUninstallFileTypesSuccessMessage": "¡Tipos de archivos desinstalados con éxito!", "DialogUninstallFileTypesErrorMessage": "No se pudo desinstalar los tipos de archivo.", "DialogOpenSettingsWindowLabel": "Abrir ventana de opciones", - "DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window", + "DialogOpenXCITrimmerWindowLabel": "Ventana recortador XCI", "DialogControllerAppletTitle": "Applet de mandos", "DialogMessageDialogErrorExceptionMessage": "Error al mostrar cuadro de diálogo: {0}", "DialogSoftwareKeyboardErrorExceptionMessage": "Error al mostrar teclado de software: {0}", @@ -509,13 +509,13 @@ "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n¿Continuar?", "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalando firmware...", "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Versión de sistema {0} instalada con éxito.", - "DialogKeysInstallerKeysNotFoundErrorMessage": "An invalid Keys file was found in {0}", - "DialogKeysInstallerKeysInstallTitle": "Install Keys", - "DialogKeysInstallerKeysInstallMessage": "New Keys file will be installed.", - "DialogKeysInstallerKeysInstallSubMessage": "\n\nThis may replace some of the current installed Keys.", - "DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDo you want to continue?", - "DialogKeysInstallerKeysInstallWaitMessage": "Installing Keys...", - "DialogKeysInstallerKeysInstallSuccessMessage": "New Keys file successfully installed.", + "DialogKeysInstallerKeysNotFoundErrorMessage": "Se halló un archivo Keys inválido en {0}", + "DialogKeysInstallerKeysInstallTitle": "Instalar Keys", + "DialogKeysInstallerKeysInstallMessage": "Un nuevo archivo Keys será instalado.", + "DialogKeysInstallerKeysInstallSubMessage": "\n\nEsto puede reemplazar algunas de las Keys actualmente instaladas.", + "DialogKeysInstallerKeysInstallConfirmMessage": "\n\nDeseas continuar?", + "DialogKeysInstallerKeysInstallWaitMessage": "Instalando Keys...", + "DialogKeysInstallerKeysInstallSuccessMessage": "Nuevo archivo Keys instalado con éxito.", "DialogUserProfileDeletionWarningMessage": "Si eliminas el perfil seleccionado no quedará ningún otro perfil", "DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?", "DialogUserProfileUnsavedChangesTitle": "Advertencia - Cambios sin guardar", @@ -688,20 +688,23 @@ "OpenSetupGuideMessage": "Abrir la guía de instalación", "NoUpdate": "No actualizado", "TitleUpdateVersionLabel": "Versión {0} - {1}", - "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", - "TitleBundledDlcLabel": "Bundled:", - "TitleXCIStatusPartialLabel": "Partial", - "TitleXCIStatusTrimmableLabel": "Untrimmed", - "TitleXCIStatusUntrimmableLabel": "Trimmed", - "TitleXCIStatusFailedLabel": "(Failed)", - "TitleXCICanSaveLabel": "Save {0:n0} Mb", - "TitleXCISavingLabel": "Saved {0:n0} Mb", + "TitleBundledUpdateVersionLabel": "Incorporado: Versión {0}", + "TitleBundledDlcLabel": "Incorporado:", + "TitleXCIStatusPartialLabel": "Parcial", + "TitleXCIStatusTrimmableLabel": "Sin recortar", + "TitleXCIStatusUntrimmableLabel": "Recortado", + "TitleXCIStatusFailedLabel": "(Fallido)", + "TitleXCICanSaveLabel": "Ahorra {0:n0} Mb", + "TitleXCISavingLabel": "{0:n0} Mb ahorrado(s)", "RyujinxInfo": "Ryujinx - Info", "RyujinxConfirm": "Ryujinx - Confirmación", "FileDialogAllTypes": "Todos los tipos", "Never": "Nunca", "SwkbdMinCharacters": "Debe tener al menos {0} caracteres", "SwkbdMinRangeCharacters": "Debe tener {0}-{1} caracteres", + "CabinetTitle": "Diálogo Gabinete", + "CabinetDialog": "Ingresa el nuevo nombre de tu Amiibo", + "CabinetScanDialog": "Escanea tu Amiibo ahora.", "SoftwareKeyboard": "Teclado de software", "SoftwareKeyboardModeNumeric": "Debe ser sólo 0-9 o '.'", "SoftwareKeyboardModeAlphabet": "Solo deben ser caracteres no CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Añadiendo nuevos archivos...", "UpdaterExtracting": "Extrayendo actualización...", "UpdaterDownloading": "Descargando actualización...", - "Game": "Juego", "Docked": "Dock/TV", "Handheld": "Portátil", "ConnectionError": "Error de conexión.", @@ -748,44 +750,44 @@ "SelectDlcDialogTitle": "Selecciona archivo(s) de DLC", "SelectUpdateDialogTitle": "Selecciona archivo(s) de actualización", "SelectModDialogTitle": "Seleccionar un directorio de Mods", - "TrimXCIFileDialogTitle": "Check and Trim XCI File", - "TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.", - "TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB", - "TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details", - "TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details", - "TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details", - "TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.", - "TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim", - "TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details", - "TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details", - "TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed", - "TrimXCIFileCancelled": "The operation was cancelled", - "TrimXCIFileFileUndertermined": "No operation was performed", + "TrimXCIFileDialogTitle": "Verificar y recortar archivo XCI", + "TrimXCIFileDialogPrimaryText": "Esta función verificará el espacio vacío y después recortará el archivo XCI para ahorrar espacio en disco", + "TrimXCIFileDialogSecondaryText": "Tamaño de archivo actual: {0:n} MB\nTamaño de datos de juego: {1:n} MB\nAhorro de espacio en disco: {2:n} MB", + "TrimXCIFileNoTrimNecessary": "El archivo XCI no necesita ser recortado. Verifica los logs para más detalles.", + "TrimXCIFileNoUntrimPossible": "El recorte del archivo XCI no puede ser deshecho. Verifica los registros para más detalles.", + "TrimXCIFileReadOnlyFileCannotFix": "El archivo XCI es de solo Lectura y no se le puede escribir. Lee el registro para más información.", + "TrimXCIFileFileSizeChanged": "El archivo XCI ha cambiado de tamaño desde que fue escaneado. Verifica que no se esté escribiendo al archivo y vuelve a intentarlo.", + "TrimXCIFileFreeSpaceCheckFailed": "El archivo XCI tiene datos en el área de espacio libre, no es seguro recortar.", + "TrimXCIFileInvalidXCIFile": "El archivo XCI contiene datos inválidos. Lee el registro para más información.", + "TrimXCIFileFileIOWriteError": "El archivo XCI no se puede abrir para escribirlo. Lee el registro para más información.", + "TrimXCIFileFailedPrimaryText": "El recorte del archivo XCI falló", + "TrimXCIFileCancelled": "La operación fue cancelada", + "TrimXCIFileFileUndertermined": "No se realizó ninguna operación", "UserProfileWindowTitle": "Administrar perfiles de usuario", "CheatWindowTitle": "Administrar cheats", "DlcWindowTitle": "Administrar contenido descargable", "ModWindowTitle": "Administrar Mods para {0} ({1})", "UpdateWindowTitle": "Administrar actualizaciones", - "XCITrimmerWindowTitle": "XCI File Trimmer", - "XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected", - "XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)", - "XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...", - "XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...", - "XCITrimmerTitleStatusFailed": "Failed", - "XCITrimmerPotentialSavings": "Potential Savings", - "XCITrimmerActualSavings": "Actual Savings", + "XCITrimmerWindowTitle": "Recortador de archivos XCI", + "XCITrimmerTitleStatusCount": "{0} de {1} Título(s) seleccionado(s)", + "XCITrimmerTitleStatusCountWithFilter": "{0} de {1} Título(s) seleccionado(s) ({2} mostrado(s))", + "XCITrimmerTitleStatusTrimming": "Recortando {0} Título(s)...", + "XCITrimmerTitleStatusUntrimming": "Deshaciendo recorte de {0} Título(s)...", + "XCITrimmerTitleStatusFailed": "Fallido", + "XCITrimmerPotentialSavings": "Ahorro potencial", + "XCITrimmerActualSavings": "Ahorro real", "XCITrimmerSavingsMb": "{0:n0} Mb", - "XCITrimmerSelectDisplayed": "Select Shown", - "XCITrimmerDeselectDisplayed": "Deselect Shown", - "XCITrimmerSortName": "Title", - "XCITrimmerSortSaved": "Space Savings", - "XCITrimmerTrim": "Trim", - "XCITrimmerUntrim": "Untrim", + "XCITrimmerSelectDisplayed": "Seleccionar mostrado(s)", + "XCITrimmerDeselectDisplayed": "Deseleccionar mostrado(s)", + "XCITrimmerSortName": "Título", + "XCITrimmerSortSaved": "Ahorro de espacio", + "XCITrimmerTrim": "Recortar", + "XCITrimmerUntrim": "Deshacer recorte", "UpdateWindowUpdateAddedMessage": "{0} nueva(s) actualización(es) agregada(s)", "UpdateWindowBundledContentNotice": "Las actualizaciones agrupadas no pueden ser eliminadas, solamente deshabilitadas.", "CheatWindowHeading": "Cheats disponibles para {0} [{1}]", "BuildId": "Id de compilación:", - "DlcWindowHeading": "Contenido descargable disponible para {0} [{1}]", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "Se agregaron {0} nuevo(s) contenido(s) descargable(s)", "AutoloadDlcAddedMessage": "Se agregaron {0} nuevo(s) contenido(s) descargable(s)", "AutoloadDlcRemovedMessage": "Se eliminaron {0} contenido(s) descargable(s) faltantes", @@ -793,7 +795,7 @@ "AutoloadUpdateRemovedMessage": "Se eliminaron {0} actualización(es) faltantes", "ModWindowHeading": "{0} Mod(s)", "UserProfilesEditProfile": "Editar selección", - "Continue": "Continue", + "Continue": "Continuar", "Cancel": "Cancelar", "Save": "Guardar", "Discard": "Descartar", diff --git a/src/Ryujinx/Assets/Locales/fr_FR.json b/src/Ryujinx/Assets/Locales/fr_FR.json index 947c48eab..c5a4bbeec 100644 --- a/src/Ryujinx/Assets/Locales/fr_FR.json +++ b/src/Ryujinx/Assets/Locales/fr_FR.json @@ -702,6 +702,9 @@ "Never": "Jamais", "SwkbdMinCharacters": "Doit comporter au moins {0} caractères", "SwkbdMinRangeCharacters": "Doit comporter entre {0} et {1} caractères", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Clavier logiciel", "SoftwareKeyboardModeNumeric": "Doit être 0-9 ou '.' uniquement", "SoftwareKeyboardModeAlphabet": "Doit être uniquement des caractères non CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Ajout des nouveaux fichiers...", "UpdaterExtracting": "Extraction de la mise à jour…", "UpdaterDownloading": "Téléchargement de la mise à jour...", - "Game": "Jeu", "Docked": "Mode station d'accueil", "Handheld": "Mode Portable", "ConnectionError": "Erreur de connexion.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Cheats disponibles pour {0} [{1}]", "BuildId": "BuildId :", "DlcWindowBundledContentNotice": "Les DLC inclus avec le jeu ne peuvent pas être supprimés mais peuvent être désactivés.", - "DlcWindowHeading": "{0} Contenu(s) téléchargeable(s)", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)", "AutoloadDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)", "AutoloadDlcRemovedMessage": "{0} contenu(s) téléchargeable(s) manquant(s) supprimé(s)", diff --git a/src/Ryujinx/Assets/Locales/he_IL.json b/src/Ryujinx/Assets/Locales/he_IL.json index 88b6a059a..318068bf3 100644 --- a/src/Ryujinx/Assets/Locales/he_IL.json +++ b/src/Ryujinx/Assets/Locales/he_IL.json @@ -702,6 +702,9 @@ "Never": "אף פעם", "SwkbdMinCharacters": "לפחות {0} תווים", "SwkbdMinRangeCharacters": "באורך {0}-{1} תווים", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "מקלדת וירטואלית", "SoftwareKeyboardModeNumeric": "חייב להיות בין 0-9 או '.' בלבד", "SoftwareKeyboardModeAlphabet": "מחויב להיות ללא אותיות CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "מוסיף קבצים חדשים...", "UpdaterExtracting": "מחלץ עדכון...", "UpdaterDownloading": "מוריד עדכון...", - "Game": "משחק", "Docked": "בתחנת עגינה", "Handheld": "נייד", "ConnectionError": "שגיאת חיבור", diff --git a/src/Ryujinx/Assets/Locales/it_IT.json b/src/Ryujinx/Assets/Locales/it_IT.json index e689a2cd9..18e4ee04f 100644 --- a/src/Ryujinx/Assets/Locales/it_IT.json +++ b/src/Ryujinx/Assets/Locales/it_IT.json @@ -702,6 +702,9 @@ "Never": "Mai", "SwkbdMinCharacters": "Non può avere meno di {0} caratteri", "SwkbdMinRangeCharacters": "Può avere da {0} a {1} caratteri", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Tastiera software", "SoftwareKeyboardModeNumeric": "Deve essere solo 0-9 o '.'", "SoftwareKeyboardModeAlphabet": "Deve essere solo caratteri non CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Aggiunta dei nuovi file...", "UpdaterExtracting": "Estrazione dell'aggiornamento...", "UpdaterDownloading": "Download dell'aggiornamento...", - "Game": "Gioco", "Docked": "TV", "Handheld": "Portatile", "ConnectionError": "Errore di connessione.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Trucchi disponibili per {0} [{1}]", "BuildId": "ID Build", "DlcWindowBundledContentNotice": "i DLC \"impacchettati\" non possono essere rimossi, ma solo disabilitati.", - "DlcWindowHeading": "DLC disponibili per {0} [{1}]", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} nuovo/i contenuto/i scaricabile/i aggiunto/i", "AutoloadDlcAddedMessage": "{0} contenuto/i scaricabile/i aggiunto/i", "AutoloadDlcRemovedMessage": "{0} contenuto/i scaricabile/i mancante/i rimosso/i", diff --git a/src/Ryujinx/Assets/Locales/ja_JP.json b/src/Ryujinx/Assets/Locales/ja_JP.json index d55d1449d..6ecc74009 100644 --- a/src/Ryujinx/Assets/Locales/ja_JP.json +++ b/src/Ryujinx/Assets/Locales/ja_JP.json @@ -702,6 +702,9 @@ "Never": "決して", "SwkbdMinCharacters": "最低 {0} 文字必要です", "SwkbdMinRangeCharacters": "{0}-{1} 文字にしてください", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "ソフトウェアキーボード", "SoftwareKeyboardModeNumeric": "0-9 または '.' のみでなければなりません", "SoftwareKeyboardModeAlphabet": "CJK文字以外のみ", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "新規ファイルを追加中...", "UpdaterExtracting": "アップデートを展開中...", "UpdaterDownloading": "アップデートをダウンロード中...", - "Game": "ゲーム", "Docked": "ドッキング", "Handheld": "携帯", "ConnectionError": "接続エラー.", @@ -785,7 +787,7 @@ "UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.", "CheatWindowHeading": "利用可能なチート {0} [{1}]", "BuildId": "ビルドID:", - "DlcWindowHeading": "利用可能な DLC {0} [{1}]", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/ko_KR.json b/src/Ryujinx/Assets/Locales/ko_KR.json index 8a3799e15..71aaa41e8 100644 --- a/src/Ryujinx/Assets/Locales/ko_KR.json +++ b/src/Ryujinx/Assets/Locales/ko_KR.json @@ -1,7 +1,7 @@ { "Language": "한국어", "MenuBarFileOpenApplet": "애플릿 열기", - "MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet", + "MenuBarFileOpenAppletOpenMiiApplet": "Mii 편집 애플릿", "MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드로 Mii 편집기 애플릿 열기", "SettingsTabInputDirectMouseAccess": "마우스 직접 접근", "SettingsTabSystemMemoryManagerMode": "메모리 관리자 모드 :", @@ -484,7 +484,7 @@ "DialogControllerAppletTitle": "컨트롤러 애플릿", "DialogMessageDialogErrorExceptionMessage": "메시지 대화 상자 표시 오류 : {0}", "DialogSoftwareKeyboardErrorExceptionMessage": "소프트웨어 키보드 표시 오류 : {0}", - "DialogErrorAppletErrorExceptionMessage": "ErrorApplet 대화 상자 표시 오류 : {0}", + "DialogErrorAppletErrorExceptionMessage": "애플릿 오류류 대화 상자 표시 오류 : {0}", "DialogUserErrorDialogMessage": "{0}: {1}", "DialogUserErrorDialogInfoMessage": "\n이 오류를 해결하는 방법에 대한 자세한 내용은 설정 가이드를 참조하세요.", "DialogUserErrorDialogTitle": "Ryujinx 오류 ({0})", @@ -702,6 +702,9 @@ "Never": "절대 안 함", "SwkbdMinCharacters": "{0}자 이상이어야 함", "SwkbdMinRangeCharacters": "{0}-{1}자 길이여야 함", + "CabinetTitle": "캐비닛 대화 상자", + "CabinetDialog": "Amiibo의 새 이름 입력하기", + "CabinetScanDialog": "지금 Amiibo를 스캔하세요.", "SoftwareKeyboard": "소프트웨어 키보드", "SoftwareKeyboardModeNumeric": "0-9 또는 '.'만 가능", "SoftwareKeyboardModeAlphabet": "CJK 문자가 아닌 문자만 가능", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "새 파일 추가...", "UpdaterExtracting": "업데이트 추출...", "UpdaterDownloading": "업데이트 내려받기 중...", - "Game": "게임", "Docked": "도킹", "Handheld": "휴대", "ConnectionError": "연결 오류가 발생했습니다.", @@ -779,14 +781,14 @@ "XCITrimmerDeselectDisplayed": "표시됨 선택 취소", "XCITrimmerSortName": "타이틀", "XCITrimmerSortSaved": "공간 절약s", - "XCITrimmerTrim": "Trim", - "XCITrimmerUntrim": "Untrim", + "XCITrimmerTrim": "트림", + "XCITrimmerUntrim": "언트림", "UpdateWindowUpdateAddedMessage": "{0}개의 새 업데이트가 추가됨", "UpdateWindowBundledContentNotice": "번들 업데이트는 제거할 수 없으며, 비활성화만 가능합니다.", "CheatWindowHeading": "{0} [{1}]에 사용 가능한 치트", "BuildId": "빌드ID:", "DlcWindowBundledContentNotice": "번들 DLC는 제거할 수 없으며 비활성화만 가능합니다.", - "DlcWindowHeading": "{1} ({2})에 내려받기 가능한 콘텐츠 {0}개 사용 가능", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0}개의 새로운 내려받기 가능한 콘텐츠가 추가됨", "AutoloadDlcAddedMessage": "{0}개의 새로운 내려받기 가능한 콘텐츠가 추가됨", "AutoloadDlcRemovedMessage": "{0}개의 내려받기 가능한 콘텐츠가 제거됨", diff --git a/src/Ryujinx/Assets/Locales/pl_PL.json b/src/Ryujinx/Assets/Locales/pl_PL.json index c3202020f..6a2fa08b1 100644 --- a/src/Ryujinx/Assets/Locales/pl_PL.json +++ b/src/Ryujinx/Assets/Locales/pl_PL.json @@ -702,6 +702,9 @@ "Never": "Nigdy", "SwkbdMinCharacters": "Musi mieć co najmniej {0} znaków", "SwkbdMinRangeCharacters": "Musi mieć długość od {0}-{1} znaków", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Klawiatura Oprogramowania", "SoftwareKeyboardModeNumeric": "Może składać się jedynie z 0-9 lub '.'", "SoftwareKeyboardModeAlphabet": "Nie może zawierać znaków CJK", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Dodawanie Nowych Plików...", "UpdaterExtracting": "Wypakowywanie Aktualizacji...", "UpdaterDownloading": "Pobieranie Aktualizacji...", - "Game": "Gra", "Docked": "Zadokowany", "Handheld": "Przenośny", "ConnectionError": "Błąd Połączenia.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Kody Dostępne dla {0} [{1}]", "BuildId": "Identyfikator wersji:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "{0} Zawartości do Pobrania dostępna dla {1} ({2})", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/pt_BR.json b/src/Ryujinx/Assets/Locales/pt_BR.json index 71992434b..90b78b732 100644 --- a/src/Ryujinx/Assets/Locales/pt_BR.json +++ b/src/Ryujinx/Assets/Locales/pt_BR.json @@ -701,6 +701,9 @@ "Never": "Nunca", "SwkbdMinCharacters": "Deve ter pelo menos {0} caracteres", "SwkbdMinRangeCharacters": "Deve ter entre {0}-{1} caracteres", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Teclado por Software", "SoftwareKeyboardModeNumeric": "Deve ser somente 0-9 ou '.'", "SoftwareKeyboardModeAlphabet": "Apenas devem ser caracteres não CJK.", @@ -714,7 +717,6 @@ "UpdaterAddingFiles": "Adicionando novos arquivos...", "UpdaterExtracting": "Extraíndo atualização...", "UpdaterDownloading": "Baixando atualização...", - "Game": "Jogo", "Docked": "TV", "Handheld": "Portátil", "ConnectionError": "Erro de conexão.", @@ -785,7 +787,7 @@ "CheatWindowHeading": "Cheats disponíveis para {0} [{1}]", "BuildId": "ID da Build:", "DlcWindowBundledContentNotice": "DLCs incorporadas não podem ser removidas, apenas desativadas.", - "DlcWindowHeading": "{0} DLCs disponíveis para {1} ({2})", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} novo(s) conteúdo(s) para download adicionado(s)", "AutoloadDlcAddedMessage": "{0} novo(s) conteúdo(s) para download adicionado(s)", "AutoloadDlcRemovedMessage": "{0} conteúdo(s) para download ausente(s) removido(s)", diff --git a/src/Ryujinx/Assets/Locales/ru_RU.json b/src/Ryujinx/Assets/Locales/ru_RU.json index f0218ffcc..f058154e9 100644 --- a/src/Ryujinx/Assets/Locales/ru_RU.json +++ b/src/Ryujinx/Assets/Locales/ru_RU.json @@ -702,6 +702,9 @@ "Never": "Никогда", "SwkbdMinCharacters": "Должно быть не менее {0} символов.", "SwkbdMinRangeCharacters": "Должно быть {0}-{1} символов", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Программная клавиатура", "SoftwareKeyboardModeNumeric": "Должно быть в диапазоне 0-9 или '.'", "SoftwareKeyboardModeAlphabet": "Не должно быть CJK-символов", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Добавление новых файлов...", "UpdaterExtracting": "Извлечение обновления...", "UpdaterDownloading": "Загрузка обновления...", - "Game": "Игра", "Docked": "Стационарный режим", "Handheld": "Портативный режим", "ConnectionError": "Ошибка соединения", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Доступные читы для {0} [{1}]", "BuildId": "ID версии:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "{0} DLC", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/th_TH.json b/src/Ryujinx/Assets/Locales/th_TH.json index 02ddda899..33b2c4f30 100644 --- a/src/Ryujinx/Assets/Locales/th_TH.json +++ b/src/Ryujinx/Assets/Locales/th_TH.json @@ -702,6 +702,9 @@ "Never": "ไม่ต้อง", "SwkbdMinCharacters": "ต้องมีความยาวของตัวอักษรอย่างน้อย {0} ตัว", "SwkbdMinRangeCharacters": "ต้องมีความยาวของตัวอักษร {0}-{1} ตัว", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "ซอฟต์แวร์คีย์บอร์ด", "SoftwareKeyboardModeNumeric": "ต้องเป็น 0-9 หรือ '.' เท่านั้น", "SoftwareKeyboardModeAlphabet": "ต้องเป็นตัวอักษรที่ไม่ใช่ประเภท CJK เท่านั้น", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "กำลังเพิ่มไฟล์ใหม่...", "UpdaterExtracting": "กำลังแยกการอัปเดต...", "UpdaterDownloading": "กำลังดาวน์โหลดอัปเดต...", - "Game": "เกมส์", "Docked": "ด็อก", "Handheld": "แฮนด์เฮลด์", "ConnectionError": "การเชื่อมต่อล้มเหลว", @@ -786,7 +788,7 @@ "CheatWindowHeading": "สูตรโกงมีให้สำหรับ {0} [{1}]", "BuildId": "รหัสการสร้าง:", "DlcWindowBundledContentNotice": "แพ็ค DLC ไม่สามารถลบทิ้งได้ สามารถปิดใช้งานได้เท่านั้น", - "DlcWindowHeading": "{0} DLC ที่สามารถดาวน์โหลดได้", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} DLC ใหม่ที่เพิ่มเข้ามา", "AutoloadDlcAddedMessage": "{0} ใหม่ที่เพิ่มเข้ามา", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/tr_TR.json b/src/Ryujinx/Assets/Locales/tr_TR.json index a65064a38..72da205cb 100644 --- a/src/Ryujinx/Assets/Locales/tr_TR.json +++ b/src/Ryujinx/Assets/Locales/tr_TR.json @@ -702,6 +702,9 @@ "Never": "Hiçbir Zaman", "SwkbdMinCharacters": "En az {0} karakter uzunluğunda olmalı", "SwkbdMinRangeCharacters": "{0}-{1} karakter uzunluğunda olmalı", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Yazılım Klavyesi", "SoftwareKeyboardModeNumeric": "Sadece 0-9 veya '.' olabilir", "SoftwareKeyboardModeAlphabet": "Sadece CJK-characters olmayan karakterler olabilir", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Yeni Dosyalar Ekleniyor...", "UpdaterExtracting": "Güncelleme Ayrıştırılıyor...", "UpdaterDownloading": "Güncelleme İndiriliyor...", - "Game": "Oyun", "Docked": "Docked", "Handheld": "El tipi", "ConnectionError": "Bağlantı Hatası.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "{0} için Hile mevcut [{1}]", "BuildId": "BuildId:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/uk_UA.json b/src/Ryujinx/Assets/Locales/uk_UA.json index ef26ace65..06f658640 100644 --- a/src/Ryujinx/Assets/Locales/uk_UA.json +++ b/src/Ryujinx/Assets/Locales/uk_UA.json @@ -702,6 +702,9 @@ "Never": "Ніколи", "SwkbdMinCharacters": "Мінімальна кількість символів: {0}", "SwkbdMinRangeCharacters": "Має бути {0}-{1} символів", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "Програмна клавіатура", "SoftwareKeyboardModeNumeric": "Повинно бути лише 0-9 або “.”", "SoftwareKeyboardModeAlphabet": "Повинно бути лише не CJK-символи", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "Додавання нових файлів...", "UpdaterExtracting": "Видобування оновлення...", "UpdaterDownloading": "Завантаження оновлення...", - "Game": "Гра", "Docked": "Док-станція", "Handheld": "Портативний", "ConnectionError": "Помилка з'єднання.", @@ -786,7 +788,7 @@ "CheatWindowHeading": "Коди доступні для {0} [{1}]", "BuildId": "ID збірки:", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", - "DlcWindowHeading": "Вміст для завантаження, доступний для {1} ({2}): {0}", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcAddedMessage": "{0} new downloadable content(s) added", "AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed", diff --git a/src/Ryujinx/Assets/Locales/zh_CN.json b/src/Ryujinx/Assets/Locales/zh_CN.json index dc3f27b5a..66ac309de 100644 --- a/src/Ryujinx/Assets/Locales/zh_CN.json +++ b/src/Ryujinx/Assets/Locales/zh_CN.json @@ -702,6 +702,9 @@ "Never": "从不", "SwkbdMinCharacters": "不少于 {0} 个字符", "SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字符", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "软键盘", "SoftwareKeyboardModeNumeric": "只能输入 0-9 或 \".\"", "SoftwareKeyboardModeAlphabet": "仅支持非中文字符", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "安装更新中...", "UpdaterExtracting": "正在提取更新...", "UpdaterDownloading": "下载更新中...", - "Game": "游戏", "Docked": "主机模式", "Handheld": "掌机模式", "ConnectionError": "连接错误。", diff --git a/src/Ryujinx/Assets/Locales/zh_TW.json b/src/Ryujinx/Assets/Locales/zh_TW.json index c33885784..64f137885 100644 --- a/src/Ryujinx/Assets/Locales/zh_TW.json +++ b/src/Ryujinx/Assets/Locales/zh_TW.json @@ -702,6 +702,9 @@ "Never": "從不", "SwkbdMinCharacters": "長度必須至少為 {0} 個字元", "SwkbdMinRangeCharacters": "長度必須為 {0} 到 {1} 個字元", + "CabinetTitle": "Cabinet Dialog", + "CabinetDialog": "Enter your Amiibo's new name", + "CabinetScanDialog": "Please scan your Amiibo now.", "SoftwareKeyboard": "軟體鍵盤", "SoftwareKeyboardModeNumeric": "必須是 0 到 9 或「.」", "SoftwareKeyboardModeAlphabet": "必須是「非中日韓字元」 (non CJK)", @@ -715,7 +718,6 @@ "UpdaterAddingFiles": "正在加入新檔案...", "UpdaterExtracting": "正在提取更新...", "UpdaterDownloading": "正在下載更新...", - "Game": "遊戲", "Docked": "底座模式", "Handheld": "手提模式", "ConnectionError": "連線錯誤。", @@ -786,7 +788,7 @@ "CheatWindowHeading": "可用於 {0} [{1}] 的密技", "BuildId": "組建識別碼:", "DlcWindowBundledContentNotice": "附帶的 DLC 只能被停用而無法被刪除。", - "DlcWindowHeading": "{0} 個可下載內容", + "DlcWindowHeading": "{0} DLC(s) available", "DlcWindowDlcAddedMessage": "已加入 {0} 個 DLC", "AutoloadDlcAddedMessage": "已加入 {0} 個 DLC", "AutoloadDlcRemovedMessage": "已刪除 {0} 個遺失的 DLC", diff --git a/src/Ryujinx/Assets/Styles/Themes.xaml b/src/Ryujinx/Assets/Styles/Themes.xaml index 056eba228..46e298035 100644 --- a/src/Ryujinx/Assets/Styles/Themes.xaml +++ b/src/Ryujinx/Assets/Styles/Themes.xaml @@ -4,18 +4,6 @@ - - - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FFe8e8e8 #FF00FABB #FFF0F0F0 #FFd6d6d6 @@ -26,6 +14,7 @@ #b3ffffff #80cccccc #A0000000 + #fffcd12a #FF2EEAC9 #FFFF4554 #6483F5 @@ -33,18 +22,6 @@ - - - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FF00C3E3 - #FFe8e8e8 #FF00FABB #FFF0F0F0 #FFd6d6d6 @@ -59,18 +36,7 @@ - - #008AA8 - #FF00C3E3 - #FF99b000 - #FF006d7d - #FF00525E - #FF00dbff - #FF19dfff - #FF33e3ff #FF00FABB #FF2D2D2D #FF505050 diff --git a/src/Ryujinx/Common/Markup/BasicMarkupExtension.cs b/src/Ryujinx/Common/Markup/BasicMarkupExtension.cs index 67c016562..b1b7361a6 100644 --- a/src/Ryujinx/Common/Markup/BasicMarkupExtension.cs +++ b/src/Ryujinx/Common/Markup/BasicMarkupExtension.cs @@ -17,13 +17,13 @@ namespace Ryujinx.Ava.Common.Markup public virtual string Name => "Item"; public virtual Action? Setter => null; - protected abstract T? GetValue(); + protected abstract T? Value { get; } protected virtual void ConfigureBindingExtension(CompiledBindingExtension _) { } private ClrPropertyInfo PropertyInfo => new(Name, - _ => GetValue(), + _ => Value, Setter as Action, typeof(T)); diff --git a/src/Ryujinx/Common/Markup/MarkupExtensions.cs b/src/Ryujinx/Common/Markup/MarkupExtensions.cs index a804792c7..cae6d8c2c 100644 --- a/src/Ryujinx/Common/Markup/MarkupExtensions.cs +++ b/src/Ryujinx/Common/Markup/MarkupExtensions.cs @@ -6,17 +6,17 @@ namespace Ryujinx.Ava.Common.Markup { internal class IconExtension(string iconString) : BasicMarkupExtension { - protected override Icon GetValue() => new() { Value = iconString }; + protected override Icon Value => new() { Value = iconString }; } internal class SpinningIconExtension(string iconString) : BasicMarkupExtension { - protected override Icon GetValue() => new() { Value = iconString, Animation = IconAnimation.Spin }; + protected override Icon Value => new() { Value = iconString, Animation = IconAnimation.Spin }; } internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension { - protected override string GetValue() => LocaleManager.Instance[key]; + protected override string Value => LocaleManager.Instance[key]; protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension) => bindingExtension.Source = LocaleManager.Instance; diff --git a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs index 2ebba7ac0..893ea95ac 100644 --- a/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs +++ b/src/Ryujinx/UI/Applet/AvaHostUIHandler.cs @@ -7,6 +7,7 @@ using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Windows; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.UI; using Ryujinx.UI.Common.Configuration; @@ -155,6 +156,55 @@ namespace Ryujinx.Ava.UI.Applet return error || okPressed; } + public bool DisplayCabinetDialog(out string userText) + { + ManualResetEvent dialogCloseEvent = new(false); + bool okPressed = false; + string inputText = "My Amiibo"; + Dispatcher.UIThread.InvokeAsync(async () => + { + try + { + _parent.ViewModel.AppHost.NpadManager.BlockInputUpdates(); + SoftwareKeyboardUIArgs args = new SoftwareKeyboardUIArgs(); + args.KeyboardMode = KeyboardMode.Default; + args.InitialText = "Ryujinx"; + args.StringLengthMin = 1; + args.StringLengthMax = 25; + (UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.CabinetDialog], args); + if (result == UserResult.Ok) + { + inputText = userInput; + okPressed = true; + } + } + finally + { + dialogCloseEvent.Set(); + } + }); + dialogCloseEvent.WaitOne(); + _parent.ViewModel.AppHost.NpadManager.UnblockInputUpdates(); + userText = inputText; + return okPressed; + } + + public void DisplayCabinetMessageDialog() + { + ManualResetEvent dialogCloseEvent = new(false); + Dispatcher.UIThread.InvokeAsync(async () => + { + dialogCloseEvent.Set(); + await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.CabinetScanDialog], + string.Empty, + LocaleManager.Instance[LocaleKeys.InputDialogOk], + string.Empty, + LocaleManager.Instance[LocaleKeys.CabinetTitle]); + }); + dialogCloseEvent.WaitOne(); + } + + public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) { device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); diff --git a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml index 98a1c004b..3bcb468ae 100644 --- a/src/Ryujinx/UI/Controls/ApplicationGridView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationGridView.axaml @@ -91,7 +91,7 @@ HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" - Foreground="{DynamicResource SystemAccentColor}" + Foreground="{DynamicResource FavoriteApplicationIconColor}" IsVisible="{Binding Favorite}" Symbol="StarFilled" /> diff --git a/src/Ryujinx/UI/Controls/ApplicationListView.axaml b/src/Ryujinx/UI/Controls/ApplicationListView.axaml index 0daa77ac4..8a72ebfbf 100644 --- a/src/Ryujinx/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx/UI/Controls/ApplicationListView.axaml @@ -146,7 +146,7 @@ HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" - Foreground="{DynamicResource SystemAccentColor}" + Foreground="{DynamicResource FavoriteApplicationIconColor}" IsVisible="{Binding Favorite}" Symbol="StarFilled" /> diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 3672f8c71..04db947b9 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -10,6 +10,7 @@ using DynamicData; using DynamicData.Binding; using FluentAvalonia.UI.Controls; using LibHac.Common; +using LibHac.Ns; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; @@ -70,7 +71,7 @@ namespace Ryujinx.Ava.UI.ViewModels private string _gpuStatusText; private string _shaderCountText; private bool _isAmiiboRequested; - private bool _showRightmostSeparator; + private bool _showShaderCompilationHint; private bool _isGameRunning; private bool _isFullScreen; private int _progressMaximum; @@ -275,12 +276,12 @@ namespace Ryujinx.Ava.UI.ViewModels public bool ShowFirmwareStatus => !ShowLoadProgress; - public bool ShowRightmostSeparator + public bool ShowShaderCompilationHint { - get => _showRightmostSeparator; + get => _showShaderCompilationHint; set { - _showRightmostSeparator = value; + _showShaderCompilationHint = value; OnPropertyChanged(); } @@ -1497,7 +1498,7 @@ namespace Ryujinx.Ava.UI.ViewModels VolumeStatusText = args.VolumeStatus; FifoStatusText = args.FifoStatus; - ShaderCountText = (ShowRightmostSeparator = args.ShaderCount > 0) + ShaderCountText = (ShowShaderCompilationHint = args.ShaderCount > 0) ? $"{LocaleManager.Instance[LocaleKeys.CompilingShaders]}: {args.ShaderCount}" : string.Empty; @@ -1897,7 +1898,7 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public async Task LoadApplication(ApplicationData application, bool startFullscreen = false) + public async Task LoadApplication(ApplicationData application, bool startFullscreen = false, BlitStruct? customNacpData = null) { if (AppHost != null) { @@ -1935,7 +1936,7 @@ namespace Ryujinx.Ava.UI.ViewModels this, TopLevel); - if (!await AppHost.LoadGuestApplication()) + if (!await AppHost.LoadGuestApplication(customNacpData)) { AppHost.DisposeContext(); AppHost = null; diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 41b27e9c1..ffe9b066c 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -3,7 +3,9 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; using Gommon; +using LibHac.Common; using LibHac.Ncm; +using LibHac.Ns; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; @@ -11,6 +13,7 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Utilities; +using Ryujinx.HLE; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; @@ -19,6 +22,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; namespace Ryujinx.Ava.UI.Views.Main { @@ -123,18 +127,24 @@ namespace Ryujinx.Ava.UI.Views.Main public async void OpenMiiApplet(object sender, RoutedEventArgs e) { - string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); + 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)) { ApplicationData applicationData = new() { - Name = "miiEdit", - Id = 0x0100000000001009, - Path = contentPath, + Name = AppletName, + Id = AppletProgramId, + Path = contentPath }; + + var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion); - await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen); + await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); } } @@ -200,7 +210,6 @@ namespace Ryujinx.Ava.UI.Views.Main await Dispatcher.UIThread.InvokeAsync(() => { - ViewModel.WindowState = WindowState.Normal; Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, windowWidthScaled, windowHeightScaled)); @@ -210,7 +219,7 @@ namespace Ryujinx.Ava.UI.Views.Main public async void CheckForUpdates(object sender, RoutedEventArgs e) { if (Updater.CanUpdate(true)) - await Window.BeginUpdateAsync(true); + await Updater.BeginUpdateAsync(true); } public async void OpenXCITrimmerWindow(object sender, RoutedEventArgs e) => await XCITrimmerWindow.Show(ViewModel); diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml index 597cf10e1..6e72a8b4b 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml @@ -23,7 +23,7 @@ Background="{DynamicResource ThemeContentBackgroundColor}" DockPanel.Dock="Bottom" IsVisible="{Binding ShowMenuAndStatusBar}" - ColumnDefinitions="Auto,Auto,*,Auto"> + ColumnDefinitions="Auto,Auto,*,Auto,Auto"> + + + + IsVisible="{Binding ShowShaderCompilationHint}" /> + + - - - + IsVisible="{Binding IsGameRunning}" /> diff --git a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs index 340515a5b..2afa8b529 100644 --- a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -61,23 +61,17 @@ namespace Ryujinx.Ava.UI.Windows private void RemoveDLC(object sender, RoutedEventArgs e) { - if (sender is Button button) + if (sender is Button { DataContext: DownloadableContentModel dlc }) { - if (button.DataContext is DownloadableContentModel model) - { - ViewModel.Remove(model); - } + ViewModel.Remove(dlc); } } private void OpenLocation(object sender, RoutedEventArgs e) { - if (sender is Button button) + if (sender is Button { DataContext: DownloadableContentModel dlc }) { - if (button.DataContext is DownloadableContentModel model) - { - OpenHelper.LocateFile(model.ContainerPath); - } + OpenHelper.LocateFile(dlc.ContainerPath); } } diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 059f99a60..09c8b9448 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -7,6 +7,7 @@ using Avalonia.Threading; using DynamicData; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Windowing; +using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; @@ -387,10 +388,8 @@ namespace Ryujinx.Ava.UI.Windows if (ConfigurationState.Instance.CheckUpdatesOnStart && !CommandLineState.HideAvailableUpdates && Updater.CanUpdate()) { - await this.BeginUpdateAsync() - .ContinueWith( - task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"), - TaskContinuationOptions.OnlyOnFaulted); + await Updater.BeginUpdateAsync() + .Catch(task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}")); } } diff --git a/src/Ryujinx/Updater.cs b/src/Ryujinx/Updater.cs index bdb44d668..6a1701208 100644 --- a/src/Ryujinx/Updater.cs +++ b/src/Ryujinx/Updater.cs @@ -1,4 +1,3 @@ -using Avalonia.Controls; using Avalonia.Threading; using FluentAvalonia.UI.Controls; using Gommon; @@ -51,7 +50,7 @@ namespace Ryujinx.Ava private static readonly string[] _windowsDependencyDirs = []; - public static async Task BeginUpdateAsync(this Window mainWindow, bool showVersionUpToDate = false) + public static async Task BeginUpdateAsync(bool showVersionUpToDate = false) { if (_running) { @@ -225,7 +224,7 @@ namespace Ryujinx.Ava ? $"Canary {currentVersion} -> Canary {newVersion}" : $"{currentVersion} -> {newVersion}"; - RequestUserToUpdate: + RequestUserToUpdate: // Show a message asking the user if they want to update UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog( LocaleManager.Instance[LocaleKeys.RyujinxUpdater], @@ -235,7 +234,7 @@ namespace Ryujinx.Ava switch (shouldUpdate) { case UserResult.Yes: - await UpdateRyujinx(mainWindow, _buildUrl); + await UpdateRyujinx(_buildUrl); break; // Secondary button maps to no, which in this case is the show changelog button. case UserResult.No: @@ -258,7 +257,7 @@ namespace Ryujinx.Ava return result; } - private static async Task UpdateRyujinx(Window parent, string downloadUrl) + private static async Task UpdateRyujinx(string downloadUrl) { _updateSuccessful = false; @@ -278,7 +277,7 @@ namespace Ryujinx.Ava SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], IconSource = new SymbolIconSource { Symbol = Symbol.Download }, ShowProgressBar = true, - XamlRoot = parent, + XamlRoot = App.MainWindow, }; taskDialog.Opened += (s, e) =>