INfp.cs 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  1. using Ryujinx.Common.Memory;
  2. using Ryujinx.Cpu;
  3. using Ryujinx.HLE.Exceptions;
  4. using Ryujinx.HLE.HOS.Ipc;
  5. using Ryujinx.HLE.HOS.Kernel.Common;
  6. using Ryujinx.HLE.HOS.Kernel.Threading;
  7. using Ryujinx.HLE.HOS.Services.Hid;
  8. using Ryujinx.HLE.HOS.Services.Hid.HidServer;
  9. using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
  10. using System;
  11. using System.Buffers.Binary;
  12. using System.Globalization;
  13. using System.Runtime.InteropServices;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
  17. {
  18. class INfp : IpcService
  19. {
  20. private ulong _appletResourceUserId;
  21. private ulong _mcuVersionData;
  22. private byte[] _mcuData;
  23. private State _state = State.NonInitialized;
  24. private KEvent _availabilityChangeEvent;
  25. private CancellationTokenSource _cancelTokenSource;
  26. private NfpPermissionLevel _permissionLevel;
  27. public INfp(NfpPermissionLevel permissionLevel)
  28. {
  29. _permissionLevel = permissionLevel;
  30. }
  31. [CommandHipc(0)]
  32. // Initialize(u64, u64, pid, buffer<unknown, 5>)
  33. public ResultCode Initialize(ServiceCtx context)
  34. {
  35. _appletResourceUserId = context.RequestData.ReadUInt64();
  36. _mcuVersionData = context.RequestData.ReadUInt64();
  37. ulong inputPosition = context.Request.SendBuff[0].Position;
  38. ulong inputSize = context.Request.SendBuff[0].Size;
  39. _mcuData = new byte[inputSize];
  40. context.Memory.Read(inputPosition, _mcuData);
  41. // TODO: The mcuData buffer seems to contains entries with a size of 0x40 bytes each. Usage of the data needs to be determined.
  42. // TODO: Handle this in a controller class directly.
  43. // Every functions which use the Handle call nn::hid::system::GetXcdHandleForNpadWithNfc().
  44. NfpDevice devicePlayer1 = new NfpDevice
  45. {
  46. NpadIdType = NpadIdType.Player1,
  47. Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
  48. State = NfpDeviceState.Initialized
  49. };
  50. context.Device.System.NfpDevices.Add(devicePlayer1);
  51. // TODO: It mounts 0x8000000000000020 save data and stores a random generate value inside. Usage of the data needs to be determined.
  52. _state = State.Initialized;
  53. return ResultCode.Success;
  54. }
  55. [CommandHipc(1)]
  56. // Finalize()
  57. public ResultCode Finalize(ServiceCtx context)
  58. {
  59. if (_state == State.Initialized)
  60. {
  61. if (_cancelTokenSource != null)
  62. {
  63. _cancelTokenSource.Cancel();
  64. }
  65. // NOTE: All events are destroyed here.
  66. context.Device.System.NfpDevices.Clear();
  67. _state = State.NonInitialized;
  68. }
  69. return ResultCode.Success;
  70. }
  71. [CommandHipc(2)]
  72. // ListDevices() -> (u32, buffer<unknown, 0xa>)
  73. public ResultCode ListDevices(ServiceCtx context)
  74. {
  75. if (context.Request.RecvListBuff.Count == 0)
  76. {
  77. return ResultCode.WrongArgument;
  78. }
  79. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  80. ulong outputSize = context.Request.RecvListBuff[0].Size;
  81. if (context.Device.System.NfpDevices.Count == 0)
  82. {
  83. return ResultCode.DeviceNotFound;
  84. }
  85. MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
  86. if (CheckNfcIsEnabled() == ResultCode.Success)
  87. {
  88. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  89. {
  90. context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfpDevices[i].Handle);
  91. }
  92. context.ResponseData.Write(context.Device.System.NfpDevices.Count);
  93. }
  94. else
  95. {
  96. context.ResponseData.Write(0);
  97. }
  98. return ResultCode.Success;
  99. }
  100. [CommandHipc(3)]
  101. // StartDetection(bytes<8, 4>)
  102. public ResultCode StartDetection(ServiceCtx context)
  103. {
  104. ResultCode resultCode = CheckNfcIsEnabled();
  105. if (resultCode != ResultCode.Success)
  106. {
  107. return resultCode;
  108. }
  109. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  110. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  111. {
  112. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  113. {
  114. context.Device.System.NfpDevices[i].State = NfpDeviceState.SearchingForTag;
  115. break;
  116. }
  117. }
  118. _cancelTokenSource = new CancellationTokenSource();
  119. Task.Run(() =>
  120. {
  121. while (true)
  122. {
  123. if (_cancelTokenSource.Token.IsCancellationRequested)
  124. {
  125. break;
  126. }
  127. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  128. {
  129. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
  130. {
  131. context.Device.System.NfpDevices[i].SignalActivate();
  132. Thread.Sleep(125); // NOTE: Simulate amiibo scanning delay.
  133. context.Device.System.NfpDevices[i].SignalDeactivate();
  134. break;
  135. }
  136. }
  137. }
  138. }, _cancelTokenSource.Token);
  139. return ResultCode.Success;
  140. }
  141. [CommandHipc(4)]
  142. // StopDetection(bytes<8, 4>)
  143. public ResultCode StopDetection(ServiceCtx context)
  144. {
  145. ResultCode resultCode = CheckNfcIsEnabled();
  146. if (resultCode != ResultCode.Success)
  147. {
  148. return resultCode;
  149. }
  150. if (_cancelTokenSource != null)
  151. {
  152. _cancelTokenSource.Cancel();
  153. }
  154. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  155. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  156. {
  157. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  158. {
  159. context.Device.System.NfpDevices[i].State = NfpDeviceState.Initialized;
  160. break;
  161. }
  162. }
  163. return ResultCode.Success;
  164. }
  165. [CommandHipc(5)]
  166. // Mount(bytes<8, 4>, u32, u32)
  167. public ResultCode Mount(ServiceCtx context)
  168. {
  169. ResultCode resultCode = CheckNfcIsEnabled();
  170. if (resultCode != ResultCode.Success)
  171. {
  172. return resultCode;
  173. }
  174. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  175. DeviceType deviceType = (DeviceType)context.RequestData.ReadUInt32();
  176. MountTarget mountTarget = (MountTarget)context.RequestData.ReadUInt32();
  177. if (deviceType != 0)
  178. {
  179. return ResultCode.WrongArgument;
  180. }
  181. if (((uint)mountTarget & 3) == 0)
  182. {
  183. return ResultCode.WrongArgument;
  184. }
  185. // TODO: Found how the MountTarget is handled.
  186. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  187. {
  188. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  189. {
  190. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  191. {
  192. resultCode = ResultCode.TagNotFound;
  193. }
  194. else
  195. {
  196. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
  197. {
  198. // NOTE: This mount the amiibo data, which isn't needed in our case.
  199. context.Device.System.NfpDevices[i].State = NfpDeviceState.TagMounted;
  200. resultCode = ResultCode.Success;
  201. }
  202. else
  203. {
  204. resultCode = ResultCode.WrongDeviceState;
  205. }
  206. }
  207. break;
  208. }
  209. }
  210. return resultCode;
  211. }
  212. [CommandHipc(6)]
  213. // Unmount(bytes<8, 4>)
  214. public ResultCode Unmount(ServiceCtx context)
  215. {
  216. ResultCode resultCode = CheckNfcIsEnabled();
  217. if (resultCode != ResultCode.Success)
  218. {
  219. return resultCode;
  220. }
  221. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  222. if (context.Device.System.NfpDevices.Count == 0)
  223. {
  224. return ResultCode.DeviceNotFound;
  225. }
  226. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  227. {
  228. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  229. {
  230. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  231. {
  232. resultCode = ResultCode.TagNotFound;
  233. }
  234. else
  235. {
  236. // NOTE: This mount the amiibo data, which isn't needed in our case.
  237. context.Device.System.NfpDevices[i].State = NfpDeviceState.TagFound;
  238. resultCode = ResultCode.Success;
  239. }
  240. break;
  241. }
  242. }
  243. return resultCode;
  244. }
  245. [CommandHipc(7)]
  246. // OpenApplicationArea(bytes<8, 4>, u32)
  247. public ResultCode OpenApplicationArea(ServiceCtx context)
  248. {
  249. ResultCode resultCode = CheckNfcIsEnabled();
  250. if (resultCode != ResultCode.Success)
  251. {
  252. return resultCode;
  253. }
  254. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  255. if (context.Device.System.NfpDevices.Count == 0)
  256. {
  257. return ResultCode.DeviceNotFound;
  258. }
  259. uint applicationAreaId = context.RequestData.ReadUInt32();
  260. bool isOpened = false;
  261. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  262. {
  263. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  264. {
  265. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  266. {
  267. resultCode = ResultCode.TagNotFound;
  268. }
  269. else
  270. {
  271. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  272. {
  273. isOpened = VirtualAmiibo.OpenApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId);
  274. resultCode = ResultCode.Success;
  275. }
  276. else
  277. {
  278. resultCode = ResultCode.WrongDeviceState;
  279. }
  280. }
  281. break;
  282. }
  283. }
  284. if (!isOpened)
  285. {
  286. resultCode = ResultCode.ApplicationAreaIsNull;
  287. }
  288. return resultCode;
  289. }
  290. [CommandHipc(8)]
  291. // GetApplicationArea(bytes<8, 4>) -> (u32, buffer<unknown, 6>)
  292. public ResultCode GetApplicationArea(ServiceCtx context)
  293. {
  294. ResultCode resultCode = CheckNfcIsEnabled();
  295. if (resultCode != ResultCode.Success)
  296. {
  297. return resultCode;
  298. }
  299. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  300. if (context.Device.System.NfpDevices.Count == 0)
  301. {
  302. return ResultCode.DeviceNotFound;
  303. }
  304. ulong outputPosition = context.Request.ReceiveBuff[0].Position;
  305. ulong outputSize = context.Request.ReceiveBuff[0].Size;
  306. MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
  307. uint size = 0;
  308. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  309. {
  310. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  311. {
  312. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  313. {
  314. resultCode = ResultCode.TagNotFound;
  315. }
  316. else
  317. {
  318. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  319. {
  320. byte[] applicationArea = VirtualAmiibo.GetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId);
  321. context.Memory.Write(outputPosition, applicationArea);
  322. size = (uint)applicationArea.Length;
  323. resultCode = ResultCode.Success;
  324. }
  325. else
  326. {
  327. resultCode = ResultCode.WrongDeviceState;
  328. }
  329. }
  330. }
  331. }
  332. if (resultCode != ResultCode.Success)
  333. {
  334. return resultCode;
  335. }
  336. if (size == 0)
  337. {
  338. return ResultCode.ApplicationAreaIsNull;
  339. }
  340. context.ResponseData.Write(size);
  341. return ResultCode.Success;
  342. }
  343. [CommandHipc(9)]
  344. // SetApplicationArea(bytes<8, 4>, buffer<unknown, 5>)
  345. public ResultCode SetApplicationArea(ServiceCtx context)
  346. {
  347. ResultCode resultCode = CheckNfcIsEnabled();
  348. if (resultCode != ResultCode.Success)
  349. {
  350. return resultCode;
  351. }
  352. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  353. if (context.Device.System.NfpDevices.Count == 0)
  354. {
  355. return ResultCode.DeviceNotFound;
  356. }
  357. ulong inputPosition = context.Request.SendBuff[0].Position;
  358. ulong inputSize = context.Request.SendBuff[0].Size;
  359. byte[] applicationArea = new byte[inputSize];
  360. context.Memory.Read(inputPosition, applicationArea);
  361. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  362. {
  363. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  364. {
  365. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  366. {
  367. resultCode = ResultCode.TagNotFound;
  368. }
  369. else
  370. {
  371. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  372. {
  373. VirtualAmiibo.SetApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationArea);
  374. resultCode = ResultCode.Success;
  375. }
  376. else
  377. {
  378. resultCode = ResultCode.WrongDeviceState;
  379. }
  380. }
  381. break;
  382. }
  383. }
  384. return resultCode;
  385. }
  386. [CommandHipc(10)]
  387. // Flush(bytes<8, 4>)
  388. public ResultCode Flush(ServiceCtx context)
  389. {
  390. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  391. if (context.Device.System.NfpDevices.Count == 0)
  392. {
  393. return ResultCode.DeviceNotFound;
  394. }
  395. // NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case.
  396. return ResultCode.Success;
  397. }
  398. [CommandHipc(11)]
  399. // Restore(bytes<8, 4>)
  400. public ResultCode Restore(ServiceCtx context)
  401. {
  402. throw new ServiceNotImplementedException(this, context);
  403. }
  404. [CommandHipc(12)]
  405. // CreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
  406. public ResultCode CreateApplicationArea(ServiceCtx context)
  407. {
  408. ResultCode resultCode = CheckNfcIsEnabled();
  409. if (resultCode != ResultCode.Success)
  410. {
  411. return resultCode;
  412. }
  413. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  414. if (context.Device.System.NfpDevices.Count == 0)
  415. {
  416. return ResultCode.DeviceNotFound;
  417. }
  418. uint applicationAreaId = context.RequestData.ReadUInt32();
  419. ulong inputPosition = context.Request.SendBuff[0].Position;
  420. ulong inputSize = context.Request.SendBuff[0].Size;
  421. byte[] applicationArea = new byte[inputSize];
  422. context.Memory.Read(inputPosition, applicationArea);
  423. bool isCreated = false;
  424. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  425. {
  426. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  427. {
  428. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  429. {
  430. resultCode = ResultCode.TagNotFound;
  431. }
  432. else
  433. {
  434. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  435. {
  436. isCreated = VirtualAmiibo.CreateApplicationArea(context.Device.System.NfpDevices[i].AmiiboId, applicationAreaId, applicationArea);
  437. resultCode = ResultCode.Success;
  438. }
  439. else
  440. {
  441. resultCode = ResultCode.WrongDeviceState;
  442. }
  443. }
  444. break;
  445. }
  446. }
  447. if (!isCreated)
  448. {
  449. resultCode = ResultCode.ApplicationAreaIsNull;
  450. }
  451. return resultCode;
  452. }
  453. [CommandHipc(13)]
  454. // GetTagInfo(bytes<8, 4>) -> buffer<unknown<0x58>, 0x1a>
  455. public ResultCode GetTagInfo(ServiceCtx context)
  456. {
  457. ResultCode resultCode = CheckNfcIsEnabled();
  458. if (resultCode != ResultCode.Success)
  459. {
  460. return resultCode;
  461. }
  462. if (context.Request.RecvListBuff.Count == 0)
  463. {
  464. return ResultCode.WrongArgument;
  465. }
  466. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  467. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(TagInfo)));
  468. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo)));
  469. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  470. if (context.Device.System.NfpDevices.Count == 0)
  471. {
  472. return ResultCode.DeviceNotFound;
  473. }
  474. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  475. {
  476. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  477. {
  478. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  479. {
  480. resultCode = ResultCode.TagNotFound;
  481. }
  482. else
  483. {
  484. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted || context.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound)
  485. {
  486. byte[] Uuid = VirtualAmiibo.GenerateUuid(context.Device.System.NfpDevices[i].AmiiboId, context.Device.System.NfpDevices[i].UseRandomUuid);
  487. if (Uuid.Length > AmiiboConstants.UuidMaxLength)
  488. {
  489. throw new ArgumentOutOfRangeException();
  490. }
  491. TagInfo tagInfo = new TagInfo
  492. {
  493. UuidLength = (byte)Uuid.Length,
  494. Reserved1 = new Array21<byte>(),
  495. Protocol = uint.MaxValue, // All Protocol
  496. TagType = uint.MaxValue, // All Type
  497. Reserved2 = new Array6<byte>()
  498. };
  499. Uuid.CopyTo(tagInfo.Uuid.AsSpan());
  500. context.Memory.Write(outputPosition, tagInfo);
  501. resultCode = ResultCode.Success;
  502. }
  503. else
  504. {
  505. resultCode = ResultCode.WrongDeviceState;
  506. }
  507. }
  508. break;
  509. }
  510. }
  511. return resultCode;
  512. }
  513. [CommandHipc(14)]
  514. // GetRegisterInfo(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a>
  515. public ResultCode GetRegisterInfo(ServiceCtx context)
  516. {
  517. ResultCode resultCode = CheckNfcIsEnabled();
  518. if (resultCode != ResultCode.Success)
  519. {
  520. return resultCode;
  521. }
  522. if (context.Request.RecvListBuff.Count == 0)
  523. {
  524. return ResultCode.WrongArgument;
  525. }
  526. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  527. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(RegisterInfo)));
  528. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo)));
  529. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  530. if (context.Device.System.NfpDevices.Count == 0)
  531. {
  532. return ResultCode.DeviceNotFound;
  533. }
  534. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  535. {
  536. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  537. {
  538. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  539. {
  540. resultCode = ResultCode.TagNotFound;
  541. }
  542. else
  543. {
  544. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  545. {
  546. RegisterInfo registerInfo = VirtualAmiibo.GetRegisterInfo(
  547. context.Device.System.TickSource,
  548. context.Device.System.NfpDevices[i].AmiiboId,
  549. context.Device.System.AccountManager.LastOpenedUser.Name);
  550. context.Memory.Write(outputPosition, registerInfo);
  551. resultCode = ResultCode.Success;
  552. }
  553. else
  554. {
  555. resultCode = ResultCode.WrongDeviceState;
  556. }
  557. }
  558. break;
  559. }
  560. }
  561. return resultCode;
  562. }
  563. [CommandHipc(15)]
  564. // GetCommonInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
  565. public ResultCode GetCommonInfo(ServiceCtx context)
  566. {
  567. ResultCode resultCode = CheckNfcIsEnabled();
  568. if (resultCode != ResultCode.Success)
  569. {
  570. return resultCode;
  571. }
  572. if (context.Request.RecvListBuff.Count == 0)
  573. {
  574. return ResultCode.WrongArgument;
  575. }
  576. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  577. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(CommonInfo)));
  578. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo)));
  579. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  580. if (context.Device.System.NfpDevices.Count == 0)
  581. {
  582. return ResultCode.DeviceNotFound;
  583. }
  584. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  585. {
  586. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  587. {
  588. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  589. {
  590. resultCode = ResultCode.TagNotFound;
  591. }
  592. else
  593. {
  594. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  595. {
  596. CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId);
  597. context.Memory.Write(outputPosition, commonInfo);
  598. resultCode = ResultCode.Success;
  599. }
  600. else
  601. {
  602. resultCode = ResultCode.WrongDeviceState;
  603. }
  604. }
  605. break;
  606. }
  607. }
  608. return resultCode;
  609. }
  610. [CommandHipc(16)]
  611. // GetModelInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
  612. public ResultCode GetModelInfo(ServiceCtx context)
  613. {
  614. ResultCode resultCode = CheckNfcIsEnabled();
  615. if (resultCode != ResultCode.Success)
  616. {
  617. return resultCode;
  618. }
  619. if (context.Request.RecvListBuff.Count == 0)
  620. {
  621. return ResultCode.WrongArgument;
  622. }
  623. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  624. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(ModelInfo)));
  625. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo)));
  626. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  627. if (context.Device.System.NfpDevices.Count == 0)
  628. {
  629. return ResultCode.DeviceNotFound;
  630. }
  631. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  632. {
  633. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  634. {
  635. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  636. {
  637. resultCode = ResultCode.TagNotFound;
  638. }
  639. else
  640. {
  641. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  642. {
  643. ModelInfo modelInfo = new ModelInfo
  644. {
  645. Reserved = new Array57<byte>()
  646. };
  647. modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
  648. modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
  649. modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
  650. modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
  651. modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
  652. context.Memory.Write(outputPosition, modelInfo);
  653. resultCode = ResultCode.Success;
  654. }
  655. else
  656. {
  657. resultCode = ResultCode.WrongDeviceState;
  658. }
  659. }
  660. break;
  661. }
  662. }
  663. return resultCode;
  664. }
  665. [CommandHipc(17)]
  666. // AttachActivateEvent(bytes<8, 4>) -> handle<copy>
  667. public ResultCode AttachActivateEvent(ServiceCtx context)
  668. {
  669. uint deviceHandle = context.RequestData.ReadUInt32();
  670. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  671. {
  672. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  673. {
  674. context.Device.System.NfpDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext);
  675. if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != KernelResult.Success)
  676. {
  677. throw new InvalidOperationException("Out of handles!");
  678. }
  679. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle);
  680. return ResultCode.Success;
  681. }
  682. }
  683. return ResultCode.DeviceNotFound;
  684. }
  685. [CommandHipc(18)]
  686. // AttachDeactivateEvent(bytes<8, 4>) -> handle<copy>
  687. public ResultCode AttachDeactivateEvent(ServiceCtx context)
  688. {
  689. uint deviceHandle = context.RequestData.ReadUInt32();
  690. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  691. {
  692. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  693. {
  694. context.Device.System.NfpDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext);
  695. if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != KernelResult.Success)
  696. {
  697. throw new InvalidOperationException("Out of handles!");
  698. }
  699. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle);
  700. return ResultCode.Success;
  701. }
  702. }
  703. return ResultCode.DeviceNotFound;
  704. }
  705. [CommandHipc(19)]
  706. // GetState() -> u32
  707. public ResultCode GetState(ServiceCtx context)
  708. {
  709. context.ResponseData.Write((int)_state);
  710. return ResultCode.Success;
  711. }
  712. [CommandHipc(20)]
  713. // GetDeviceState(bytes<8, 4>) -> u32
  714. public ResultCode GetDeviceState(ServiceCtx context)
  715. {
  716. uint deviceHandle = context.RequestData.ReadUInt32();
  717. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  718. {
  719. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  720. {
  721. if (context.Device.System.NfpDevices[i].State > NfpDeviceState.Finalized)
  722. {
  723. throw new ArgumentOutOfRangeException();
  724. }
  725. context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State);
  726. return ResultCode.Success;
  727. }
  728. }
  729. context.ResponseData.Write((uint)NfpDeviceState.Unavailable);
  730. return ResultCode.DeviceNotFound;
  731. }
  732. [CommandHipc(21)]
  733. // GetNpadId(bytes<8, 4>) -> u32
  734. public ResultCode GetNpadId(ServiceCtx context)
  735. {
  736. uint deviceHandle = context.RequestData.ReadUInt32();
  737. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  738. {
  739. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  740. {
  741. context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfpDevices[i].Handle));
  742. return ResultCode.Success;
  743. }
  744. }
  745. return ResultCode.DeviceNotFound;
  746. }
  747. [CommandHipc(22)]
  748. // GetApplicationAreaSize() -> u32
  749. public ResultCode GetApplicationAreaSize(ServiceCtx context)
  750. {
  751. context.ResponseData.Write(AmiiboConstants.ApplicationAreaSize);
  752. return ResultCode.Success;
  753. }
  754. [CommandHipc(23)] // 3.0.0+
  755. // AttachAvailabilityChangeEvent() -> handle<copy>
  756. public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context)
  757. {
  758. _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext);
  759. if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != KernelResult.Success)
  760. {
  761. throw new InvalidOperationException("Out of handles!");
  762. }
  763. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle);
  764. return ResultCode.Success;
  765. }
  766. [CommandHipc(24)] // 3.0.0+
  767. // RecreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
  768. public ResultCode RecreateApplicationArea(ServiceCtx context)
  769. {
  770. throw new ServiceNotImplementedException(this, context);
  771. }
  772. [CommandHipc(102)]
  773. // GetRegisterInfo2(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a>
  774. public ResultCode GetRegisterInfo2(ServiceCtx context)
  775. {
  776. // TODO: Find the differencies between IUser and ISystem/IDebug.
  777. if (_permissionLevel == NfpPermissionLevel.Debug || _permissionLevel == NfpPermissionLevel.System)
  778. {
  779. return GetRegisterInfo(context);
  780. }
  781. return ResultCode.DeviceNotFound;
  782. }
  783. private ResultCode CheckNfcIsEnabled()
  784. {
  785. // TODO: Call nn::settings::detail::GetNfcEnableFlag when it will be implemented.
  786. return true ? ResultCode.Success : ResultCode.NfcDisabled;
  787. }
  788. }
  789. }