From 55e0c714898cc331173628714bdc8e64fb9a3756 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 24 Jun 2021 01:05:40 +0200 Subject: nfc/nfp: Implement ISystemManager and ISystem (#2381) * nfc/nfp: Implement ISystemManager and ISystem This PR add permission levels for `nfc` and `nfp` services: - `nfc`: `CreateUserInterface` and `CreateSystemInterface` are implemented. - `INfc`: `Initialize` and `IsNfcEnabled` calls are stubbed. - `nfp`: `CreateDebugInterface` and `CreateSystemInterface` are implemented. - `INfp`: `GetRegisterInfo2` for `IDebug` and `ISystem` are implemented. * Addresses gdkchan feedback --- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.HLE/HOS/Horizon.cs | 2 +- Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs | 13 +- Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs | 13 +- Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs | 37 + .../Nfc/NfcManager/Types/NfcPermissionLevel.cs | 8 + Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs | 13 +- Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs | 13 +- Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs | 8 +- .../HOS/Services/Nfc/Nfp/NfpManager/INfp.cs | 997 +++++++++++++++++++++ .../Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs | 8 + .../Nfc/Nfp/NfpManager/Types/CommonInfo.cs | 17 + .../Nfc/Nfp/NfpManager/Types/DeviceType.cs | 7 + .../Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs | 16 + .../Nfc/Nfp/NfpManager/Types/MountTarget.cs | 9 + .../Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs | 23 + .../Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs | 13 + .../Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs | 9 + .../Nfc/Nfp/NfpManager/Types/RegisterInfo.cs | 19 + .../HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs | 8 + .../Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs | 16 + .../Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs | 22 + .../HOS/Services/Nfc/Nfp/UserManager/IUser.cs | 978 -------------------- .../Nfc/Nfp/UserManager/Types/AmiiboConstants.cs | 8 - .../Nfc/Nfp/UserManager/Types/CommonInfo.cs | 17 - .../Nfc/Nfp/UserManager/Types/DeviceType.cs | 7 - .../Nfc/Nfp/UserManager/Types/ModelInfo.cs | 16 - .../Nfc/Nfp/UserManager/Types/MountTarget.cs | 9 - .../Nfc/Nfp/UserManager/Types/NfpDevice.cs | 23 - .../Nfc/Nfp/UserManager/Types/NfpDeviceState.cs | 13 - .../Nfc/Nfp/UserManager/Types/RegisterInfo.cs | 19 - .../Services/Nfc/Nfp/UserManager/Types/State.cs | 8 - .../Services/Nfc/Nfp/UserManager/Types/TagInfo.cs | 16 - .../Nfc/Nfp/UserManager/Types/VirtualAmiiboFile.cs | 22 - Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs | 2 +- 35 files changed, 1265 insertions(+), 1145 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/AmiiboConstants.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/CommonInfo.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/DeviceType.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/ModelInfo.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/MountTarget.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDevice.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDeviceState.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/RegisterInfo.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/State.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/TagInfo.cs delete mode 100644 Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/VirtualAmiiboFile.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 28b344cd..c2d2f55e 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Common.Logging ServiceLdr, ServiceLm, ServiceMm, + ServiceNfc, ServiceNfp, ServiceNgct, ServiceNifm, diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index b8343cd1..3034d107 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -22,7 +22,7 @@ using Ryujinx.HLE.HOS.Services.Arp; using Ryujinx.HLE.HOS.Services.Audio.AudioRenderer; using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.Mii; -using Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Pcv.Bpc; diff --git a/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs index 0bab0b79..9072ed83 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/ISystemManager.cs @@ -1,8 +1,19 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc +using Ryujinx.HLE.HOS.Services.Nfc.NfcManager; + +namespace Ryujinx.HLE.HOS.Services.Nfc { [Service("nfc:sys")] class ISystemManager : IpcService { public ISystemManager(ServiceCtx context) { } + + [CommandHipc(0)] + // CreateSystemInterface() -> object + public ResultCode CreateSystemInterface(ServiceCtx context) + { + MakeObject(context, new INfc(NfcPermissionLevel.System)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs index 048adf8c..5f0ccf14 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/IUserManager.cs @@ -1,8 +1,19 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc +using Ryujinx.HLE.HOS.Services.Nfc.NfcManager; + +namespace Ryujinx.HLE.HOS.Services.Nfc { [Service("nfc:user")] class IUserManager : IpcService { public IUserManager(ServiceCtx context) { } + + [CommandHipc(0)] + // CreateUserInterface() -> object + public ResultCode CreateUserInterface(ServiceCtx context) + { + MakeObject(context, new INfc(NfcPermissionLevel.User)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs new file mode 100644 index 00000000..f2fc867d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/INfc.cs @@ -0,0 +1,37 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager +{ + class INfc : IpcService + { + private NfcPermissionLevel _permissionLevel; + + public INfc(NfcPermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CommandHipc(0)] + [CommandHipc(400)] // 4.0.0+ + // Initialize() + public ResultCode Initialize(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel }); + + return ResultCode.Success; + } + + [CommandHipc(3)] + [CommandHipc(403)] // 4.0.0+ + // IsNfcEnabled() -> b8 + public ResultCode IsNfcEnabled(ServiceCtx context) + { + // NOTE: Write false value here could make nfp service not called. + context.ResponseData.Write(true); + + Logger.Stub?.PrintStub(LogClass.ServiceNfc, new { _permissionLevel }); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs new file mode 100644 index 00000000..39babc73 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/NfcManager/Types/NfcPermissionLevel.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.NfcManager +{ + enum NfcPermissionLevel + { + User, + System + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs index c5da8da9..2faf489c 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IDebugManager.cs @@ -1,8 +1,19 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { [Service("nfp:dbg")] class IAmManager : IpcService { public IAmManager(ServiceCtx context) { } + + [CommandHipc(0)] + // CreateDebugInterface() -> object + public ResultCode CreateDebugInterface(ServiceCtx context) + { + MakeObject(context, new INfp(NfpPermissionLevel.Debug)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs index 78ea4896..ed4c7bbf 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/ISystemManager.cs @@ -1,8 +1,19 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { [Service("nfp:sys")] class ISystemManager : IpcService { public ISystemManager(ServiceCtx context) { } + + [CommandHipc(0)] + // CreateSystemInterface() -> object + public ResultCode CreateSystemInterface(ServiceCtx context) + { + MakeObject(context, new INfp(NfpPermissionLevel.System)); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs index 5f70758b..09df720a 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/IUserManager.cs @@ -1,4 +1,6 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { [Service("nfp:user")] class IUserManager : IpcService @@ -7,9 +9,9 @@ [CommandHipc(0)] // CreateUserInterface() -> object - public ResultCode GetUserInterface(ServiceCtx context) + public ResultCode CreateUserInterface(ServiceCtx context) { - MakeObject(context, new IUser()); + MakeObject(context, new INfp(NfpPermissionLevel.User)); return ResultCode.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs new file mode 100644 index 00000000..6b4ea5ef --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -0,0 +1,997 @@ +using Ryujinx.Common.Memory; +using Ryujinx.Cpu; +using Ryujinx.HLE.Exceptions; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Hid.HidServer; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using System; +using System.Buffers.Binary; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp +{ + class INfp : IpcService + { + private ulong _appletResourceUserId; + private ulong _mcuVersionData; + private byte[] _mcuData; + + private State _state = State.NonInitialized; + + private KEvent _availabilityChangeEvent; + + private CancellationTokenSource _cancelTokenSource; + + private NfpPermissionLevel _permissionLevel; + + public INfp(NfpPermissionLevel permissionLevel) + { + _permissionLevel = permissionLevel; + } + + [CommandHipc(0)] + // Initialize(u64, u64, pid, buffer) + public ResultCode Initialize(ServiceCtx context) + { + _appletResourceUserId = context.RequestData.ReadUInt64(); + _mcuVersionData = context.RequestData.ReadUInt64(); + + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; + + _mcuData = new byte[inputSize]; + + context.Memory.Read(inputPosition, _mcuData); + + // TODO: The mcuData buffer seems to contains entries with a size of 0x40 bytes each. Usage of the data needs to be determined. + + // TODO: Handle this in a controller class directly. + // Every functions which use the Handle call nn::hid::system::GetXcdHandleForNpadWithNfc(). + NfpDevice devicePlayer1 = new NfpDevice + { + NpadIdType = NpadIdType.Player1, + Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), + State = NfpDeviceState.Initialized + }; + + context.Device.System.NfpDevices.Add(devicePlayer1); + + // TODO: It mounts 0x8000000000000020 save data and stores a random generate value inside. Usage of the data needs to be determined. + + _state = State.Initialized; + + return ResultCode.Success; + } + + [CommandHipc(1)] + // Finalize() + public ResultCode Finalize(ServiceCtx context) + { + if (_state == State.Initialized) + { + if (_cancelTokenSource != null) + { + _cancelTokenSource.Cancel(); + } + + // NOTE: All events are destroyed here. + context.Device.System.NfpDevices.Clear(); + + _state = State.NonInitialized; + } + + return ResultCode.Success; + } + + [CommandHipc(2)] + // ListDevices() -> (u32, buffer) + public ResultCode ListDevices(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.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); + + if (CheckNfcIsEnabled() == ResultCode.Success) + { + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfpDevices[i].Handle); + } + + context.ResponseData.Write(context.Device.System.NfpDevices.Count); + } + else + { + context.ResponseData.Write(0); + } + + return ResultCode.Success; + } + + [CommandHipc(3)] + // StartDetection(bytes<8, 4>) + public ResultCode StartDetection(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + context.Device.System.NfpDevices[i].State = NfpDeviceState.SearchingForTag; + + break; + } + } + + _cancelTokenSource = new CancellationTokenSource(); + + Task.Run(() => + { + while (true) + { + if (_cancelTokenSource.Token.IsCancellationRequested) + { + break; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) + { + context.Device.System.NfpDevices[i].SignalActivate(); + Thread.Sleep(125); // NOTE: Simulate amiibo scanning delay. + context.Device.System.NfpDevices[i].SignalDeactivate(); + + break; + } + } + } + }, _cancelTokenSource.Token); + + return ResultCode.Success; + } + + [CommandHipc(4)] + // StopDetection(bytes<8, 4>) + public ResultCode StopDetection(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + if (_cancelTokenSource != null) + { + _cancelTokenSource.Cancel(); + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + context.Device.System.NfpDevices[i].State = NfpDeviceState.Initialized; + + break; + } + } + + return ResultCode.Success; + } + + [CommandHipc(5)] + // Mount(bytes<8, 4>, u32, u32) + public ResultCode Mount(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + DeviceType deviceType = (DeviceType)context.RequestData.ReadUInt32(); + MountTarget mountTarget = (MountTarget)context.RequestData.ReadUInt32(); + + if (deviceType != 0) + { + return ResultCode.WrongArgument; + } + + if (((uint)mountTarget & 3) == 0) + { + return ResultCode.WrongArgument; + } + + // TODO: Found how the MountTarget is handled. + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) + { + // NOTE: This mount the amiibo data, which isn't needed in our case. + + context.Device.System.NfpDevices[i].State = NfpDeviceState.TagMounted; + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(6)] + // Unmount(bytes<8, 4>) + public ResultCode Unmount(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + // NOTE: This mount the amiibo data, which isn't needed in our case. + + context.Device.System.NfpDevices[i].State = NfpDeviceState.TagFound; + + resultCode = ResultCode.Success; + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(7)] + // OpenApplicationArea(bytes<8, 4>, u32) + public ResultCode OpenApplicationArea(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + uint applicationAreaId = context.RequestData.ReadUInt32(); + + bool isOpened = false; + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + isOpened = VirtualAmiibo.OpenApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + if (!isOpened) + { + resultCode = ResultCode.ApplicationAreaIsNull; + } + + return resultCode; + } + + [CommandHipc(8)] + // GetApplicationArea(bytes<8, 4>) -> (u32, buffer) + public ResultCode GetApplicationArea(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.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); + + uint size = 0; + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + byte[] applicationArea = VirtualAmiibo.GetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId); + + context.Memory.Write(outputPosition, applicationArea); + + size = (uint)applicationArea.Length; + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + } + } + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + if (size == 0) + { + return ResultCode.ApplicationAreaIsNull; + } + + context.ResponseData.Write(size); + + return ResultCode.Success; + } + + [CommandHipc(9)] + // SetApplicationArea(bytes<8, 4>, buffer) + public ResultCode SetApplicationArea(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; + + byte[] applicationArea = new byte[inputSize]; + + context.Memory.Read(inputPosition, applicationArea); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + VirtualAmiibo.SetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationArea); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(10)] + // Flush(bytes<8, 4>) + public ResultCode Flush(ServiceCtx context) + { + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + // NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case. + + return ResultCode.Success; + } + + [CommandHipc(11)] + // Restore(bytes<8, 4>) + public ResultCode Restore(ServiceCtx context) + { + throw new ServiceNotImplementedException(this, context, false); + } + + [CommandHipc(12)] + // CreateApplicationArea(bytes<8, 4>, u32, buffer) + public ResultCode CreateApplicationArea(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + uint applicationAreaId = context.RequestData.ReadUInt32(); + + ulong inputPosition = context.Request.SendBuff[0].Position; + ulong inputSize = context.Request.SendBuff[0].Size; + + byte[] applicationArea = new byte[inputSize]; + + context.Memory.Read(inputPosition, applicationArea); + + bool isCreated = false; + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + isCreated = VirtualAmiibo.CreateApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId, applicationArea); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + if (!isCreated) + { + resultCode = ResultCode.ApplicationAreaIsNull; + } + + return resultCode; + } + + [CommandHipc(13)] + // GetTagInfo(bytes<8, 4>) -> buffer, 0x1a> + public ResultCode GetTagInfo(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + 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(typeof(TagInfo))); + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo))); + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted || context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) + { + byte[] Uuid = VirtualAmiibo.GenerateUuid(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.NfpDevices[i].UseRandomUuid); + + if (Uuid.Length > AmiiboConstants.UuidMaxLength) + { + throw new ArgumentOutOfRangeException(); + } + + TagInfo tagInfo = new TagInfo + { + UuidLength = (byte)Uuid.Length, + Reserved1 = new Array21(), + Protocol = uint.MaxValue, // All Protocol + TagType = uint.MaxValue, // All Type + Reserved2 = new Array6() + }; + + Uuid.CopyTo(tagInfo.Uuid.ToSpan()); + + context.Memory.Write(outputPosition, tagInfo); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(14)] + // GetRegisterInfo(bytes<8, 4>) -> buffer, 0x1a> + public ResultCode GetRegisterInfo(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + 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(typeof(RegisterInfo))); + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo))); + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(context.Device.System.NfpDevices[i].AmiiboId); + + context.Memory.Write(outputPosition, registerInfo); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(15)] + // GetCommonInfo(bytes<8, 4>) -> buffer, 0x1a> + public ResultCode GetCommonInfo(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + 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(typeof(CommonInfo))); + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo))); + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId); + + context.Memory.Write(outputPosition, commonInfo); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(16)] + // GetModelInfo(bytes<8, 4>) -> buffer, 0x1a> + public ResultCode GetModelInfo(ServiceCtx context) + { + ResultCode resultCode = CheckNfcIsEnabled(); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + 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(typeof(ModelInfo))); + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo))); + + uint deviceHandle = (uint)context.RequestData.ReadUInt64(); + + if (context.Device.System.NfpDevices.Count == 0) + { + return ResultCode.DeviceNotFound; + } + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) + { + resultCode = ResultCode.TagNotFound; + } + else + { + if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) + { + ModelInfo modelInfo = new ModelInfo + { + Reserved = new Array57() + }; + + modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber)); + modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber); + modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber); + modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber); + modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber); + + context.Memory.Write(outputPosition, modelInfo); + + resultCode = ResultCode.Success; + } + else + { + resultCode = ResultCode.WrongDeviceState; + } + } + + break; + } + } + + return resultCode; + } + + [CommandHipc(17)] + // AttachActivateEvent(bytes<8, 4>) -> handle + public ResultCode AttachActivateEvent(ServiceCtx context) + { + uint deviceHandle = context.RequestData.ReadUInt32(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) + { + context.Device.System.NfpDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext); + + if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle); + + return ResultCode.Success; + } + } + + return ResultCode.DeviceNotFound; + } + + [CommandHipc(18)] + // AttachDeactivateEvent(bytes<8, 4>) -> handle + public ResultCode AttachDeactivateEvent(ServiceCtx context) + { + uint deviceHandle = context.RequestData.ReadUInt32(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) + { + context.Device.System.NfpDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext); + + if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle); + + return ResultCode.Success; + } + } + + return ResultCode.DeviceNotFound; + } + + [CommandHipc(19)] + // GetState() -> u32 + public ResultCode GetState(ServiceCtx context) + { + context.ResponseData.Write((int)_state); + + return ResultCode.Success; + } + + [CommandHipc(20)] + // GetDeviceState(bytes<8, 4>) -> u32 + public ResultCode GetDeviceState(ServiceCtx context) + { + uint deviceHandle = context.RequestData.ReadUInt32(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) + { + if (context.Device.System.NfpDevices[i].State > NfpDeviceState.Finalized) + { + throw new ArgumentOutOfRangeException(); + } + + context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State); + + return ResultCode.Success; + } + } + + context.ResponseData.Write((uint)NfpDeviceState.Unavailable); + + return ResultCode.DeviceNotFound; + } + + [CommandHipc(21)] + // GetNpadId(bytes<8, 4>) -> u32 + public ResultCode GetNpadId(ServiceCtx context) + { + uint deviceHandle = context.RequestData.ReadUInt32(); + + for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) + { + if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) + { + context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfpDevices[i].Handle)); + + return ResultCode.Success; + } + } + + return ResultCode.DeviceNotFound; + } + + [CommandHipc(22)] + // GetApplicationAreaSize() -> u32 + public ResultCode GetApplicationAreaSize(ServiceCtx context) + { + context.ResponseData.Write(AmiiboConstants.ApplicationAreaSize); + + return ResultCode.Success; + } + + [CommandHipc(23)] // 3.0.0+ + // AttachAvailabilityChangeEvent() -> handle + public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context) + { + _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext); + + if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle); + + return ResultCode.Success; + } + + [CommandHipc(24)] // 3.0.0+ + // RecreateApplicationArea(bytes<8, 4>, u32, buffer) + public ResultCode RecreateApplicationArea(ServiceCtx context) + { + throw new ServiceNotImplementedException(this, context, false); + } + + [CommandHipc(102)] + // GetRegisterInfo2(bytes<8, 4>) -> buffer, 0x1a> + public ResultCode GetRegisterInfo2(ServiceCtx context) + { + // TODO: Find the differencies between IUser and ISystem/IDebug. + + if (_permissionLevel == NfpPermissionLevel.Debug || _permissionLevel == NfpPermissionLevel.System) + { + return GetRegisterInfo(context); + } + + return ResultCode.DeviceNotFound; + } + + private ResultCode CheckNfcIsEnabled() + { + // TODO: Call nn::settings::detail::GetNfcEnableFlag when it will be implemented. + return true ? ResultCode.Success : ResultCode.NfcDisabled; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs new file mode 100644 index 00000000..b06492e6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/AmiiboConstants.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + static class AmiiboConstants + { + public const int UuidMaxLength = 10; + public const int ApplicationAreaSize = 0xD8; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs new file mode 100644 index 00000000..a7976de9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/CommonInfo.cs @@ -0,0 +1,17 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + [StructLayout(LayoutKind.Sequential, Size = 0x40)] + struct CommonInfo + { + public ushort LastWriteYear; + public byte LastWriteMonth; + public byte LastWriteDay; + public ushort WriteCounter; + public ushort Version; + public uint ApplicationAreaSize; + public Array52 Reserved; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs new file mode 100644 index 00000000..096522a0 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/DeviceType.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + enum DeviceType : uint + { + Amiibo + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs new file mode 100644 index 00000000..c66636ae --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/ModelInfo.cs @@ -0,0 +1,16 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + [StructLayout(LayoutKind.Sequential, Size = 0x40)] + struct ModelInfo + { + public ushort CharacterId; + public byte CharacterVariant; + public byte Series; + public ushort ModelNumber; + public byte Type; + public Array57 Reserved; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs new file mode 100644 index 00000000..4a145773 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/MountTarget.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + enum MountTarget : uint + { + Rom = 1, + Ram = 2, + All = 3 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs new file mode 100644 index 00000000..f56d33a9 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDevice.cs @@ -0,0 +1,23 @@ +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Hid; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + class NfpDevice + { + public KEvent ActivateEvent; + public KEvent DeactivateEvent; + + public void SignalActivate() => ActivateEvent.ReadableEvent.Signal(); + public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal(); + + public NfpDeviceState State = NfpDeviceState.Unavailable; + + public PlayerIndex Handle; + public NpadIdType NpadIdType; + + public string AmiiboId; + + public bool UseRandomUuid; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs new file mode 100644 index 00000000..51e1d060 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpDeviceState.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + enum NfpDeviceState + { + Initialized = 0, + SearchingForTag = 1, + TagFound = 2, + TagRemoved = 3, + TagMounted = 4, + Unavailable = 5, + Finalized = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs new file mode 100644 index 00000000..8b84dcfe --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/NfpPermissionLevel.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + enum NfpPermissionLevel + { + Debug, + User, + System + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs new file mode 100644 index 00000000..6b30eb8e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/RegisterInfo.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Mii.Types; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + [StructLayout(LayoutKind.Sequential, Size = 0x100)] + struct RegisterInfo + { + public CharInfo MiiCharInfo; + public ushort FirstWriteYear; + public byte FirstWriteMonth; + public byte FirstWriteDay; + public Array41 Nickname; + public byte FontRegion; + public Array64 Reserved1; + public Array58 Reserved2; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs new file mode 100644 index 00000000..b38cf9e2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/State.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + enum State + { + NonInitialized = 0, + Initialized = 1 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs new file mode 100644 index 00000000..d2076b2a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/TagInfo.cs @@ -0,0 +1,16 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + [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/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs new file mode 100644 index 00000000..be1877e5 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager +{ + struct VirtualAmiiboFile + { + public uint FileVersion { get; set; } + public byte[] TagUuid { get; set; } + public string AmiiboId { get; set; } + public DateTime FirstWriteDate { get; set; } + public DateTime LastWriteDate { get; set; } + public ushort WriteCounter { get; set; } + public List ApplicationAreas { get; set; } + } + + struct VirtualAmiiboApplicationArea + { + public uint ApplicationAreaId { get; set; } + public byte[] ApplicationArea { get; set; } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs deleted file mode 100644 index f241c311..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/IUser.cs +++ /dev/null @@ -1,978 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.Cpu; -using Ryujinx.HLE.Exceptions; -using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel.Common; -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Hid; -using Ryujinx.HLE.HOS.Services.Hid.HidServer; -using Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager; -using System; -using System.Buffers.Binary; -using System.Globalization; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp -{ - class IUser : IpcService - { - private ulong _appletResourceUserId; - private ulong _mcuVersionData; - private byte[] _mcuData; - - private State _state = State.NonInitialized; - - private KEvent _availabilityChangeEvent; - - private CancellationTokenSource _cancelTokenSource; - - public IUser() { } - - [CommandHipc(0)] - // Initialize(u64, u64, pid, buffer) - public ResultCode Initialize(ServiceCtx context) - { - _appletResourceUserId = context.RequestData.ReadUInt64(); - _mcuVersionData = context.RequestData.ReadUInt64(); - - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - _mcuData = new byte[inputSize]; - - context.Memory.Read(inputPosition, _mcuData); - - // TODO: The mcuData buffer seems to contains entries with a size of 0x40 bytes each. Usage of the data needs to be determined. - - // TODO: Handle this in a controller class directly. - // Every functions which use the Handle call nn::hid::system::GetXcdHandleForNpadWithNfc(). - NfpDevice devicePlayer1 = new NfpDevice - { - NpadIdType = NpadIdType.Player1, - Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), - State = NfpDeviceState.Initialized - }; - - context.Device.System.NfpDevices.Add(devicePlayer1); - - // TODO: It mounts 0x8000000000000020 save data and stores a random generate value inside. Usage of the data needs to be determined. - - _state = State.Initialized; - - return ResultCode.Success; - } - - [CommandHipc(1)] - // Finalize() - public ResultCode Finalize(ServiceCtx context) - { - if (_state == State.Initialized) - { - if (_cancelTokenSource != null) - { - _cancelTokenSource.Cancel(); - } - - // NOTE: All events are destroyed here. - context.Device.System.NfpDevices.Clear(); - - _state = State.NonInitialized; - } - - return ResultCode.Success; - } - - [CommandHipc(2)] - // ListDevices() -> (u32, buffer) - public ResultCode ListDevices(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.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - - if (CheckNfcIsEnabled() == ResultCode.Success) - { - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfpDevices[i].Handle); - } - - context.ResponseData.Write(context.Device.System.NfpDevices.Count); - } - else - { - context.ResponseData.Write(0); - } - - return ResultCode.Success; - } - - [CommandHipc(3)] - // StartDetection(bytes<8, 4>) - public ResultCode StartDetection(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - context.Device.System.NfpDevices[i].State = NfpDeviceState.SearchingForTag; - - break; - } - } - - _cancelTokenSource = new CancellationTokenSource(); - - Task.Run(() => - { - while (true) - { - if (_cancelTokenSource.Token.IsCancellationRequested) - { - break; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) - { - context.Device.System.NfpDevices[i].SignalActivate(); - Thread.Sleep(125); // NOTE: Simulate amiibo scanning delay. - context.Device.System.NfpDevices[i].SignalDeactivate(); - - break; - } - } - } - }, _cancelTokenSource.Token); - - return ResultCode.Success; - } - - [CommandHipc(4)] - // StopDetection(bytes<8, 4>) - public ResultCode StopDetection(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - if (_cancelTokenSource != null) - { - _cancelTokenSource.Cancel(); - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - context.Device.System.NfpDevices[i].State = NfpDeviceState.Initialized; - - break; - } - } - - return ResultCode.Success; - } - - [CommandHipc(5)] - // Mount(bytes<8, 4>, u32, u32) - public ResultCode Mount(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - UserManager.DeviceType deviceType = (UserManager.DeviceType)context.RequestData.ReadUInt32(); - MountTarget mountTarget = (MountTarget)context.RequestData.ReadUInt32(); - - if (deviceType != 0) - { - return ResultCode.WrongArgument; - } - - if (((uint)mountTarget & 3) == 0) - { - return ResultCode.WrongArgument; - } - - // TODO: Found how the MountTarget is handled. - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) - { - // NOTE: This mount the amiibo data, which isn't needed in our case. - - context.Device.System.NfpDevices[i].State = NfpDeviceState.TagMounted; - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(6)] - // Unmount(bytes<8, 4>) - public ResultCode Unmount(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - // NOTE: This mount the amiibo data, which isn't needed in our case. - - context.Device.System.NfpDevices[i].State = NfpDeviceState.TagFound; - - resultCode = ResultCode.Success; - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(7)] - // OpenApplicationArea(bytes<8, 4>, u32) - public ResultCode OpenApplicationArea(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - uint applicationAreaId = context.RequestData.ReadUInt32(); - - bool isOpened = false; - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - isOpened = VirtualAmiibo.OpenApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - if (!isOpened) - { - resultCode = ResultCode.ApplicationAreaIsNull; - } - - return resultCode; - } - - [CommandHipc(8)] - // GetApplicationArea(bytes<8, 4>) -> (u32, buffer) - public ResultCode GetApplicationArea(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.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); - - uint size = 0; - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - byte[] applicationArea = VirtualAmiibo.GetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId); - - context.Memory.Write(outputPosition, applicationArea); - - size = (uint)applicationArea.Length; - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - } - } - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - if (size == 0) - { - return ResultCode.ApplicationAreaIsNull; - } - - context.ResponseData.Write(size); - - return ResultCode.Success; - } - - [CommandHipc(9)] - // SetApplicationArea(bytes<8, 4>, buffer) - public ResultCode SetApplicationArea(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - byte[] applicationArea = new byte[inputSize]; - - context.Memory.Read(inputPosition, applicationArea); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - VirtualAmiibo.SetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationArea); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(10)] - // Flush(bytes<8, 4>) - public ResultCode Flush(ServiceCtx context) - { - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - // NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case. - - return ResultCode.Success; - } - - [CommandHipc(11)] - // Restore(bytes<8, 4>) - public ResultCode Restore(ServiceCtx context) - { - throw new ServiceNotImplementedException(this, context, false); - } - - [CommandHipc(12)] - // CreateApplicationArea(bytes<8, 4>, u32, buffer) - public ResultCode CreateApplicationArea(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - uint applicationAreaId = context.RequestData.ReadUInt32(); - - ulong inputPosition = context.Request.SendBuff[0].Position; - ulong inputSize = context.Request.SendBuff[0].Size; - - byte[] applicationArea = new byte[inputSize]; - - context.Memory.Read(inputPosition, applicationArea); - - bool isCreated = false; - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - isCreated = VirtualAmiibo.CreateApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId, applicationArea); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - if (!isCreated) - { - resultCode = ResultCode.ApplicationAreaIsNull; - } - - return resultCode; - } - - [CommandHipc(13)] - // GetTagInfo(bytes<8, 4>) -> buffer, 0x1a> - public ResultCode GetTagInfo(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - 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(typeof(TagInfo))); - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo))); - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted || context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) - { - byte[] Uuid = VirtualAmiibo.GenerateUuid(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.NfpDevices[i].UseRandomUuid); - - if (Uuid.Length > AmiiboConstants.UuidMaxLength) - { - throw new ArgumentOutOfRangeException(); - } - - TagInfo tagInfo = new TagInfo - { - UuidLength = (byte)Uuid.Length, - Reserved1 = new Array21(), - Protocol = uint.MaxValue, // All Protocol - TagType = uint.MaxValue, // All Type - Reserved2 = new Array6() - }; - - Uuid.CopyTo(tagInfo.Uuid.ToSpan()); - - context.Memory.Write(outputPosition, tagInfo); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(14)] - // GetRegisterInfo(bytes<8, 4>) -> buffer, 0x1a> - public ResultCode GetRegisterInfo(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - 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(typeof(RegisterInfo))); - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo))); - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(context.Device.System.NfpDevices[i].AmiiboId); - - context.Memory.Write(outputPosition, registerInfo); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(15)] - // GetCommonInfo(bytes<8, 4>) -> buffer, 0x1a> - public ResultCode GetCommonInfo(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - 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(typeof(CommonInfo))); - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo))); - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId); - - context.Memory.Write(outputPosition, commonInfo); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(16)] - // GetModelInfo(bytes<8, 4>) -> buffer, 0x1a> - public ResultCode GetModelInfo(ServiceCtx context) - { - ResultCode resultCode = CheckNfcIsEnabled(); - - if (resultCode != ResultCode.Success) - { - return resultCode; - } - - 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(typeof(ModelInfo))); - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo))); - - uint deviceHandle = (uint)context.RequestData.ReadUInt64(); - - if (context.Device.System.NfpDevices.Count == 0) - { - return ResultCode.DeviceNotFound; - } - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved) - { - resultCode = ResultCode.TagNotFound; - } - else - { - if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted) - { - ModelInfo modelInfo = new ModelInfo - { - Reserved = new Array57() - }; - - modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber)); - modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber); - modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber); - modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber); - modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber); - - context.Memory.Write(outputPosition, modelInfo); - - resultCode = ResultCode.Success; - } - else - { - resultCode = ResultCode.WrongDeviceState; - } - } - - break; - } - } - - return resultCode; - } - - [CommandHipc(17)] - // AttachActivateEvent(bytes<8, 4>) -> handle - public ResultCode AttachActivateEvent(ServiceCtx context) - { - uint deviceHandle = context.RequestData.ReadUInt32(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) - { - context.Device.System.NfpDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext); - - if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle); - - return ResultCode.Success; - } - } - - return ResultCode.DeviceNotFound; - } - - [CommandHipc(18)] - // AttachDeactivateEvent(bytes<8, 4>) -> handle - public ResultCode AttachDeactivateEvent(ServiceCtx context) - { - uint deviceHandle = context.RequestData.ReadUInt32(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) - { - context.Device.System.NfpDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext); - - if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle); - - return ResultCode.Success; - } - } - - return ResultCode.DeviceNotFound; - } - - [CommandHipc(19)] - // GetState() -> u32 - public ResultCode GetState(ServiceCtx context) - { - context.ResponseData.Write((int)_state); - - return ResultCode.Success; - } - - [CommandHipc(20)] - // GetDeviceState(bytes<8, 4>) -> u32 - public ResultCode GetDeviceState(ServiceCtx context) - { - uint deviceHandle = context.RequestData.ReadUInt32(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) - { - if (context.Device.System.NfpDevices[i].State > NfpDeviceState.Finalized) - { - throw new ArgumentOutOfRangeException(); - } - - context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State); - - return ResultCode.Success; - } - } - - context.ResponseData.Write((uint)NfpDeviceState.Unavailable); - - return ResultCode.DeviceNotFound; - } - - [CommandHipc(21)] - // GetNpadId(bytes<8, 4>) -> u32 - public ResultCode GetNpadId(ServiceCtx context) - { - uint deviceHandle = context.RequestData.ReadUInt32(); - - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) - { - if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle) - { - context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfpDevices[i].Handle)); - - return ResultCode.Success; - } - } - - return ResultCode.DeviceNotFound; - } - - [CommandHipc(22)] - // GetApplicationAreaSize() -> u32 - public ResultCode GetApplicationAreaSize(ServiceCtx context) - { - context.ResponseData.Write(AmiiboConstants.ApplicationAreaSize); - - return ResultCode.Success; - } - - [CommandHipc(23)] // 3.0.0+ - // AttachAvailabilityChangeEvent() -> handle - public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context) - { - _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext); - - if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != KernelResult.Success) - { - throw new InvalidOperationException("Out of handles!"); - } - - context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle); - - return ResultCode.Success; - } - - [CommandHipc(24)] // 3.0.0+ - // RecreateApplicationArea(bytes<8, 4>, u32, buffer) - public ResultCode RecreateApplicationArea(ServiceCtx context) - { - throw new ServiceNotImplementedException(this, context, false); - } - - private ResultCode CheckNfcIsEnabled() - { - // TODO: Call nn::settings::detail::GetNfcEnableFlag when it will be implemented. - return true ? ResultCode.Success : ResultCode.NfcDisabled; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/AmiiboConstants.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/AmiiboConstants.cs deleted file mode 100644 index 47f6f0fa..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/AmiiboConstants.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - static class AmiiboConstants - { - public const int UuidMaxLength = 10; - public const int ApplicationAreaSize = 0xD8; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/CommonInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/CommonInfo.cs deleted file mode 100644 index da055dc3..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/CommonInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Ryujinx.Common.Memory; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x40)] - struct CommonInfo - { - public ushort LastWriteYear; - public byte LastWriteMonth; - public byte LastWriteDay; - public ushort WriteCounter; - public ushort Version; - public uint ApplicationAreaSize; - public Array52 Reserved; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/DeviceType.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/DeviceType.cs deleted file mode 100644 index 753b91a9..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/DeviceType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - enum DeviceType : uint - { - Amiibo - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/ModelInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/ModelInfo.cs deleted file mode 100644 index 1b6a3d32..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/ModelInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.Common.Memory; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x40)] - struct ModelInfo - { - public ushort CharacterId; - public byte CharacterVariant; - public byte Series; - public ushort ModelNumber; - public byte Type; - public Array57 Reserved; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/MountTarget.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/MountTarget.cs deleted file mode 100644 index 11520bc6..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/MountTarget.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - enum MountTarget : uint - { - Rom = 1, - Ram = 2, - All = 3 - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDevice.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDevice.cs deleted file mode 100644 index b0d9c806..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDevice.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Hid; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - class NfpDevice - { - public KEvent ActivateEvent; - public KEvent DeactivateEvent; - - public void SignalActivate() => ActivateEvent.ReadableEvent.Signal(); - public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal(); - - public NfpDeviceState State = NfpDeviceState.Unavailable; - - public PlayerIndex Handle; - public NpadIdType NpadIdType; - - public string AmiiboId; - - public bool UseRandomUuid; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDeviceState.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDeviceState.cs deleted file mode 100644 index 0e753250..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/NfpDeviceState.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - enum NfpDeviceState - { - Initialized = 0, - SearchingForTag = 1, - TagFound = 2, - TagRemoved = 3, - TagMounted = 4, - Unavailable = 5, - Finalized = 6 - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/RegisterInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/RegisterInfo.cs deleted file mode 100644 index 3c72a971..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/RegisterInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ryujinx.Common.Memory; -using Ryujinx.HLE.HOS.Services.Mii.Types; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - [StructLayout(LayoutKind.Sequential, Size = 0x100)] - struct RegisterInfo - { - public CharInfo MiiCharInfo; - public ushort FirstWriteYear; - public byte FirstWriteMonth; - public byte FirstWriteDay; - public Array11 Nickname; - public byte FontRegion; - public Array64 Reserved1; - public Array58 Reserved2; - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/State.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/State.cs deleted file mode 100644 index 8d141f0b..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/State.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - enum State - { - NonInitialized = 0, - Initialized = 1 - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/TagInfo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/TagInfo.cs deleted file mode 100644 index 950f8c10..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/TagInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.Common.Memory; -using System.Runtime.InteropServices; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - [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/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/VirtualAmiiboFile.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/VirtualAmiiboFile.cs deleted file mode 100644 index 5265c038..00000000 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/VirtualAmiiboFile.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager -{ - struct VirtualAmiiboFile - { - public uint FileVersion { get; set; } - public byte[] TagUuid { get; set; } - public string AmiiboId { get; set; } - public DateTime FirstWriteDate { get; set; } - public DateTime LastWriteDate { get; set; } - public ushort WriteCounter { get; set; } - public List ApplicationAreas { get; set; } - } - - struct VirtualAmiiboApplicationArea - { - public uint ApplicationAreaId { get; set; } - public byte[] ApplicationArea { get; set; } - } -} diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index bd810d96..2617dfb5 100644 --- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -2,7 +2,7 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii.Types; -using Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using System; using System.Collections.Generic; using System.IO; -- cgit v1.2.3