IAccountServiceForApplication.cs 9.8 KB

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