INfp.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997
  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.ToSpan());
  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(context.Device.System.NfpDevices[i].AmiiboId);
  547. context.Memory.Write(outputPosition, registerInfo);
  548. resultCode = ResultCode.Success;
  549. }
  550. else
  551. {
  552. resultCode = ResultCode.WrongDeviceState;
  553. }
  554. }
  555. break;
  556. }
  557. }
  558. return resultCode;
  559. }
  560. [CommandHipc(15)]
  561. // GetCommonInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
  562. public ResultCode GetCommonInfo(ServiceCtx context)
  563. {
  564. ResultCode resultCode = CheckNfcIsEnabled();
  565. if (resultCode != ResultCode.Success)
  566. {
  567. return resultCode;
  568. }
  569. if (context.Request.RecvListBuff.Count == 0)
  570. {
  571. return ResultCode.WrongArgument;
  572. }
  573. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  574. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(CommonInfo)));
  575. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo)));
  576. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  577. if (context.Device.System.NfpDevices.Count == 0)
  578. {
  579. return ResultCode.DeviceNotFound;
  580. }
  581. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  582. {
  583. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  584. {
  585. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  586. {
  587. resultCode = ResultCode.TagNotFound;
  588. }
  589. else
  590. {
  591. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  592. {
  593. CommonInfo commonInfo = VirtualAmiibo.GetCommonInfo(context.Device.System.NfpDevices[i].AmiiboId);
  594. context.Memory.Write(outputPosition, commonInfo);
  595. resultCode = ResultCode.Success;
  596. }
  597. else
  598. {
  599. resultCode = ResultCode.WrongDeviceState;
  600. }
  601. }
  602. break;
  603. }
  604. }
  605. return resultCode;
  606. }
  607. [CommandHipc(16)]
  608. // GetModelInfo(bytes<8, 4>) -> buffer<unknown<0x40>, 0x1a>
  609. public ResultCode GetModelInfo(ServiceCtx context)
  610. {
  611. ResultCode resultCode = CheckNfcIsEnabled();
  612. if (resultCode != ResultCode.Success)
  613. {
  614. return resultCode;
  615. }
  616. if (context.Request.RecvListBuff.Count == 0)
  617. {
  618. return ResultCode.WrongArgument;
  619. }
  620. ulong outputPosition = context.Request.RecvListBuff[0].Position;
  621. context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(ModelInfo)));
  622. MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo)));
  623. uint deviceHandle = (uint)context.RequestData.ReadUInt64();
  624. if (context.Device.System.NfpDevices.Count == 0)
  625. {
  626. return ResultCode.DeviceNotFound;
  627. }
  628. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  629. {
  630. if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
  631. {
  632. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagRemoved)
  633. {
  634. resultCode = ResultCode.TagNotFound;
  635. }
  636. else
  637. {
  638. if (context.Device.System.NfpDevices[i].State == NfpDeviceState.TagMounted)
  639. {
  640. ModelInfo modelInfo = new ModelInfo
  641. {
  642. Reserved = new Array57<byte>()
  643. };
  644. modelInfo.CharacterId = BinaryPrimitives.ReverseEndianness(ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(0, 4), NumberStyles.HexNumber));
  645. modelInfo.CharacterVariant = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(4, 2), NumberStyles.HexNumber);
  646. modelInfo.Series = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(12, 2), NumberStyles.HexNumber);
  647. modelInfo.ModelNumber = ushort.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(8, 4), NumberStyles.HexNumber);
  648. modelInfo.Type = byte.Parse(context.Device.System.NfpDevices[i].AmiiboId.Substring(6, 2), NumberStyles.HexNumber);
  649. context.Memory.Write(outputPosition, modelInfo);
  650. resultCode = ResultCode.Success;
  651. }
  652. else
  653. {
  654. resultCode = ResultCode.WrongDeviceState;
  655. }
  656. }
  657. break;
  658. }
  659. }
  660. return resultCode;
  661. }
  662. [CommandHipc(17)]
  663. // AttachActivateEvent(bytes<8, 4>) -> handle<copy>
  664. public ResultCode AttachActivateEvent(ServiceCtx context)
  665. {
  666. uint deviceHandle = context.RequestData.ReadUInt32();
  667. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  668. {
  669. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  670. {
  671. context.Device.System.NfpDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext);
  672. if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != KernelResult.Success)
  673. {
  674. throw new InvalidOperationException("Out of handles!");
  675. }
  676. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle);
  677. return ResultCode.Success;
  678. }
  679. }
  680. return ResultCode.DeviceNotFound;
  681. }
  682. [CommandHipc(18)]
  683. // AttachDeactivateEvent(bytes<8, 4>) -> handle<copy>
  684. public ResultCode AttachDeactivateEvent(ServiceCtx context)
  685. {
  686. uint deviceHandle = context.RequestData.ReadUInt32();
  687. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  688. {
  689. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  690. {
  691. context.Device.System.NfpDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext);
  692. if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfpDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != KernelResult.Success)
  693. {
  694. throw new InvalidOperationException("Out of handles!");
  695. }
  696. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle);
  697. return ResultCode.Success;
  698. }
  699. }
  700. return ResultCode.DeviceNotFound;
  701. }
  702. [CommandHipc(19)]
  703. // GetState() -> u32
  704. public ResultCode GetState(ServiceCtx context)
  705. {
  706. context.ResponseData.Write((int)_state);
  707. return ResultCode.Success;
  708. }
  709. [CommandHipc(20)]
  710. // GetDeviceState(bytes<8, 4>) -> u32
  711. public ResultCode GetDeviceState(ServiceCtx context)
  712. {
  713. uint deviceHandle = context.RequestData.ReadUInt32();
  714. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  715. {
  716. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  717. {
  718. if (context.Device.System.NfpDevices[i].State > NfpDeviceState.Finalized)
  719. {
  720. throw new ArgumentOutOfRangeException();
  721. }
  722. context.ResponseData.Write((uint)context.Device.System.NfpDevices[i].State);
  723. return ResultCode.Success;
  724. }
  725. }
  726. context.ResponseData.Write((uint)NfpDeviceState.Unavailable);
  727. return ResultCode.DeviceNotFound;
  728. }
  729. [CommandHipc(21)]
  730. // GetNpadId(bytes<8, 4>) -> u32
  731. public ResultCode GetNpadId(ServiceCtx context)
  732. {
  733. uint deviceHandle = context.RequestData.ReadUInt32();
  734. for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
  735. {
  736. if ((uint)context.Device.System.NfpDevices[i].Handle == deviceHandle)
  737. {
  738. context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfpDevices[i].Handle));
  739. return ResultCode.Success;
  740. }
  741. }
  742. return ResultCode.DeviceNotFound;
  743. }
  744. [CommandHipc(22)]
  745. // GetApplicationAreaSize() -> u32
  746. public ResultCode GetApplicationAreaSize(ServiceCtx context)
  747. {
  748. context.ResponseData.Write(AmiiboConstants.ApplicationAreaSize);
  749. return ResultCode.Success;
  750. }
  751. [CommandHipc(23)] // 3.0.0+
  752. // AttachAvailabilityChangeEvent() -> handle<copy>
  753. public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context)
  754. {
  755. _availabilityChangeEvent = new KEvent(context.Device.System.KernelContext);
  756. if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != KernelResult.Success)
  757. {
  758. throw new InvalidOperationException("Out of handles!");
  759. }
  760. context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle);
  761. return ResultCode.Success;
  762. }
  763. [CommandHipc(24)] // 3.0.0+
  764. // RecreateApplicationArea(bytes<8, 4>, u32, buffer<unknown, 5>)
  765. public ResultCode RecreateApplicationArea(ServiceCtx context)
  766. {
  767. throw new ServiceNotImplementedException(this, context);
  768. }
  769. [CommandHipc(102)]
  770. // GetRegisterInfo2(bytes<8, 4>) -> buffer<unknown<0x100>, 0x1a>
  771. public ResultCode GetRegisterInfo2(ServiceCtx context)
  772. {
  773. // TODO: Find the differencies between IUser and ISystem/IDebug.
  774. if (_permissionLevel == NfpPermissionLevel.Debug || _permissionLevel == NfpPermissionLevel.System)
  775. {
  776. return GetRegisterInfo(context);
  777. }
  778. return ResultCode.DeviceNotFound;
  779. }
  780. private ResultCode CheckNfcIsEnabled()
  781. {
  782. // TODO: Call nn::settings::detail::GetNfcEnableFlag when it will be implemented.
  783. return true ? ResultCode.Success : ResultCode.NfcDisabled;
  784. }
  785. }
  786. }