IAccountService.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.HLE.FileSystem;
  3. using Ryujinx.HLE.HOS.Services.Arp;
  4. using Ryujinx.HLE.HOS.SystemState;
  5. using Ryujinx.HLE.Utilities;
  6. using System;
  7. using System.Collections.Generic;
  8. namespace Ryujinx.HLE.HOS.Services.Acc
  9. {
  10. [Service("acc:u0")]
  11. [Service("acc:u1")]
  12. class IAccountService : IpcService
  13. {
  14. private bool _userRegistrationRequestPermitted = false;
  15. private ApplicationLaunchProperty _applicationLaunchProperty;
  16. public IAccountService(ServiceCtx context) { }
  17. [Command(0)]
  18. // GetUserCount() -> i32
  19. public ResultCode GetUserCount(ServiceCtx context)
  20. {
  21. context.ResponseData.Write(context.Device.System.State.Account.GetUserCount());
  22. return ResultCode.Success;
  23. }
  24. [Command(1)]
  25. // GetUserExistence(nn::account::Uid) -> bool
  26. public ResultCode GetUserExistence(ServiceCtx context)
  27. {
  28. UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
  29. if (userId.IsNull)
  30. {
  31. return ResultCode.NullArgument;
  32. }
  33. context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _));
  34. return ResultCode.Success;
  35. }
  36. [Command(2)]
  37. // ListAllUsers() -> array<nn::account::Uid, 0xa>
  38. public ResultCode ListAllUsers(ServiceCtx context)
  39. {
  40. return WriteUserList(context, context.Device.System.State.Account.GetAllUsers());
  41. }
  42. [Command(3)]
  43. // ListOpenUsers() -> array<nn::account::Uid, 0xa>
  44. public ResultCode ListOpenUsers(ServiceCtx context)
  45. {
  46. return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers());
  47. }
  48. private ResultCode WriteUserList(ServiceCtx context, IEnumerable<UserProfile> profiles)
  49. {
  50. if (context.Request.RecvListBuff.Count == 0)
  51. {
  52. return ResultCode.InvalidInputBuffer;
  53. }
  54. long outputPosition = context.Request.RecvListBuff[0].Position;
  55. long outputSize = context.Request.RecvListBuff[0].Size;
  56. ulong offset = 0;
  57. foreach (UserProfile userProfile in profiles)
  58. {
  59. if (offset + 0x10 > (ulong)outputSize)
  60. {
  61. break;
  62. }
  63. context.Memory.WriteInt64(outputPosition + (long)offset, userProfile.UserId.Low);
  64. context.Memory.WriteInt64(outputPosition + (long)offset + 8, userProfile.UserId.High);
  65. offset += 0x10;
  66. }
  67. return ResultCode.Success;
  68. }
  69. [Command(4)]
  70. // GetLastOpenedUser() -> nn::account::Uid
  71. public ResultCode GetLastOpenedUser(ServiceCtx context)
  72. {
  73. context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData);
  74. return ResultCode.Success;
  75. }
  76. [Command(5)]
  77. // GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile>
  78. public ResultCode GetProfile(ServiceCtx context)
  79. {
  80. UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
  81. if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile))
  82. {
  83. Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{userId} not found!");
  84. return ResultCode.UserNotFound;
  85. }
  86. MakeObject(context, new IProfile(userProfile));
  87. // Doesn't occur in our case.
  88. // return MakeError(ErrorModule.Account, AccErr.NullObject);
  89. return ResultCode.Success;
  90. }
  91. [Command(50)]
  92. // IsUserRegistrationRequestPermitted(u64, pid) -> bool
  93. public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context)
  94. {
  95. // The u64 argument seems to be unused by account.
  96. context.ResponseData.Write(_userRegistrationRequestPermitted);
  97. return ResultCode.Success;
  98. }
  99. [Command(51)]
  100. // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid
  101. public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context)
  102. {
  103. if (context.Device.System.State.Account.GetUserCount() != 1)
  104. {
  105. // Invalid UserId.
  106. new UInt128(0, 0).Write(context.ResponseData);
  107. return 0;
  108. }
  109. bool baasCheck = context.RequestData.ReadBoolean();
  110. if (baasCheck)
  111. {
  112. // This checks something related to baas (online), and then return an invalid UserId if the check in baas returns an error code.
  113. // In our case, we can just log it for now.
  114. Logger.PrintStub(LogClass.ServiceAcc, new { baasCheck });
  115. }
  116. // As we returned an invalid UserId if there is more than one user earlier, now we can return only the first one.
  117. context.Device.System.State.Account.GetFirst().UserId.Write(context.ResponseData);
  118. return ResultCode.Success;
  119. }
  120. [Command(100)]
  121. [Command(140)] // 6.0.0+
  122. // InitializeApplicationInfo(u64, pid)
  123. // Both calls (100, 140) use the same submethod, maybe there's something different further along when arp:r is called?
  124. public ResultCode InitializeApplicationInfo(ServiceCtx context)
  125. {
  126. if (_applicationLaunchProperty != null)
  127. {
  128. return ResultCode.ApplicationLaunchPropertyAlreadyInit;
  129. }
  130. // The u64 argument seems to be unused by account.
  131. long unknown = context.RequestData.ReadInt64();
  132. // TODO: Account actually calls nn::arp::detail::IReader::GetApplicationLaunchProperty() with the current PID and store the result (ApplicationLaunchProperty) internally.
  133. // For now we can hardcode values, and fix it after GetApplicationLaunchProperty is implemented.
  134. /*
  135. if (nn::arp::detail::IReader::GetApplicationLaunchProperty() == 0xCC9D) // InvalidProcessId
  136. {
  137. _applicationLaunchProperty = new ApplicationLaunchProperty
  138. {
  139. TitleId = 0x00;
  140. Version = 0x00;
  141. BaseGameStorageId = 0x03;
  142. UpdateGameStorageId = 0x00;
  143. }
  144. return ResultCode.InvalidArgument;
  145. }
  146. else
  147. */
  148. {
  149. _applicationLaunchProperty = new ApplicationLaunchProperty
  150. {
  151. TitleId = BitConverter.ToInt64(StringUtils.HexToBytes(context.Device.System.TitleID), 0),
  152. Version = 0x00,
  153. BaseGameStorageId = (byte)StorageId.NandSystem,
  154. UpdateGameStorageId = (byte)StorageId.None
  155. };
  156. }
  157. Logger.PrintStub(LogClass.ServiceAcc, new { unknown });
  158. return ResultCode.Success;
  159. }
  160. [Command(101)]
  161. // GetBaasAccountManagerForApplication(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication>
  162. public ResultCode GetBaasAccountManagerForApplication(ServiceCtx context)
  163. {
  164. UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
  165. if (userId.IsNull)
  166. {
  167. return ResultCode.NullArgument;
  168. }
  169. if (_applicationLaunchProperty == null)
  170. {
  171. return ResultCode.InvalidArgument;
  172. }
  173. MakeObject(context, new IManagerForApplication(userId, _applicationLaunchProperty));
  174. // Doesn't occur in our case.
  175. // return ResultCode.NullObject;
  176. return ResultCode.Success;
  177. }
  178. [Command(110)]
  179. // StoreSaveDataThumbnail(nn::account::Uid, buffer<bytes, 5>)
  180. public ResultCode StoreSaveDataThumbnail(ServiceCtx context)
  181. {
  182. if (_applicationLaunchProperty == null)
  183. {
  184. return ResultCode.InvalidArgument;
  185. }
  186. UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
  187. if (userId.IsNull)
  188. {
  189. return ResultCode.NullArgument;
  190. }
  191. if (context.Request.SendBuff.Count == 0)
  192. {
  193. return ResultCode.InvalidInputBuffer;
  194. }
  195. long inputPosition = context.Request.SendBuff[0].Position;
  196. long inputSize = context.Request.SendBuff[0].Size;
  197. if (inputSize != 0x24000)
  198. {
  199. return ResultCode.InvalidInputBufferSize;
  200. }
  201. byte[] thumbnailBuffer = context.Memory.ReadBytes(inputPosition, inputSize);
  202. // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ?
  203. Logger.PrintStub(LogClass.ServiceAcc);
  204. return ResultCode.Success;
  205. }
  206. [Command(111)]
  207. // ClearSaveDataThumbnail(nn::account::Uid)
  208. public ResultCode ClearSaveDataThumbnail(ServiceCtx context)
  209. {
  210. if (_applicationLaunchProperty == null)
  211. {
  212. return ResultCode.InvalidArgument;
  213. }
  214. UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
  215. if (userId.IsNull)
  216. {
  217. return ResultCode.NullArgument;
  218. }
  219. // TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ?
  220. Logger.PrintStub(LogClass.ServiceAcc);
  221. return ResultCode.Success;
  222. }
  223. [Command(150)] // 6.0.0+
  224. // IsUserAccountSwitchLocked() -> bool
  225. public ResultCode IsUserAccountSwitchLocked(ServiceCtx context)
  226. {
  227. // TODO : Validate the following check.
  228. /*
  229. if (_applicationLaunchProperty != null)
  230. {
  231. return ResultCode.ApplicationLaunchPropertyAlreadyInit;
  232. }
  233. */
  234. // Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current PID and store the result (NACP File) internally.
  235. // But since we use LibHac and we load one Application at a time, it's not necessary.
  236. context.ResponseData.Write(context.Device.System.ControlData.UserAccountSwitchLock);
  237. Logger.PrintStub(LogClass.ServiceAcc);
  238. return ResultCode.Success;
  239. }
  240. }
  241. }