diff --git a/Directory.Packages.props b/Directory.Packages.props
index fd61602a8..fba3792db 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -41,7 +41,7 @@
-
+
@@ -56,7 +56,6 @@
-
-
\ No newline at end of file
+
diff --git a/assets/Locales/Root.json b/assets/Locales/Root.json
index 9ecf9826c..3d23d4286 100644
--- a/assets/Locales/Root.json
+++ b/assets/Locales/Root.json
@@ -700,6 +700,56 @@
"zh_TW": "掃描 Amiibo"
}
},
+ {
+ "ID": "MenuBarActionsScanSkylander",
+ "Translations": {
+ "ar_SA": "فحص Skylander",
+ "de_DE": "Skylander scannen",
+ "el_GR": "Σάρωση Skylander",
+ "en_US": "Scan A Skylander",
+ "es_ES": "Escanear Skylander",
+ "fr_FR": "Scanner un Skylander",
+ "he_IL": "סרוק אמיבו",
+ "it_IT": "Scansiona un Skylander",
+ "ja_JP": "Skylander をスキャン",
+ "ko_KR": "Skylander 스캔",
+ "no_NO": "Skann en Skylander",
+ "pl_PL": "Skanuj Skylander",
+ "pt_BR": "Escanear um Skylander",
+ "ru_RU": "Сканировать Skylander",
+ "sv_SE": "Skanna en Skylander",
+ "th_TH": "สแกนหา Skylander",
+ "tr_TR": "Bir Skylander Tara",
+ "uk_UA": "Сканувати Skylander",
+ "zh_CN": "扫描 Skylander",
+ "zh_TW": "掃描 Skylander"
+ }
+ },
+ {
+ "ID": "MenuBarActionsRemoveSkylander",
+ "Translations": {
+ "ar_SA": "إزالة Skylander",
+ "de_DE": "Skylander entfernen",
+ "el_GR": "Αφαίρεση Skylander",
+ "en_US": "Remove Skylander",
+ "es_ES": "Eliminar Skylander",
+ "fr_FR": "Supprimer un Skylander",
+ "he_IL": "הסר Skylander",
+ "it_IT": "Rimuovi Skylander",
+ "ja_JP": "Skylander を削除",
+ "ko_KR": "Skylander 제거",
+ "no_NO": "Fjern Skylander",
+ "pl_PL": "Usuń Skylander",
+ "pt_BR": "Remover um Skylander",
+ "ru_RU": "Удалить Skylander",
+ "sv_SE": "Ta bort Skylander",
+ "th_TH": "ลบ Skylander",
+ "tr_TR": "Skylander'ı Kaldır",
+ "uk_UA": "Видалити Skylander",
+ "zh_CN": "移除 Skylander",
+ "zh_TW": "移除 Skylander"
+ }
+ },
{
"ID": "MenuBarActionsScanAmiiboBin",
"Translations": {
diff --git a/docs/compatibility.csv b/docs/compatibility.csv
index e476f9253..fa804c909 100644
--- a/docs/compatibility.csv
+++ b/docs/compatibility.csv
@@ -2050,7 +2050,9 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
+010057D00ECE4000,"Nintendo 64™ – Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ – Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
+010037A0170D2000,"NINTENDO 64™ – Nintendo Switch Online 18+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2638,6 +2640,7 @@
0100B16009C10000,"SINNER: Sacrifice for Redemption",nvdec;UE4;vulkan-backend-bug,playable,2022-08-12 20:37:33
0100E9201410E000,"Sir Lovelot",,playable,2021-04-05 16:21:46
0100134011E32000,"Skate City",,playable,2022-11-04 11:37:39
+0100a8501b66e000,"Skateboard Drifting with Maxwell Cat: The Game Simulator",,playable,2026-02-17 19:05:00
0100B2F008BD8000,"Skee-Ball",,playable,2020-11-16 04:44:07
01001A900F862000,"Skelattack",,playable,2021-06-09 15:26:26
01008E700F952000,"Skelittle: A Giant Party!",,playable,2021-06-09 19:08:34
@@ -3307,6 +3310,7 @@
0100AFA011068000,"Voxel Pirates",,playable,2022-09-28 22:55:02
0100BFB00D1F4000,"Voxel Sword",,playable,2022-08-30 14:57:27
01004E90028A2000,"Vroom in the night sky",Needs Update;vulkan-backend-bug,playable,2023-02-20 02:32:29
+0100BFC01D976000,"Virtual Boy – Nintendo Classics",services,nothing,2026-02-17 11:26:59
0100C7C00AE6C000,"VSR: Void Space Racing",,playable,2021-01-27 14:08:59
0100B130119D0000,"Waifu Uncovered",crash,ingame,2023-02-27 01:17:46
0100E29010A4A000,"Wanba Warriors",,playable,2020-10-04 17:56:22
diff --git a/src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs b/src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs
index a97e7b409..24a318be4 100644
--- a/src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs
+++ b/src/Ryujinx.BuildValidationTasks/LocalesValidationTask.cs
@@ -107,12 +107,12 @@ namespace Ryujinx.BuildValidationTasks
{
locale.Translations[langCode] = string.Empty;
Console.WriteLine(
- $"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
+ $"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
}
else
{
Console.WriteLine(
- $"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
+ $"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
}
}
diff --git a/src/Ryujinx.Common/Ryujinx.Common.csproj b/src/Ryujinx.Common/Ryujinx.Common.csproj
index e31d2f3bc..60a75a7b2 100644
--- a/src/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/src/Ryujinx.Common/Ryujinx.Common.csproj
@@ -10,7 +10,6 @@
-
diff --git a/src/Ryujinx.Common/TitleIDs.cs b/src/Ryujinx.Common/TitleIDs.cs
index f77a2858f..c232cfd01 100644
--- a/src/Ryujinx.Common/TitleIDs.cs
+++ b/src/Ryujinx.Common/TitleIDs.cs
@@ -184,6 +184,7 @@ namespace Ryujinx.Common
"01001b300b9be000", // Diablo III: Eternal Collection
"010027400cdc6000", // Divinity Original 2 - Definitive Edition
"01008c8012920000", // Dying Light Platinum Edition
+ "0100d11013e6a000", // Eschatos
"01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2
"0100f7e00c70e000", // Hogwarts Legacy
@@ -193,9 +194,15 @@ namespace Ryujinx.Common
"0100d71004694000", // Minecraft
"01007430037f6000", // Monopoly
"0100853015e86000", // No Man's Sky
+ "0100f85014ed0000", // No More Heroes
+ "0100463014ed4000", // No More Heroes 2
+ "0100e570094e8000", // Owlboy
"01007bb017812000", // Portal
"0100abd01785c000", // Portal 2
+ "01009f100bc52000", // Psikyo Collection 1
+ "01009d400c4a8000", // Psikyo Collection 2
"01008e200c5c2000", // Muse Dash
+ "01005ff002e2a000", // Rayman Legends
"01007820196a6000", // Red Dead Redemption
"0100e8300a67a000", // Risk
"01002f7013224000", // Rune Factory 5
diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs
index 83aaa1f4d..38d504bbf 100644
--- a/src/Ryujinx.HLE/HOS/Horizon.cs
+++ b/src/Ryujinx.HLE/HOS/Horizon.cs
@@ -20,6 +20,7 @@ using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
+using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
@@ -66,6 +67,8 @@ namespace Ryujinx.HLE.HOS
internal List NfpDevices { get; private set; }
+ internal List NfcDevices { get; private set; }
+
internal SmRegistry SmRegistry { get; private set; }
internal ServerBase SmServer { get; private set; }
@@ -132,6 +135,7 @@ namespace Ryujinx.HLE.HOS
PerformanceState = new PerformanceState();
NfpDevices = [];
+ NfcDevices = [];
// Note: This is not really correct, but with HLE of services, the only memory
// region used that is used is Application, so we can use the other ones for anything.
@@ -372,6 +376,15 @@ namespace Ryujinx.HLE.HOS
}
}
+ public void ScanSkylander(int nfcDeviceId, byte[] data)
+ {
+ if (NfcDevices[nfcDeviceId].State == NfcDeviceState.SearchingForTag)
+ {
+ NfcDevices[nfcDeviceId].State = NfcDeviceState.TagFound;
+ NfcDevices[nfcDeviceId].Data = data;
+ }
+ }
+
public bool SearchingForAmiibo(out int nfpDeviceId)
{
nfpDeviceId = default;
@@ -389,6 +402,53 @@ namespace Ryujinx.HLE.HOS
return false;
}
+ public bool SearchingForSkylander(out int nfcDeviceId)
+ {
+ nfcDeviceId = default;
+
+ for (int i = 0; i < NfcDevices.Count; i++)
+ {
+ if (NfcDevices[i].State == NfcDeviceState.SearchingForTag)
+ {
+ nfcDeviceId = i;
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool HasSkylander(out int nfcDeviceId)
+ {
+ nfcDeviceId = default;
+
+ for (int i = 0; i < NfcDevices.Count; i++)
+ {
+ if (NfcDevices[i].State == NfcDeviceState.TagFound)
+ {
+ nfcDeviceId = i;
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void RemoveSkylander()
+ {
+ for (int i = 0; i < NfcDevices.Count; i++)
+ {
+ if (NfcDevices[i].State == NfcDeviceState.TagFound)
+ {
+ NfcDevices[i].State = NfcDeviceState.Initialized;
+ NfcDevices[i].SignalDeactivate();
+ Thread.Sleep(100); // NOTE: Simulate skylander scanning delay.
+ }
+ }
+ }
+
public void SignalDisplayResolutionChange()
{
DisplayResolutionChangeEvent.ReadableEvent.Signal();
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs
index 295c7e71a..53b6549c5 100644
--- a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/IUserManager.cs
@@ -1,8 +1,19 @@
+using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
+
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
{
[Service("nfc:mf:u")]
class IUserManager : IpcService
{
public IUserManager(ServiceCtx context) { }
+
+ [CommandCmif(0)]
+ // CreateUserInterface() -> object
+ public ResultCode CreateUserInterface(ServiceCtx context)
+ {
+ MakeObject(context, new IMifare());
+
+ return ResultCode.Success;
+ }
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
new file mode 100644
index 000000000..43e28b5cf
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
@@ -0,0 +1,477 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Hid;
+using Ryujinx.HLE.HOS.Services.Hid.HidServer;
+using Ryujinx.Horizon.Common;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ class IMifare : IpcService
+ {
+ private State _state;
+
+ private KEvent _availabilityChangeEvent;
+
+ private CancellationTokenSource _cancelTokenSource;
+
+ public IMifare()
+ {
+ _state = State.NonInitialized;
+ }
+
+ [CommandCmif(0)]
+ public ResultCode Initialize(ServiceCtx context)
+ {
+ _state = State.Initialized;
+
+ NfcDevice devicePlayer1 = new()
+ {
+ NpadIdType = NpadIdType.Player1,
+ Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
+ State = NfcDeviceState.Initialized,
+ };
+
+ context.Device.System.NfcDevices.Add(devicePlayer1);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(1)]
+ public ResultCode Finalize(ServiceCtx context)
+ {
+ if (_state == State.Initialized)
+ {
+ _cancelTokenSource?.Cancel();
+
+ // NOTE: All events are destroyed here.
+ context.Device.System.NfcDevices.Clear();
+
+ _state = State.NonInitialized;
+ }
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(2)]
+ public ResultCode GetListDevices(ServiceCtx context)
+ {
+ if (context.Request.RecvListBuff.Count == 0)
+ {
+ return ResultCode.WrongArgument;
+ }
+
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
+ ulong outputSize = context.Request.RecvListBuff[0].Size;
+
+ if (context.Device.System.NfcDevices.Count == 0)
+ {
+ return ResultCode.DeviceNotFound;
+ }
+
+ MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfcDevices[i].Handle);
+ }
+
+ context.ResponseData.Write(context.Device.System.NfcDevices.Count);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(3)]
+ public ResultCode StartDetection(ServiceCtx context)
+ {
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
+ {
+ context.Device.System.NfcDevices[i].State = NfcDeviceState.SearchingForTag;
+
+ break;
+ }
+ }
+
+ _cancelTokenSource = new CancellationTokenSource();
+
+ Task.Run(() =>
+ {
+ while (true)
+ {
+ if (_cancelTokenSource.Token.IsCancellationRequested)
+ {
+ break;
+ }
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
+ {
+ context.Device.System.NfcDevices[i].SignalActivate();
+ Thread.Sleep(125); // NOTE: Simulate skylander scanning delay.
+
+ break;
+ }
+ }
+ }
+ }, _cancelTokenSource.Token);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(4)]
+ public ResultCode StopDetection(ServiceCtx context)
+ {
+ _cancelTokenSource?.Cancel();
+
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
+ {
+ context.Device.System.NfcDevices[i].State = NfcDeviceState.Initialized;
+ Array.Clear(context.Device.System.NfcDevices[i].Data);
+ context.Device.System.NfcDevices[i].SignalDeactivate();
+
+ break;
+ }
+ }
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(5)]
+ public ResultCode ReadMifare(ServiceCtx context)
+ {
+ if (context.Request.ReceiveBuff.Count == 0 || context.Request.SendBuff.Count == 0)
+ {
+ return ResultCode.WrongArgument;
+ }
+
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ if (context.Device.System.NfcDevices.Count == 0)
+ {
+ return ResultCode.DeviceNotFound;
+ }
+
+ ulong outputPosition = context.Request.ReceiveBuff[0].Position;
+ ulong outputSize = context.Request.ReceiveBuff[0].Size;
+
+ MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
+
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
+
+ byte[] readBlockParameter = new byte[inputSize];
+
+ context.Memory.Read(inputPosition, readBlockParameter);
+
+ var span = MemoryMarshal.Cast(readBlockParameter);
+ var list = new List(span.Length);
+
+ foreach (var item in span)
+ list.Add(item);
+
+ Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
+ {
+ if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
+ {
+ return ResultCode.TagNotFound;
+ }
+ else
+ {
+ for (int p = 0; p < list.Count; p++)
+ {
+ NfcMifareReadBlockData blockData = new()
+ {
+ SectorNumber = list[p].SectorNumber,
+ Reserved = new Array7(),
+ };
+ byte[] data = new byte[16];
+
+ switch (list[p].SectorKey.MifareCommand)
+ {
+ case NfcMifareCommand.NfcMifareCommand_Read:
+ case NfcMifareCommand.NfcMifareCommand_AuthA:
+ if (IsCurrentBlockKeyBlock(list[p].SectorNumber))
+ {
+ Array.Copy(context.Device.System.NfcDevices[i].Data, (16 * list[p].SectorNumber) + 6, data, 6, 4);
+ }
+ else
+ {
+ Array.Copy(context.Device.System.NfcDevices[i].Data, 16 * list[p].SectorNumber, data, 0, 16);
+ }
+ data.CopyTo(blockData.Data.AsSpan());
+ context.Memory.Write(outputPosition + ((uint)(p * Unsafe.SizeOf())), blockData);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(6)]
+ public ResultCode WriteMifare(ServiceCtx context)
+ {
+ if (context.Request.SendBuff.Count == 0)
+ {
+ return ResultCode.WrongArgument;
+ }
+
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ if (context.Device.System.NfcDevices.Count == 0)
+ {
+ return ResultCode.DeviceNotFound;
+ }
+
+ ulong inputPosition = context.Request.SendBuff[0].Position;
+ ulong inputSize = context.Request.SendBuff[0].Size;
+
+ byte[] writeBlockParameter = new byte[inputSize];
+
+ context.Memory.Read(inputPosition, writeBlockParameter);
+
+ var span = MemoryMarshal.Cast(writeBlockParameter);
+ var list = new List(span.Length);
+
+ foreach (var item in span)
+ list.Add(item);
+
+ Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
+ {
+ if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
+ {
+ return ResultCode.TagNotFound;
+ }
+ else
+ {
+ for (int p = 0; p < list.Count; p++)
+ {
+ switch (list[p].SectorKey.MifareCommand)
+ {
+ case NfcMifareCommand.NfcMifareCommand_Write:
+ case NfcMifareCommand.NfcMifareCommand_AuthA:
+ list[p].Data.AsSpan().CopyTo(context.Device.System.NfcDevices[i].Data.AsSpan(list[p].SectorNumber * 16, 16));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(7)]
+ public ResultCode GetTagInfo(ServiceCtx context)
+ {
+ ResultCode resultCode = ResultCode.Success;
+
+ if (context.Request.RecvListBuff.Count == 0)
+ {
+ return ResultCode.WrongArgument;
+ }
+
+ ulong outputPosition = context.Request.RecvListBuff[0].Position;
+
+ context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf());
+
+ MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf());
+
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ if (context.Device.System.NfcDevices.Count == 0)
+ {
+ return ResultCode.DeviceNotFound;
+ }
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
+ {
+ if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
+ {
+ resultCode = ResultCode.TagNotFound;
+ }
+ else
+ {
+ if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagMounted || context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
+ {
+ TagInfo tagInfo = new()
+ {
+ UuidLength = 4,
+ Reserved1 = new Array21(),
+ Protocol = (uint)NfcProtocol.NfcProtocol_TypeA, // Type A Protocol
+ TagType = (uint)NfcTagType.NfcTagType_Mifare, // Mifare Type
+ Reserved2 = new Array6(),
+ };
+
+ byte[] uuid = new byte[4];
+
+ Array.Copy(context.Device.System.NfcDevices[i].Data, 0, uuid, 0, 4);
+
+ uuid.CopyTo(tagInfo.Uuid.AsSpan());
+
+ context.Memory.Write(outputPosition, tagInfo);
+
+ resultCode = ResultCode.Success;
+ }
+ else
+ {
+ resultCode = ResultCode.WrongDeviceState;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return resultCode;
+ }
+
+ [CommandCmif(8)]
+ public ResultCode AttachActivateEvent(ServiceCtx context)
+ {
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
+ {
+ context.Device.System.NfcDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext);
+
+ if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != Result.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle);
+
+ return ResultCode.Success;
+ }
+ }
+
+ return ResultCode.DeviceNotFound;
+ }
+
+ [CommandCmif(9)]
+ public ResultCode AttachDeactivateEvent(ServiceCtx context)
+ {
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
+ {
+ context.Device.System.NfcDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext);
+
+ if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != Result.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle);
+
+ return ResultCode.Success;
+ }
+ }
+
+ return ResultCode.DeviceNotFound;
+ }
+
+ [CommandCmif(10)]
+ public ResultCode GetState(ServiceCtx context)
+ {
+ context.ResponseData.Write((int)_state);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(11)]
+ public ResultCode GetDeviceState(ServiceCtx context)
+ {
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
+ {
+ if (context.Device.System.NfcDevices[i].State > NfcDeviceState.Finalized)
+ {
+ throw new InvalidOperationException($"{nameof(context.Device.System.NfcDevices)} contains an invalid state for device {i}: {context.Device.System.NfcDevices[i].State}");
+ }
+ context.ResponseData.Write((uint)context.Device.System.NfcDevices[i].State);
+
+ return ResultCode.Success;
+ }
+ }
+
+ context.ResponseData.Write((uint)NfcDeviceState.Unavailable);
+
+ return ResultCode.DeviceNotFound;
+ }
+
+ [CommandCmif(12)]
+ public ResultCode GetNpadId(ServiceCtx context)
+ {
+ uint deviceHandle = (uint)context.RequestData.ReadUInt64();
+
+ for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
+ {
+ if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
+ {
+ context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfcDevices[i].Handle));
+
+ return ResultCode.Success;
+ }
+ }
+
+ return ResultCode.DeviceNotFound;
+ }
+
+ [CommandCmif(13)]
+ public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context)
+ {
+ _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext);
+
+ if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != Result.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle);
+
+ return ResultCode.Success;
+ }
+
+ private bool IsCurrentBlockKeyBlock(byte block)
+ {
+ return ((block + 1) % 4) == 0;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcDevice.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcDevice.cs
new file mode 100644
index 000000000..5d2b97fe9
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcDevice.cs
@@ -0,0 +1,21 @@
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Hid;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ class NfcDevice
+ {
+ public KEvent ActivateEvent;
+ public KEvent DeactivateEvent;
+
+ public void SignalActivate() => ActivateEvent.ReadableEvent.Signal();
+ public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal();
+
+ public NfcDeviceState State = NfcDeviceState.Unavailable;
+
+ public PlayerIndex Handle;
+ public NpadIdType NpadIdType;
+
+ public byte[] Data;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareCommand.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareCommand.cs
new file mode 100644
index 000000000..0ab18c183
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareCommand.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ enum NfcMifareCommand : byte
+ {
+ NfcMifareCommand_Read = 0x30,
+ NfcMifareCommand_AuthA = 0x60,
+ NfcMifareCommand_AuthB = 0x61,
+ NfcMifareCommand_Write = 0xA0,
+ NfcMifareCommand_Transfer = 0xB0,
+ NfcMifareCommand_Decrement = 0xC0,
+ NfcMifareCommand_Increment = 0xC1,
+ NfcMifareCommand_Store = 0xC2,
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockData.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockData.cs
new file mode 100644
index 000000000..bc5f1dcf3
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockData.cs
@@ -0,0 +1,13 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x18)]
+ struct NfcMifareReadBlockData
+ {
+ public Array16 Data;
+ public byte SectorNumber;
+ public Array7 Reserved;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockParameter.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockParameter.cs
new file mode 100644
index 000000000..df4ed6fce
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareReadBlockParameter.cs
@@ -0,0 +1,13 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x18)]
+ struct NfcMifareReadBlockParameter
+ {
+ public byte SectorNumber;
+ public Array7 Reserved;
+ public NfcSectorKey SectorKey;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareWriteBlockParameter.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareWriteBlockParameter.cs
new file mode 100644
index 000000000..fcdbfab2d
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcMifareWriteBlockParameter.cs
@@ -0,0 +1,14 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x28)]
+ struct NfcMifareWriteBlockParameter
+ {
+ public Array16 Data;
+ public byte SectorNumber;
+ public Array7 Reserved;
+ public NfcSectorKey SectorKey;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcProtocol.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcProtocol.cs
new file mode 100644
index 000000000..d486ddade
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcProtocol.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ enum NfcProtocol : byte
+ {
+ NfcProtocol_None = 0b_0000_0000,
+ NfcProtocol_TypeA = 0b_0000_0001, ///< ISO14443A
+ NfcProtocol_TypeB = 0b_0000_0010, ///< ISO14443B
+ NfcProtocol_TypeF = 0b_0000_0100, ///< Sony FeliCa
+ NfcProtocol_All = 0xFF,
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcSectorKey.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcSectorKey.cs
new file mode 100644
index 000000000..bd7d51813
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcSectorKey.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x10)]
+ struct NfcSectorKey
+ {
+ public NfcMifareCommand MifareCommand;
+ public byte Unknown;
+ public Array6 Reserved1;
+ public Array6 SectorKey;
+ public Array2 Reserved2;
+
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcTagType.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcTagType.cs
new file mode 100644
index 000000000..b21c4c9d0
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfcTagType.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ enum NfcTagType : byte
+ {
+ NfcTagType_None = 0b_0000_0000,
+ NfcTagType_Type1 = 0b_0000_0001, ///< ISO14443A RW. Topaz
+ NfcTagType_Type2 = 0b_0000_0010, ///< ISO14443A RW. Ultralight, NTAGX, ST25TN
+ NfcTagType_Type3 = 0b_0000_0100, ///< ISO14443A RW/RO. Sony FeliCa
+ NfcTagType_Type4A = 0b_0000_1000, ///< ISO14443A RW/RO. DESFire
+ NfcTagType_Type4B = 0b_0001_0000, ///< ISO14443B RW/RO. DESFire
+ NfcTagType_Type5 = 0b_0010_0000, ///< ISO15693 RW/RO. SLI, SLIX, ST25TV
+ NfcTagType_Mifare = 0b_0100_0000, ///< Mifare clasic. Skylanders
+ NfcTagType_All = 0xFF,
+ }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfpDeviceState.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfpDeviceState.cs
new file mode 100644
index 000000000..9e51b8d4d
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/NfpDeviceState.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ enum NfcDeviceState : byte
+ {
+ Initialized = 0,
+ SearchingForTag = 1,
+ TagFound = 2,
+ TagRemoved = 3,
+ TagMounted = 4,
+ Unavailable = 5,
+ Finalized = 6,
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/State.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/State.cs
new file mode 100644
index 000000000..a9ee720e9
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/State.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ enum State
+ {
+ NonInitialized,
+ Initialized,
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/TagInfo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/TagInfo.cs
new file mode 100644
index 000000000..5db4612c9
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/Types/TagInfo.cs
@@ -0,0 +1,16 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x58)]
+ struct TagInfo
+ {
+ public Array10 Uuid;
+ public byte UuidLength;
+ public Array21 Reserved1;
+ public uint Protocol;
+ public uint TagType;
+ public Array6 Reserved2;
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
new file mode 100644
index 000000000..3148e02e4
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
+{
+ public enum ResultCode
+ {
+ ModuleId = 161,
+ ErrorCodeShift = 9,
+
+ Success = 0,
+
+ DeviceNotFound = (64 << ErrorCodeShift) | ModuleId, // 0x80A1
+ WrongArgument = (65 << ErrorCodeShift) | ModuleId, // 0x82A1
+ WrongDeviceState = (73 << ErrorCodeShift) | ModuleId, // 0x92A1
+ NfcDisabled = (80 << ErrorCodeShift) | ModuleId, // 0xA0A1
+ TagNotFound = (97 << ErrorCodeShift) | ModuleId, // 0xC2A1
+ MifareAccessError = (288 << ErrorCodeShift) | ModuleId, // 0x240a1
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs
index f5b7fc0f1..8e7a44005 100644
--- a/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs
+++ b/src/Ryujinx.HLE/HOS/SystemState/SystemLanguage.cs
@@ -20,5 +20,7 @@ namespace Ryujinx.HLE.HOS.SystemState
SimplifiedChinese,
TraditionalChinese,
BrazilianPortuguese,
+ Polish,
+ Thai,
}
}
diff --git a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
index 91277232c..74378b153 100644
--- a/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
+++ b/src/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs
@@ -23,7 +23,9 @@ namespace Ryujinx.HLE.HOS.SystemState
"es-419",
"zh-Hans",
"zh-Hant",
- "pt-BR"
+ "pt-BR",
+ "pl",
+ "th"
];
internal long DesiredKeyboardLayout { get; private set; }
diff --git a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs
index e3bfb9165..e651410ce 100644
--- a/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs
+++ b/src/Ryujinx.HLE/HOS/SystemState/TitleLanguage.cs
@@ -18,5 +18,7 @@ namespace Ryujinx.HLE.HOS.SystemState
TraditionalChinese,
SimplifiedChinese,
BrazilianPortuguese,
+ Polish,
+ Thai,
}
}
diff --git a/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs b/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs
index 7640967c6..e6a65feef 100644
--- a/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs
+++ b/src/Ryujinx.Horizon/Sdk/Ns/ApplicationControlProperty.cs
@@ -1,12 +1,19 @@
using Ryujinx.Common.Memory;
using System;
+using System.Buffers.Binary;
+using System.IO;
+using System.IO.Compression;
+using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.Horizon.Sdk.Ns
{
public struct ApplicationControlProperty
{
- public Array16 Title;
+ ///
+ /// Use to access titles instead of accessing them directly.
+ ///
+ public Array16 TitleBlock;
public Array37 Isbn;
public StartupUserAccountValue StartupUserAccount;
public UserAccountSwitchLockValue UserAccountSwitchLock;
@@ -58,7 +65,10 @@ namespace Ryujinx.Horizon.Sdk.Ns
public RepairFlagValue RepairFlag;
public byte ProgramIndex;
public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag;
- public Array4 Reserved3214;
+ public byte ApplicationErrorCodePrefix;
+ public TitleCompressionValue TitleCompression;
+ public byte AcdIndex;
+ public byte ApparentPlatform;
public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration;
public ApplicationJitConfiguration JitConfiguration;
public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors;
@@ -74,6 +84,47 @@ namespace Ryujinx.Horizon.Sdk.Ns
public readonly string DisplayVersionString => Encoding.UTF8.GetString(DisplayVersion.AsSpan()).TrimEnd('\0');
public readonly string ApplicationErrorCodeCategoryString => Encoding.UTF8.GetString(ApplicationErrorCodeCategory.AsSpan()).TrimEnd('\0');
public readonly string BcatPassphraseString => Encoding.UTF8.GetString(BcatPassphrase.AsSpan()).TrimEnd('\0');
+
+ private const int TitleCount = 32;
+ private const int TitleEntrySize = 0x300;
+
+
+ ///
+ /// Returns the resolved title entries. When is
+ /// , the raw bytes are
+ /// decompressed (raw deflate) from 0x3000 into 0x6000 bytes yielding up to 32 entries.
+ /// Otherwise the 16 uncompressed entries from are returned directly.
+ ///
+ public readonly ApplicationTitle[] Title
+ {
+ get
+ {
+ var titles = new ApplicationTitle[TitleCount];
+
+ if (TitleCompression != TitleCompressionValue.Enable)
+ {
+ TitleBlock.AsSpan().CopyTo(titles);
+
+ return titles;
+ }
+
+ ReadOnlySpan titleBytes = MemoryMarshal.AsBytes(TitleBlock.AsSpan());
+ ushort compressedBlobSize = BinaryPrimitives.ReadUInt16LittleEndian(titleBytes);
+ ReadOnlySpan compressedBlob = titleBytes.Slice(2, compressedBlobSize);
+
+ byte[] decompressed = new byte[TitleCount * TitleEntrySize];
+
+ using (var compressedStream = new MemoryStream(compressedBlob.ToArray()))
+ using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
+ {
+ deflateStream.ReadExactly(decompressed, 0, decompressed.Length);
+ }
+
+ MemoryMarshal.Cast(decompressed).CopyTo(titles);
+
+ return titles;
+ }
+ }
public struct ApplicationTitle
{
@@ -130,6 +181,8 @@ namespace Ryujinx.Horizon.Sdk.Ns
TraditionalChinese = 13,
SimplifiedChinese = 14,
BrazilianPortuguese = 15,
+ Polish = 16,
+ Thai = 17,
}
public enum Organization
@@ -302,5 +355,11 @@ namespace Ryujinx.Horizon.Sdk.Ns
Deny = 0,
Allow = 1,
}
+
+ public enum TitleCompressionValue : byte
+ {
+ Disable = 0,
+ Enable = 1,
+ }
}
}
diff --git a/src/Ryujinx/Input/AvaloniaKeyboardMappingHelper.cs b/src/Ryujinx/Input/AvaloniaKeyboardMappingHelper.cs
index 48e49a7fa..4aa8692dd 100644
--- a/src/Ryujinx/Input/AvaloniaKeyboardMappingHelper.cs
+++ b/src/Ryujinx/Input/AvaloniaKeyboardMappingHelper.cs
@@ -141,7 +141,7 @@ namespace Ryujinx.Ava.Input
AvaKey.OemComma,
AvaKey.OemPeriod,
AvaKey.OemQuestion,
- AvaKey.OemBackslash,
+ AvaKey.OemPipe,
// NOTE: invalid
AvaKey.None
diff --git a/src/Ryujinx/Ryujinx.csproj b/src/Ryujinx/Ryujinx.csproj
index 5da152501..715460274 100644
--- a/src/Ryujinx/Ryujinx.csproj
+++ b/src/Ryujinx/Ryujinx.csproj
@@ -28,11 +28,6 @@
true
partial
-
-
- true
- false
-