IAccountService.cs 12 KB

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